API / Bibliothèque
@colis/core : createBucket, l’API fichiers, les envois en morceaux, les enregistrements JSON, les codes et le gestionnaire de routes.
@colis/core est le seul paquet qui détient les identifiants du stockage. La page de livraison est
construite dessus ; vous pouvez l’utiliser pour construire votre propre flux. Il tourne côté serveur
uniquement.
createBucket(config?)
Renvoie un Bucket. L’appel est léger : le S3Client sous-jacent n’est construit qu’à la première
requête, donc on peut l’appeler au niveau du module. Chaque option est dans
Configuration.
import { createBucket } from '@colis/core'
const store = createBucket({ bucket: 'livraisons', prefix: 'drop' })new Bucket(config) est équivalent, et exporté pour typer ou étendre.
L’API fichiers
upload(body, options?)
Envoie un corps en un seul PutObject. Écrire sur une clé existante la remplace.
body accepte string, Buffer, Uint8Array, ArrayBuffer, tableau typé, Blob, File,
Readable de Node ou ReadableStream web.
| Option | Défaut |
|---|---|
key | générée : <uuid>-<filename> |
prefix | le préfixe du bucket |
filename | le nom du File, s’il y en a un |
contentType | le type du File, sinon deviné par l’extension, sinon application/octet-stream |
contentLength | la taille du corps ; obligatoire pour un flux |
cacheControl, contentDisposition, metadata | — |
acl | — (la plupart des buckets refusent les ACL) |
ifMatch | — n’écrire que si l’ETag stocké correspond encore |
ifAbsent | — n’écrire que si rien n’est encore stocké |
signal | — |
Renvoie { bucket, key, path, contentType, size?, etag?, url? }. key est la poignée à repasser aux
autres méthodes, path la clé réelle dans le bucket, préfixe compris ; url n’existe que si
publicUrl est configuré.
put(id, body, options?)
upload(body, { key: id }), avec l’identifiant en premier. Mêmes options sauf key, même résultat.
get(key, options?)
Relit un fichier, ou null si la clé n’existe pas. Le résultat porte contentType, filename,
size, etag, lastModified, metadata, et le corps : body (un Readable), bytes() ou
text(), à lire une seule fois.
head(key, options?)
Ce que S3 sait d’un objet sans le télécharger, ou null. Les mêmes champs que get(), sans le corps.
getUrl(key, options?)
L’adresse publique si publicUrl est configuré, une URL GET présignée sinon. La signature est un
calcul local : ni aller-retour réseau, ni permission supplémentaire.
| Option | Défaut |
|---|---|
expiresIn | 3600 secondes, au plus 604800 |
signed | true sauf si publicUrl est configuré |
download | — Content-Disposition: attachment, avec un nom de fichier si vous passez une chaîne |
delete(key)
Supprime une clé ou un tableau de clés (par lots de 1 000). Supprimer une clé absente n’est pas une
erreur ; un échec par clé signalé par S3 lève DELETE_FAILED en les nommant.
Les envois en morceaux
Pour envoyer un gros fichier du navigateur directement au bucket, comme le fait la page de livraison (voir Gros fichiers) :
const { uploadId } = await store.createMultipartUpload(key, { filename, contentType })
// Pour chaque morceau : une URL PUT signée avec sa longueur exacte.
const url = await store.presignUploadPart(key, uploadId, partNumber, { contentLength, expiresIn: 3600 })
// Pour reprendre : les morceaux que le bucket a déjà, ou null si l’envoi n’existe plus.
const parts = await store.listUploadedParts(key, uploadId)
// Le client renvoie l’ETag de chaque morceau.
await store.completeMultipartUpload(key, uploadId, [{ partNumber: 1, etag }], { ifAbsent: true })
// Ou l’abandonner, ce qui libère les morceaux déjà stockés.
await store.abortMultipartUpload(key, uploadId)maxSize n’est pas vérifié ici : la taille n’est connue qu’une fois les morceaux arrivés. Relisez-la
avec head() après l’assemblage. ifAbsent envoie If-None-Match: * avec l’assemblage ; AWS S3 le
respecte, vérifiez que votre fournisseur aussi. Le bucket a besoin d’une règle CORS qui autorise PUT
et expose ETag.
transferFileObject(code, { filename, contentType?, expiresAt? }) donne la clé, le type et les
métadonnées qui font d’un objet un colis que les routes du protocole reconnaissent : à passer à
createMultipartUpload() pour qu’un fichier arrivé en morceaux ne se distingue pas d’un autre.
Les enregistrements JSON
putSnapshot() et getSnapshot() stockent de petits objets JSON, compressés en gzip, avec une
expiration vérifiée à la lecture. La page de livraison s’en sert pour l’accusé de réception et les
options d’un colis ; la CLI s’en sert pour la sonde de colis verifier --remote.
await store.putSnapshot(
`meta/${code}.verdict`,
{ decision: 'approved', at: new Date().toISOString() },
{
expiresIn: 7 * 86_400,
ifAbsent: true, // la première écriture gagne, les suivantes lèvent PRECONDITION_FAILED
},
)
const record = await store.getSnapshot<{ decision: string }>(`meta/${code}.verdict`)
record?.data.decision // null si absent ou expiréOption de putSnapshot | Effet |
|---|---|
app, device | Des étiquettes libres, rendues à la lecture. |
version | Un numéro de schéma ; getSnapshot(…, { maxVersion }) refuse plus récent avec SNAPSHOT_TOO_NEW. |
expiresIn | Secondes. Passé ce délai, getSnapshot() renvoie null. |
compress | true par défaut. |
ifMatch, ifAbsent | Écritures conditionnelles, comme pour upload(). |
prefix, signal | Comme partout. |
getSnapshot() renvoie { data, app, device, version, createdAt, expiresAt, etag, size, bucket, key, path },
ou null pour une clé absente comme pour une clé expirée.
Les codes
store.codes crée et relit les codes, dans la forme configurée par syncCode :
store.codes.create() // "K7QP2M4X"
store.codes.normalize('k7qp-2m4x') // "K7QP2M4X"| Membre | Rôle |
|---|---|
create() | Un nouveau code. |
normalize(input) | Ce qu’on a tapé, remis en forme. Lève INVALID_SYNC_CODE si un caractère ne peut pas y figurer. |
alphabet, length | La forme configurée. |
entropyBits | Ce que vaut un code face à quelqu’un qui devine : 40 par défaut. |
createSyncCodes(options?), createSyncCode(), normalizeSyncCode() et syncCodeAlphabets font la
même chose hors d’un bucket. Voir Configuration.
Le gestionnaire de routes
createTransferHandler({ bucket, … }) sert les quatre routes du protocole sur n’importe quoi qui
parle Request et Response. Ses options (basePath, expiresIn, authorize, raw, app,
maxVersion) sont décrites dans Protocole.
Le reste
store.client: leS3Clientsous-jacent, pour tout ce que colis ne couvre pas (lister, administrer).store.destroy(): libère les connexions du client créé par colis. Un client passé parconfig.clientreste à vous.ColisErroretisColisError(error): voir Erreurs.