@mieweb/pulsevault 0.0.2 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +258 -0
  2. package/LICENSE +15 -39
  3. package/OPERATIONS.md +223 -0
  4. package/PROTOCOL.md +345 -0
  5. package/README.md +548 -122
  6. package/augment.d.ts +1 -0
  7. package/augment.js +3 -0
  8. package/core.d.ts +1 -0
  9. package/core.js +7 -0
  10. package/dist/app.d.ts +91 -34
  11. package/dist/app.d.ts.map +1 -1
  12. package/dist/app.js +23 -33
  13. package/dist/app.js.map +1 -1
  14. package/dist/augment.d.ts +14 -7
  15. package/dist/augment.d.ts.map +1 -1
  16. package/dist/augment.js.map +1 -1
  17. package/dist/core.d.ts +108 -0
  18. package/dist/core.d.ts.map +1 -0
  19. package/dist/core.js +494 -0
  20. package/dist/core.js.map +1 -0
  21. package/dist/lib/authorize.d.ts +19 -0
  22. package/dist/lib/authorize.d.ts.map +1 -0
  23. package/dist/lib/authorize.js +2 -0
  24. package/dist/lib/authorize.js.map +1 -0
  25. package/dist/lib/capability-token.d.ts +80 -0
  26. package/dist/lib/capability-token.d.ts.map +1 -0
  27. package/dist/lib/capability-token.js +134 -0
  28. package/dist/lib/capability-token.js.map +1 -0
  29. package/dist/lib/checksum.d.ts +45 -0
  30. package/dist/lib/checksum.d.ts.map +1 -0
  31. package/dist/lib/checksum.js +105 -0
  32. package/dist/lib/checksum.js.map +1 -0
  33. package/dist/lib/deeplinks.d.ts +37 -21
  34. package/dist/lib/deeplinks.d.ts.map +1 -1
  35. package/dist/lib/deeplinks.js +54 -13
  36. package/dist/lib/deeplinks.js.map +1 -1
  37. package/dist/lib/errors.d.ts +7 -0
  38. package/dist/lib/errors.d.ts.map +1 -1
  39. package/dist/lib/errors.js +18 -0
  40. package/dist/lib/errors.js.map +1 -1
  41. package/dist/lib/magic.d.ts +30 -5
  42. package/dist/lib/magic.d.ts.map +1 -1
  43. package/dist/lib/magic.js +51 -14
  44. package/dist/lib/magic.js.map +1 -1
  45. package/dist/lib/options.d.ts +41 -0
  46. package/dist/lib/options.d.ts.map +1 -0
  47. package/dist/lib/options.js +111 -0
  48. package/dist/lib/options.js.map +1 -0
  49. package/dist/lib/pulsevaultTus.d.ts +53 -26
  50. package/dist/lib/pulsevaultTus.d.ts.map +1 -1
  51. package/dist/lib/pulsevaultTus.js +174 -55
  52. package/dist/lib/pulsevaultTus.js.map +1 -1
  53. package/dist/lib/request.d.ts +26 -0
  54. package/dist/lib/request.d.ts.map +1 -0
  55. package/dist/lib/request.js +22 -0
  56. package/dist/lib/request.js.map +1 -0
  57. package/dist/lib/uuid.d.ts.map +1 -1
  58. package/dist/lib/uuid.js +1 -1
  59. package/dist/lib/uuid.js.map +1 -1
  60. package/dist/routes/pulsevault.d.ts +18 -16
  61. package/dist/routes/pulsevault.d.ts.map +1 -1
  62. package/dist/routes/pulsevault.js +121 -251
  63. package/dist/routes/pulsevault.js.map +1 -1
  64. package/dist/storage/local.d.ts +31 -10
  65. package/dist/storage/local.d.ts.map +1 -1
  66. package/dist/storage/local.js +205 -84
  67. package/dist/storage/local.js.map +1 -1
  68. package/dist/storage/s3.d.ts +109 -0
  69. package/dist/storage/s3.d.ts.map +1 -0
  70. package/dist/storage/s3.js +438 -0
  71. package/dist/storage/s3.js.map +1 -0
  72. package/dist/storage/types.d.ts +80 -17
  73. package/dist/storage/types.d.ts.map +1 -1
  74. package/dist/storage/types.js +13 -1
  75. package/dist/storage/types.js.map +1 -1
  76. package/package.json +29 -7
@@ -1,51 +1,135 @@
1
- import { FileStore } from "@tus/file-store";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- const SIDECAR_NAME = ".pulsevault.json";
1
+ import { FileStore } from '@tus/file-store';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { isUuid } from '../lib/uuid.js';
5
+ import { parseUploadKind } from './types.js';
5
6
  const SIDECAR_VERSION = 1;
7
+ /** Hidden directory inside workspaceRoot that holds per-upload sidecar files. */
8
+ const PULSEVAULT_META_DIR = '.pulsevault';
9
+ /** Default cap on the in-memory metadata cache before evicting the oldest entry. */
10
+ const DEFAULT_META_CACHE_LIMIT = 10_000;
11
+ /** Map a file extension to the `Content-Type` the GET route should return. */
12
+ function extToContentType(ext) {
13
+ switch (ext) {
14
+ case '.mp4':
15
+ return 'video/mp4';
16
+ case '.zip':
17
+ return 'application/zip';
18
+ case '.vtt':
19
+ return 'text/vtt';
20
+ case '.jpg':
21
+ case '.jpeg':
22
+ return 'image/jpeg';
23
+ case '.png':
24
+ return 'image/png';
25
+ default:
26
+ return 'application/octet-stream';
27
+ }
28
+ }
29
+ /** Project a `Sidecar` into the shape kept in the in-memory cache. */
30
+ function sidecarToCachedMeta(sidecar, ready) {
31
+ return {
32
+ ext: sidecar.ext,
33
+ ready,
34
+ kind: sidecar.kind ?? 'video',
35
+ relatedTo: sidecar.relatedTo,
36
+ checksum: sidecar.checksum,
37
+ };
38
+ }
6
39
  export function createLocalStorage(opts) {
7
40
  const workspaceRoot = path.resolve(opts.workspaceDir);
8
41
  const datastore = new FileStore({ directory: workspaceRoot });
9
- // Metadata cache keyed by videoid. Populated eagerly on reserve and lazily
10
- // from the sidecar on cache-miss — so we never do a workspace-wide scan at
11
- // boot and never do a per-request readdir on the GET hot path.
42
+ const metaCacheLimit = opts.metaCacheLimit ?? DEFAULT_META_CACHE_LIMIT;
43
+ // Metadata cache keyed by artifactId. Populated eagerly on reserve and
44
+ // lazily from the sidecar on cache-miss — so we never do a workspace-wide
45
+ // scan at boot and never do a per-request readdir on the GET hot path.
46
+ // Bounded with simple insertion-order eviction (a `Map` preserves insertion
47
+ // order, and re-setting a key moves it to the end) so a long-running server
48
+ // doesn't grow this unboundedly; a cache miss just costs an extra disk read.
12
49
  const metaCache = new Map();
13
- const videoDir = (videoid) => path.join(workspaceRoot, videoid);
14
- const sidecarPath = (videoid) => path.join(videoDir(videoid), SIDECAR_NAME);
15
- const videoRelPath = (videoid, ext) => `video/${videoid}${ext}`;
16
- const writeSidecar = async (videoid, sidecar) => {
50
+ const cacheSet = (artifactId, meta) => {
51
+ // Re-inserting moves the key to the end of iteration order, so eviction
52
+ // below always drops the actual least-recently-set entry.
53
+ metaCache.delete(artifactId);
54
+ metaCache.set(artifactId, meta);
55
+ if (metaCache.size > metaCacheLimit) {
56
+ const oldest = metaCache.keys().next().value;
57
+ if (oldest !== undefined)
58
+ metaCache.delete(oldest);
59
+ }
60
+ };
61
+ /** Absolute path to the hidden metadata directory. */
62
+ const sidecarDir = () => path.join(workspaceRoot, PULSEVAULT_META_DIR);
63
+ /**
64
+ * Absolute path to the sidecar JSON for a given artifactId. `readSidecar`
65
+ * already rejects non-UUID ids before any path is built; the resolve +
66
+ * containment check here is defense in depth for any future caller that
67
+ * bypasses that funnel — a path that escapes the metadata directory is a
68
+ * hard error, never a read.
69
+ */
70
+ const sidecarPath = (artifactId) => {
71
+ // Strictly inside the metadata directory: the sidecar is always a `.json`
72
+ // file under it, so the resolved path can never equal the directory itself.
73
+ const base = path.resolve(sidecarDir());
74
+ const resolved = path.resolve(base, `${artifactId}.json`);
75
+ if (!resolved.startsWith(`${base}${path.sep}`)) {
76
+ throw new Error('artifactId escapes the metadata directory');
77
+ }
78
+ return resolved;
79
+ };
80
+ /** Relative path (from workspaceRoot) to the artifact bytes. */
81
+ const artifactRelPath = (artifactId, kind, ext) => `${kind}/${artifactId}${ext}`;
82
+ const writeSidecar = async (artifactId, sidecar) => {
17
83
  // Atomic tmp + rename so a crash mid-write can never leave a truncated
18
84
  // JSON blob that `loadMeta` would then treat as corrupt.
19
- const finalPath = sidecarPath(videoid);
85
+ const finalPath = sidecarPath(artifactId);
20
86
  const tmpPath = `${finalPath}.tmp`;
21
- await fs.writeFile(tmpPath, JSON.stringify(sidecar), "utf8");
87
+ await fs.mkdir(sidecarDir(), { recursive: true, mode: 0o750 });
88
+ await fs.writeFile(tmpPath, JSON.stringify(sidecar), 'utf8');
22
89
  await fs.rename(tmpPath, finalPath);
23
90
  };
24
- const readSidecar = async (videoid) => {
91
+ const readSidecar = async (artifactId) => {
92
+ // Every sidecar/artifact path in this module is built by joining
93
+ // `artifactId` straight into a filesystem path — reject anything that
94
+ // isn't a real UUID before it reaches `fs`, the same way `resolve()`
95
+ // already refuses to serve non-UUID ids, so a stray `../` (or one
96
+ // smuggled in by a caller that skips the HTTP route layer, e.g. a
97
+ // direct `getLocalPath`/`getKind` call) can never escape the intended
98
+ // `.pulsevault`/`<kind>` subdirectories. This is the single funnel
99
+ // every other method in this file goes through (`loadMeta`), so
100
+ // gating here covers `getLocalPath`, `getKind`, `getRelatedTo`,
101
+ // `getChecksum`, `resolve`, `remove`, and `markReady` in one place.
102
+ if (!isUuid(artifactId))
103
+ return null;
25
104
  let raw;
26
105
  try {
27
- raw = await fs.readFile(sidecarPath(videoid), "utf8");
106
+ raw = await fs.readFile(sidecarPath(artifactId), 'utf8');
28
107
  }
29
108
  catch (err) {
30
- if (err?.code === "ENOENT")
109
+ if (err?.code === 'ENOENT')
31
110
  return null;
32
111
  throw err;
33
112
  }
34
113
  try {
35
114
  const parsed = JSON.parse(raw);
36
- if (typeof parsed.ext !== "string")
115
+ if (typeof parsed.ext !== 'string')
37
116
  return null;
38
- if (typeof parsed.filename !== "string")
117
+ if (typeof parsed.filename !== 'string')
39
118
  return null;
40
119
  // Older sidecars (pre-status) are treated as ready so an in-place
41
120
  // upgrade doesn't hide finalized uploads. New uploads always write a
42
121
  // `status` field explicitly.
43
- const status = parsed.status === "uploading" ? "uploading" : "ready";
122
+ const status = parsed.status === 'uploading' ? 'uploading' : 'ready';
123
+ // Older sidecars without `kind` default to `"video"` — no migration.
124
+ const kind = parseUploadKind(parsed.kind);
44
125
  return {
45
126
  version: SIDECAR_VERSION,
46
127
  ext: parsed.ext,
47
128
  filename: parsed.filename,
48
129
  status,
130
+ kind,
131
+ relatedTo: typeof parsed.relatedTo === 'string' ? parsed.relatedTo : undefined,
132
+ checksum: typeof parsed.checksum === 'string' ? parsed.checksum : undefined,
49
133
  };
50
134
  }
51
135
  catch {
@@ -54,104 +138,138 @@ export function createLocalStorage(opts) {
54
138
  return null;
55
139
  }
56
140
  };
57
- const loadMeta = async (videoid) => {
58
- const cached = metaCache.get(videoid);
141
+ const loadMeta = async (artifactId) => {
142
+ const cached = metaCache.get(artifactId);
59
143
  if (cached)
60
144
  return cached;
61
- const sidecar = await readSidecar(videoid);
145
+ const sidecar = await readSidecar(artifactId);
62
146
  if (!sidecar)
63
147
  return null;
64
- const meta = {
65
- ext: sidecar.ext,
66
- ready: sidecar.status === "ready",
67
- };
68
- metaCache.set(videoid, meta);
148
+ const meta = sidecarToCachedMeta(sidecar, sidecar.status === 'ready');
149
+ cacheSet(artifactId, meta);
69
150
  return meta;
70
151
  };
71
152
  const initialize = async () => {
72
- await fs.mkdir(workspaceRoot, { recursive: true });
73
- };
74
- const reserveUpload = async ({ videoid, filename, ext, }) => {
75
- // Collision guard: if an upload (in-progress or ready) already exists
76
- // for this videoid, refuse the new upload rather than letting
77
- // @tus/file-store silently reset the metadata sidecar to offset 0 and
78
- // overwrite the file chunk-by-chunk on subsequent PATCHes. Translates
79
- // to HTTP 409 via @tus/server's error path.
80
- const meta = await loadMeta(videoid);
81
- if (meta) {
82
- throw Object.assign(new Error(`videoid ${videoid} already has an upload`), { statusCode: 409, status_code: 409 });
83
- }
84
- const dir = videoDir(videoid);
85
- await fs.mkdir(path.join(dir, "video"), { recursive: true });
86
- await writeSidecar(videoid, {
153
+ // Dirs PulseVault creates itself get mode 0o750 directly (mkdir's mode caps
154
+ // the permission bits — umask only removes more — so this holds even under a
155
+ // permissive umask; a world-readable upload tree would otherwise leak media).
156
+ await fs.mkdir(sidecarDir(), { recursive: true, mode: 0o750 });
157
+ // @tus/file-store creates `workspaceRoot` with mode 0777 if it doesn't already exist;
158
+ // tighten it so the upload tree isn't world-writable under a permissive umask.
159
+ await fs.chmod(workspaceRoot, 0o750).catch(() => { });
160
+ };
161
+ const reserveUpload = async ({ artifactId, filename, ext, kind, relatedTo, checksum, }) => {
162
+ await fs.mkdir(path.join(workspaceRoot, kind), { recursive: true, mode: 0o750 });
163
+ await fs.mkdir(sidecarDir(), { recursive: true, mode: 0o750 });
164
+ const sidecar = {
87
165
  version: SIDECAR_VERSION,
88
166
  ext,
89
167
  filename,
90
- status: "uploading",
91
- });
92
- metaCache.set(videoid, { ext, ready: false });
168
+ status: 'uploading',
169
+ kind,
170
+ relatedTo,
171
+ checksum,
172
+ };
173
+ // Collision guard: `wx` fails atomically with EEXIST if a sidecar already exists for
174
+ // this artifactId, rather than the previous read-then-write (`loadMeta` then
175
+ // `writeSidecar`) which left a window for two concurrent/retried requests to both pass
176
+ // the check before either had written — letting the second silently clobber the first's
177
+ // sidecar and race @tus/file-store's own offset tracking. Translates to HTTP 409 via
178
+ // @tus/server's error path, same as before.
179
+ try {
180
+ await fs.writeFile(sidecarPath(artifactId), JSON.stringify(sidecar), { flag: 'wx' });
181
+ }
182
+ catch (err) {
183
+ if (err?.code !== 'EEXIST')
184
+ throw err;
185
+ // A file already exists at this path, but `readSidecar` treats a malformed/corrupt
186
+ // one as absent (e.g. debris from a crash mid-write) — re-check before deciding this
187
+ // is a genuine collision rather than debris that's safe to overwrite.
188
+ const existing = await readSidecar(artifactId);
189
+ if (existing) {
190
+ throw Object.assign(new Error(`artifactId ${artifactId} already has an upload`), {
191
+ statusCode: 409,
192
+ status_code: 409,
193
+ });
194
+ }
195
+ await writeSidecar(artifactId, sidecar);
196
+ }
197
+ cacheSet(artifactId, { ext, ready: false, kind, relatedTo, checksum });
93
198
  // @tus/file-store joins this onto its configured `directory`, so the
94
- // actual file lands at `<workspaceRoot>/<videoid>/video/<videoid><ext>`.
95
- return `${videoid}/${videoRelPath(videoid, ext)}`;
199
+ // actual file lands at `<workspaceRoot>/<kind>/<artifactId><ext>`.
200
+ return artifactRelPath(artifactId, kind, ext);
96
201
  };
97
- const resolve = async (videoid) => {
98
- const meta = await loadMeta(videoid);
202
+ const resolve = async (artifactId) => {
203
+ const meta = await loadMeta(artifactId);
99
204
  // Only serve ready uploads. In-progress uploads stay hidden — a client
100
205
  // GETting mid-upload would otherwise receive a truncated file.
101
206
  if (!meta || !meta.ready)
102
207
  return null;
103
- const root = videoDir(videoid);
104
- const relFile = videoRelPath(videoid, meta.ext);
208
+ const relFile = artifactRelPath(artifactId, meta.kind, meta.ext);
105
209
  try {
106
- const stat = await fs.stat(path.join(root, relFile));
210
+ const stat = await fs.stat(path.join(workspaceRoot, relFile));
107
211
  if (!stat.isFile())
108
212
  return null;
109
213
  }
110
214
  catch (err) {
111
- if (err?.code === "ENOENT")
215
+ if (err?.code === 'ENOENT')
112
216
  return null;
113
217
  throw err;
114
218
  }
115
- return { kind: "stream", root, filename: relFile };
219
+ return {
220
+ kind: 'stream',
221
+ root: workspaceRoot,
222
+ filename: relFile,
223
+ contentType: extToContentType(meta.ext),
224
+ };
116
225
  };
117
- const markReady = async (videoid) => {
118
- const sidecar = await readSidecar(videoid);
226
+ const markReady = async (artifactId) => {
227
+ const sidecar = await readSidecar(artifactId);
119
228
  if (!sidecar) {
120
- // No sidecar means no `reserveUpload` happened for this videoid — this
121
- // is a contract violation by the caller, not a recoverable state.
122
- throw new Error(`markReady: no sidecar for videoid ${videoid} (was reserveUpload called?)`);
229
+ // No sidecar means no `reserveUpload` happened for this artifactId —
230
+ // this is a contract violation by the caller, not a recoverable state.
231
+ throw new Error(`markReady: no sidecar for artifactId ${artifactId} (was reserveUpload called?)`);
123
232
  }
124
- if (sidecar.status === "ready") {
233
+ if (sidecar.status === 'ready') {
125
234
  // Idempotent: already ready is fine, keep the cache consistent.
126
- metaCache.set(videoid, { ext: sidecar.ext, ready: true });
235
+ cacheSet(artifactId, sidecarToCachedMeta(sidecar, true));
127
236
  return;
128
237
  }
129
- await writeSidecar(videoid, { ...sidecar, status: "ready" });
130
- metaCache.set(videoid, { ext: sidecar.ext, ready: true });
131
- };
132
- const remove = async (videoid) => {
133
- const dir = videoDir(videoid);
134
- // Drop from cache before the rm so a racing `resolve` that arrives after
135
- // the rm but before the cache eviction can't hand back a stale path.
136
- metaCache.delete(videoid);
137
- try {
138
- // Note: no `force: true` — we want ENOENT to surface so the caller
139
- // (the DELETE route) can distinguish "removed something" from "nothing
140
- // to remove" and return 204 vs 404 accordingly.
141
- await fs.rm(dir, { recursive: true });
142
- return true;
143
- }
144
- catch (err) {
145
- if (err?.code === "ENOENT")
146
- return false;
147
- throw err;
148
- }
238
+ await writeSidecar(artifactId, { ...sidecar, status: 'ready' });
239
+ cacheSet(artifactId, sidecarToCachedMeta(sidecar, true));
240
+ };
241
+ const remove = async (artifactId) => {
242
+ const meta = await loadMeta(artifactId);
243
+ // Drop from cache before rm so a racing `resolve` arriving after the
244
+ // rm but before cache eviction can't hand back a stale path.
245
+ metaCache.delete(artifactId);
246
+ if (!meta)
247
+ return false;
248
+ const artifactPath = path.join(workspaceRoot, meta.kind, `${artifactId}${meta.ext}`);
249
+ await Promise.all([
250
+ fs.rm(artifactPath, { force: true }),
251
+ fs.rm(`${artifactPath}.json`, { force: true }),
252
+ fs.rm(sidecarPath(artifactId), { force: true }),
253
+ ]);
254
+ return true;
149
255
  };
150
- const getLocalPath = async (videoid) => {
151
- const meta = await loadMeta(videoid);
256
+ const getLocalPath = async (artifactId) => {
257
+ const meta = await loadMeta(artifactId);
152
258
  if (!meta)
153
259
  return null;
154
- return path.join(workspaceRoot, videoid, "video", `${videoid}${meta.ext}`);
260
+ return path.join(workspaceRoot, meta.kind, `${artifactId}${meta.ext}`);
261
+ };
262
+ const getKind = async (artifactId) => {
263
+ const meta = await loadMeta(artifactId);
264
+ return meta ? meta.kind : null;
265
+ };
266
+ const getRelatedTo = async (artifactId) => {
267
+ const meta = await loadMeta(artifactId);
268
+ return meta?.relatedTo ?? null;
269
+ };
270
+ const getChecksum = async (artifactId) => {
271
+ const meta = await loadMeta(artifactId);
272
+ return meta?.checksum ?? null;
155
273
  };
156
274
  return {
157
275
  datastore,
@@ -162,6 +280,9 @@ export function createLocalStorage(opts) {
162
280
  markReady,
163
281
  remove,
164
282
  getLocalPath,
283
+ getKind,
284
+ getRelatedTo,
285
+ getChecksum,
165
286
  };
166
287
  }
167
288
  //# sourceMappingURL=local.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/storage/local.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/storage/local.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ import type { S3ClientConfig } from '@aws-sdk/client-s3';
2
+ import type { PulseVaultStorage, UploadKind } from './types.js';
3
+ export type S3StorageOptions = {
4
+ /** Target bucket. Must already exist (the integrator provisions it). */
5
+ bucket: string;
6
+ /**
7
+ * Custom S3 endpoint. Set this for Cloudflare R2 or any S3-compatible store
8
+ * (e.g. `https://<account-id>.r2.cloudflarestorage.com`). Omit for AWS S3.
9
+ */
10
+ endpoint?: string;
11
+ /**
12
+ * AWS region. Required for AWS S3; for R2 use `"auto"`. When omitted and an
13
+ * `endpoint` is set this defaults to `"auto"`; otherwise the SDK resolves it
14
+ * from the environment.
15
+ */
16
+ region?: string;
17
+ /**
18
+ * Access key id. Optional — when both `accessKeyId` and `secretAccessKey`
19
+ * are omitted the AWS SDK default credential chain is used (env vars, IAM
20
+ * role, etc.). Never hard-code keys; read them from env in your app.
21
+ */
22
+ accessKeyId?: string;
23
+ /** Secret access key. See `accessKeyId`. */
24
+ secretAccessKey?: string;
25
+ /** Optional STS session token for temporary credentials. */
26
+ sessionToken?: string;
27
+ /**
28
+ * Use path-style addressing (`<endpoint>/<bucket>/<key>`) instead of
29
+ * virtual-host style. Defaults to `true` whenever a custom `endpoint` is set
30
+ * (R2 and most S3-compatible stores need it).
31
+ */
32
+ forcePathStyle?: boolean;
33
+ /** Presigned playback URL TTL in seconds. Defaults to 900 (15 minutes). */
34
+ presignTtlSeconds?: number;
35
+ /**
36
+ * Preferred multipart part size in bytes, forwarded to `@tus/s3-store`. Must
37
+ * be >= 5 MiB. When omitted, `@tus/s3-store` computes an optimal size.
38
+ */
39
+ partSize?: number;
40
+ /**
41
+ * Max number of entries kept in the in-memory metadata cache before the
42
+ * oldest (by insertion order) is evicted. A cache miss falls back to a
43
+ * bucket read, so eviction only costs an extra request, not correctness.
44
+ * Defaults to 10,000.
45
+ */
46
+ metaCacheLimit?: number;
47
+ /**
48
+ * Advanced escape hatch: extra `S3ClientConfig` fields merged into the
49
+ * client used for both playback presigning and the underlying TUS datastore
50
+ * (e.g. checksum flags for S3-compatible stores). Values here win over the
51
+ * fields derived from the options above.
52
+ */
53
+ clientConfig?: Partial<S3ClientConfig>;
54
+ };
55
+ /**
56
+ * S3 / Cloudflare R2 storage adapter. Uploads stream into the bucket via S3
57
+ * multipart upload (`@tus/s3-store`); playback is served by redirecting the
58
+ * client to a short-lived presigned GET URL, so bytes never flow back through
59
+ * the app server.
60
+ */
61
+ export type S3Storage = PulseVaultStorage & {
62
+ /** The bucket this adapter writes to. */
63
+ readonly bucket: string;
64
+ /**
65
+ * Ranged GET of the first `n` bytes of a finalized upload, or `null` if the
66
+ * artifactId is unknown. Used by `createS3Mp4Sniffer` to validate the
67
+ * payload without downloading the whole object.
68
+ */
69
+ readHeader(artifactId: string, n: number): Promise<Buffer | null>;
70
+ /**
71
+ * Full GET of a finalized upload's bytes, or `null` if the artifactId is
72
+ * unknown. Used by `createS3ChecksumValidator` — unlike `readHeader`, this
73
+ * downloads the whole object, so it's only worth using for an explicit,
74
+ * opt-in integrity check, not on every upload by default.
75
+ */
76
+ readAll(artifactId: string): Promise<Buffer | null>;
77
+ /**
78
+ * Streams a finalized upload's bytes through a hash digest, returning the
79
+ * hex digest, or `null` if the artifactId is unknown. The streaming
80
+ * counterpart to `readAll` — used by `createS3ChecksumValidator` so
81
+ * checksum verification never has to hold an entire (potentially
82
+ * multi-gigabyte) object in memory as one `Buffer`.
83
+ */
84
+ digestAll(artifactId: string, algorithm: 'sha256' | 'sha1' | 'md5'): Promise<string | null>;
85
+ /** Artifact kind for a known artifactId, or `null`. Satisfies `getKind`. */
86
+ getKind(artifactId: string): Promise<UploadKind | null>;
87
+ /** Satisfies the optional `PulseVaultStorage.getRelatedTo` contract. */
88
+ getRelatedTo(artifactId: string): Promise<string | null>;
89
+ /** Satisfies the optional `PulseVaultStorage.getChecksum` contract. */
90
+ getChecksum(artifactId: string): Promise<string | null>;
91
+ };
92
+ /**
93
+ * Build an S3/R2-backed storage adapter.
94
+ *
95
+ * Async because it lazily imports the optional `@aws-sdk/*` and
96
+ * `@tus/s3-store` packages — `await` it before registering the plugin:
97
+ *
98
+ * ```ts
99
+ * const storage = await createS3Storage({
100
+ * bucket: "pulse-videos",
101
+ * endpoint: `https://${ACCOUNT}.r2.cloudflarestorage.com`,
102
+ * accessKeyId: process.env.R2_ACCESS_KEY,
103
+ * secretAccessKey: process.env.R2_SECRET_KEY,
104
+ * });
105
+ * await app.register(pulseVault, { storage, prefix: "/pulsevault", maxUploadSize: Infinity });
106
+ * ```
107
+ */
108
+ export declare function createS3Storage(opts: S3StorageOptions): Promise<S3Storage>;
109
+ //# sourceMappingURL=s3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../src/storage/s3.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAY,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAEV,iBAAiB,EAEjB,UAAU,EACX,MAAM,YAAY,CAAC;AA8EpB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CACxC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,iBAAiB,GAAG;IAC1C,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAClE;;;;;OAKG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACpD;;;;;;OAMG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5F,4EAA4E;IAC5E,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACxD,wEAAwE;IACxE,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzD,uEAAuE;IACvE,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACzD,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CAoVhF"}