@mieweb/pulsevault 0.0.1 → 0.1.0

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 (70) hide show
  1. package/CHANGELOG.md +184 -0
  2. package/LICENSE +15 -39
  3. package/OPERATIONS.md +223 -0
  4. package/PROTOCOL.md +312 -0
  5. package/README.md +532 -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 +90 -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 +431 -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 +126 -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 +102 -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/magic.d.ts +30 -5
  38. package/dist/lib/magic.d.ts.map +1 -1
  39. package/dist/lib/magic.js +51 -14
  40. package/dist/lib/magic.js.map +1 -1
  41. package/dist/lib/options.d.ts +39 -0
  42. package/dist/lib/options.d.ts.map +1 -0
  43. package/dist/lib/options.js +103 -0
  44. package/dist/lib/options.js.map +1 -0
  45. package/dist/lib/pulsevaultTus.d.ts +52 -26
  46. package/dist/lib/pulsevaultTus.d.ts.map +1 -1
  47. package/dist/lib/pulsevaultTus.js +111 -43
  48. package/dist/lib/pulsevaultTus.js.map +1 -1
  49. package/dist/lib/request.d.ts +24 -0
  50. package/dist/lib/request.d.ts.map +1 -0
  51. package/dist/lib/request.js +16 -0
  52. package/dist/lib/request.js.map +1 -0
  53. package/dist/lib/uuid.d.ts.map +1 -1
  54. package/dist/lib/uuid.js +1 -1
  55. package/dist/lib/uuid.js.map +1 -1
  56. package/dist/routes/pulsevault.d.ts +18 -14
  57. package/dist/routes/pulsevault.d.ts.map +1 -1
  58. package/dist/routes/pulsevault.js +127 -247
  59. package/dist/routes/pulsevault.js.map +1 -1
  60. package/dist/storage/local.d.ts +31 -10
  61. package/dist/storage/local.d.ts.map +1 -1
  62. package/dist/storage/local.js +166 -82
  63. package/dist/storage/local.js.map +1 -1
  64. package/dist/storage/s3.d.ts +109 -0
  65. package/dist/storage/s3.d.ts.map +1 -0
  66. package/dist/storage/s3.js +432 -0
  67. package/dist/storage/s3.js.map +1 -0
  68. package/dist/storage/types.d.ts +69 -17
  69. package/dist/storage/types.d.ts.map +1 -1
  70. package/package.json +35 -7
@@ -1,30 +1,34 @@
1
- import type { FastifyPluginAsync, FastifyPluginOptions, FastifyRequest } from "fastify";
2
- import type { PulseVaultCacheOptions } from "../app.js";
3
- import { type PulseVaultOnUploadComplete } from "../lib/pulsevaultTus.js";
4
- import type { PulseVaultValidatePayload } from "../lib/magic.js";
5
- import type { PulseVaultStorage } from "../storage/types.js";
6
- declare module "fastify" {
1
+ import type { FastifyPluginAsync, FastifyPluginOptions } from 'fastify';
2
+ import type { PulseVaultCacheOptions } from '../app.js';
3
+ import { PROTOCOL_VERSION } from '../core.js';
4
+ import type { PulseVaultOnUploadComplete, PulseVaultOnArtifactEvent } from '../lib/pulsevaultTus.js';
5
+ import type { PulseVaultValidatePayload } from '../lib/magic.js';
6
+ import { type PulseVaultAuthorize } from '../lib/authorize.js';
7
+ import type { PulseVaultAllowedExtensions } from '../lib/options.js';
8
+ import type { PulseVaultStorage, UploadKind } from '../storage/types.js';
9
+ export type { PulseVaultAuthorize, PulseVaultAuthorizeContext, PulseVaultAuthorizePhase, } from '../lib/authorize.js';
10
+ declare module 'fastify' {
7
11
  interface FastifyRequest {
8
12
  pulseVault?: {
9
- videoid: string;
13
+ artifactId: string;
14
+ kind: UploadKind;
15
+ relatedTo?: string;
10
16
  };
11
17
  }
12
18
  }
13
- export type PulseVaultAuthorizePhase = "create" | "patch" | "resolve" | "delete";
14
- export type PulseVaultAuthorizeContext = {
15
- phase: PulseVaultAuthorizePhase;
16
- videoid: string;
17
- };
18
- export type PulseVaultAuthorize = (request: FastifyRequest, ctx: PulseVaultAuthorizeContext) => void | Promise<void>;
19
19
  export type PulseVaultRoutesOptions = {
20
20
  storage: PulseVaultStorage;
21
21
  maxUploadSize: number;
22
- allowedExtensions: readonly string[];
22
+ allowedExtensions: PulseVaultAllowedExtensions;
23
+ /** Advertised via `GET /capabilities` so the client knows which upload strategy this server expects. */
24
+ uploadUnit: 'beat' | 'merged';
23
25
  cache?: PulseVaultCacheOptions;
24
26
  authorize?: PulseVaultAuthorize;
25
27
  validatePayload?: PulseVaultValidatePayload;
26
28
  onUploadComplete?: PulseVaultOnUploadComplete;
29
+ onArtifactEvent?: PulseVaultOnArtifactEvent;
27
30
  } & FastifyPluginOptions;
28
31
  declare const pulseVaultRoutes: FastifyPluginAsync<PulseVaultRoutesOptions>;
29
32
  export default pulseVaultRoutes;
33
+ export { PROTOCOL_VERSION };
30
34
  //# sourceMappingURL=pulsevault.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pulsevault.d.ts","sourceRoot":"","sources":["../../src/routes/pulsevault.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,oBAAoB,EAEpB,cAAc,EAEf,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAGL,KAAK,0BAA0B,EAChC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAGjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAM7D,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,UAAU,CAAC,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;KAClC;CACF;AAED,MAAM,MAAM,wBAAwB,GAChC,QAAQ,GACR,OAAO,GACP,SAAS,GACT,QAAQ,CAAC;AAEb,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,EAAE,wBAAwB,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAChC,OAAO,EAAE,cAAc,EACvB,GAAG,EAAE,0BAA0B,KAC5B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,eAAe,CAAC,EAAE,yBAAyB,CAAC;IAC5C,gBAAgB,CAAC,EAAE,0BAA0B,CAAC;CAC/C,GAAG,oBAAoB,CAAC;AA8JzB,QAAA,MAAM,gBAAgB,EAAE,kBAAkB,CAAC,uBAAuB,CA4NjE,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"pulsevault.d.ts","sourceRoot":"","sources":["../../src/routes/pulsevault.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,oBAAoB,EAIrB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAwB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EACV,0BAA0B,EAC1B,yBAAyB,EAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEzE,YAAY,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,UAAU,CAAC,EAAE;YAAE,UAAU,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,UAAU,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC3E;CACF;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,2BAA2B,CAAC;IAC/C,wGAAwG;IACxG,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,eAAe,CAAC,EAAE,yBAAyB,CAAC;IAC5C,gBAAgB,CAAC,EAAE,0BAA0B,CAAC;IAC9C,eAAe,CAAC,EAAE,yBAAyB,CAAC;CAC7C,GAAG,oBAAoB,CAAC;AA4JzB,QAAA,MAAM,gBAAgB,EAAE,kBAAkB,CAAC,uBAAuB,CA8EjE,CAAC;AAEF,eAAe,gBAAgB,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -1,315 +1,195 @@
1
- import send from "@fastify/send";
2
- import { createPulsevaultTusServer, pulseVaultTusContext, } from "../lib/pulsevaultTus.js";
3
- import { pulseVaultError } from "../lib/errors.js";
4
- import { isUuid } from "../lib/uuid.js";
5
- /**
6
- * Pull `videoid` out of a raw `Upload-Metadata` header. Format is a
7
- * comma-separated list of `<key> <base64-value>` pairs (tus v1 creation
8
- * extension). We only care about `videoid`; everything else is left to the
9
- * tus server proper.
10
- */
11
- function parseVideoidFromMetadata(header) {
12
- for (const pair of header.split(",")) {
13
- const trimmed = pair.trim();
14
- if (!trimmed)
15
- continue;
16
- const sep = trimmed.indexOf(" ");
17
- if (sep < 0)
18
- continue;
19
- const key = trimmed.slice(0, sep);
20
- if (key !== "videoid")
21
- continue;
22
- const value = trimmed.slice(sep + 1).trim();
23
- if (!value)
24
- return undefined;
25
- try {
26
- const decoded = Buffer.from(value, "base64").toString("utf8");
27
- return isUuid(decoded) ? decoded : undefined;
28
- }
29
- catch {
30
- return undefined;
31
- }
32
- }
33
- return undefined;
34
- }
35
- /**
36
- * Decode the last URL segment of a tus PATCH/HEAD/DELETE (base64url-encoded
37
- * id) and extract the first path component, which the plugin always shapes as
38
- * the videoid (see `pulseVaultTus.ts`).
39
- */
40
- function videoidFromTusUrl(url) {
41
- const match = url.match(/\/upload\/([^/?#]+)/);
42
- if (!match?.[1])
43
- return undefined;
44
- let decoded;
45
- try {
46
- decoded = Buffer.from(match[1], "base64url").toString("utf8");
47
- }
48
- catch {
49
- return undefined;
50
- }
51
- const first = decoded.split("/", 1)[0];
52
- return isUuid(first) ? first : undefined;
53
- }
54
- function extractAuthzStatus(err) {
55
- const e = err;
56
- if (typeof e?.statusCode === "number")
57
- return e.statusCode;
58
- if (typeof e?.status_code === "number")
59
- return e.status_code;
60
- return 403;
61
- }
62
- function extractAuthzMessage(err) {
63
- if (err instanceof Error && err.message)
64
- return err.message;
65
- return "Forbidden";
66
- }
1
+ import { createPulseVaultCore, PROTOCOL_VERSION } from '../core.js';
2
+ import {} from '../lib/authorize.js';
67
3
  const pulseVaultErrorResponse = {
68
- type: "object",
4
+ type: 'object',
69
5
  properties: {
70
- ok: { type: "boolean" },
71
- error: { type: "string" },
6
+ ok: { type: 'boolean' },
7
+ error: { type: 'string' },
72
8
  },
73
- required: ["ok", "error"],
9
+ required: ['ok', 'error'],
74
10
  };
75
11
  const tusRouteSchema = {
76
- tags: ["pulsevault"],
77
- summary: "TUS resumable upload endpoint",
78
- description: "TUS v1 resumable upload protocol.\n\n" +
79
- "- `POST` creates a new upload. The `Upload-Metadata` header must include a base64-encoded `videoid` pair.\n" +
80
- "- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n" +
81
- "- `HEAD` returns the current offset for a resumable upload.\n" +
82
- "- `DELETE` cancels an in-flight upload.\n\n" +
83
- "See https://tus.io/protocols/resumable-upload for the full protocol.",
12
+ tags: ['pulsevault'],
13
+ summary: 'TUS resumable upload endpoint',
14
+ description: 'TUS v1 resumable upload protocol.\n\n' +
15
+ '- `POST` creates a new upload. The `Upload-Metadata` header must include base64-encoded key/value pairs:\n' +
16
+ ' - `artifactId` (or the legacy `videoid`/`projectid` aliases) — a UUID generated by your server.\n' +
17
+ " - `filename` — original filename; the extension must match the kind's allowed list.\n" +
18
+ ' - `kind` — `video` (default), `project`, or `captions`. Determines the storage subdir and which completion hooks fire.\n' +
19
+ " - `relatedTo` — optional UUID of another artifact this one belongs to (e.g. a video's captions, or a beat belonging to a pulse manifest's session).\n" +
20
+ ' - `checksum` — optional `<algorithm>:<hex digest>` of the finished file, verified post-upload if a checksum validator is configured.\n' +
21
+ '- `PATCH` appends a chunk at the offset given by `Upload-Offset`, with `Content-Type: application/offset+octet-stream`.\n' +
22
+ '- `HEAD` returns the current offset for a resumable upload.\n' +
23
+ '- `DELETE` cancels an in-flight upload.\n\n' +
24
+ 'See https://tus.io/protocols/resumable-upload for the full protocol.',
84
25
  response: {
85
- 400: { description: "Invalid request.", ...pulseVaultErrorResponse },
26
+ 400: { description: 'Invalid request.', ...pulseVaultErrorResponse },
86
27
  403: {
87
- description: "Authorize hook rejected the request.",
28
+ description: 'Authorize hook rejected the request.',
88
29
  ...pulseVaultErrorResponse,
89
30
  },
90
31
  },
91
32
  };
92
- const videoDeleteSchema = {
93
- tags: ["pulsevault"],
94
- summary: "Delete an uploaded video",
95
- description: "Deletes all storage for a videoid (bytes + sidecar metadata). Runs the `authorize` hook with `phase: \"delete\"` before the adapter's `remove` is called. Returns 204 on success, 404 if the videoid was unknown, 501 if the adapter does not implement `remove`.",
33
+ const artifactDeleteSchema = {
34
+ tags: ['pulsevault'],
35
+ summary: 'Delete an uploaded artifact',
36
+ description: 'Deletes all storage for an artifactId (bytes + sidecar metadata), regardless of kind. Runs the `authorize` hook with `phase: "delete"` before the adapter\'s `remove` is called. Returns 204 on success, 404 if the artifactId was unknown, 501 if the adapter does not implement `remove`.',
96
37
  params: {
97
- type: "object",
38
+ type: 'object',
98
39
  properties: {
99
- videoid: {
100
- type: "string",
101
- format: "uuid",
102
- description: "UUID of the upload to delete.",
40
+ artifactId: {
41
+ type: 'string',
42
+ format: 'uuid',
43
+ description: 'UUID of the upload to delete.',
103
44
  },
104
45
  },
105
- required: ["videoid"],
46
+ required: ['artifactId'],
106
47
  },
107
48
  response: {
108
49
  400: {
109
- description: "`videoid` is not a valid UUID.",
50
+ description: '`artifactId` is not a valid UUID.',
110
51
  ...pulseVaultErrorResponse,
111
52
  },
112
53
  403: {
113
- description: "Authorize hook rejected the request.",
54
+ description: 'Authorize hook rejected the request.',
114
55
  ...pulseVaultErrorResponse,
115
56
  },
116
- 404: { description: "Video not found.", ...pulseVaultErrorResponse },
57
+ 404: { description: 'Artifact not found.', ...pulseVaultErrorResponse },
117
58
  501: {
118
- description: "Storage adapter does not implement delete.",
59
+ description: 'Storage adapter does not implement delete.',
119
60
  ...pulseVaultErrorResponse,
120
61
  },
121
62
  },
122
63
  };
123
- const videoGetSchema = {
124
- tags: ["pulsevault"],
125
- summary: "Serve a previously uploaded video",
126
- description: "Resolves the `videoid` through the configured storage adapter and either streams the bytes or redirects (for CDN-backed adapters). Runs the `authorize` hook before resolve.",
64
+ const artifactGetSchema = {
65
+ tags: ['pulsevault'],
66
+ summary: 'Serve a previously uploaded artifact',
67
+ description: "Resolves the `artifactId` through the configured storage adapter and either streams the bytes or redirects (for CDN-backed adapters). The artifact's kind (video, project, or captions) is resolved from storage, not the URL. Runs the `authorize` hook before resolve.",
127
68
  params: {
128
- type: "object",
69
+ type: 'object',
70
+ properties: {
71
+ artifactId: {
72
+ type: 'string',
73
+ format: 'uuid',
74
+ description: 'UUID returned from the upload flow.',
75
+ },
76
+ },
77
+ required: ['artifactId'],
78
+ },
79
+ querystring: {
80
+ type: 'object',
129
81
  properties: {
130
- videoid: {
131
- type: "string",
132
- format: "uuid",
133
- description: "UUID returned from the reserve/TUS-create flow.",
82
+ token: {
83
+ type: 'string',
84
+ description: 'Optional bearer token for pre-authenticated watch links. Forwarded to the `authorize` hook as `ctx.token` so parent servers can validate it without a separate login step.',
134
85
  },
135
86
  },
136
- required: ["videoid"],
137
87
  },
138
88
  response: {
139
89
  400: {
140
- description: "`videoid` is not a valid UUID.",
90
+ description: '`artifactId` is not a valid UUID.',
141
91
  ...pulseVaultErrorResponse,
142
92
  },
143
93
  403: {
144
- description: "Authorize hook rejected the request.",
94
+ description: 'Authorize hook rejected the request.',
145
95
  ...pulseVaultErrorResponse,
146
96
  },
147
- 404: { description: "Video not found.", ...pulseVaultErrorResponse },
97
+ 404: { description: 'Artifact not found.', ...pulseVaultErrorResponse },
148
98
  },
149
99
  };
100
+ const capabilitiesSchema = {
101
+ tags: ['pulsevault'],
102
+ summary: "Discover this deployment's protocol version and configuration",
103
+ description: 'Unauthenticated — the response carries no secrets. Lets a client detect protocol compatibility before pairing, and which upload strategy (`uploadUnit`) this server expects.',
104
+ response: {
105
+ 200: {
106
+ type: 'object',
107
+ properties: {
108
+ protocolVersion: { type: 'number' },
109
+ minSupportedVersion: { type: 'number' },
110
+ maxSupportedVersion: { type: 'number' },
111
+ uploadUnit: { type: 'string', enum: ['beat', 'merged'] },
112
+ kinds: { type: 'array', items: { type: 'string' } },
113
+ allowedExtensions: {
114
+ type: 'object',
115
+ properties: {
116
+ video: { type: 'array', items: { type: 'string' } },
117
+ project: { type: 'array', items: { type: 'string' } },
118
+ captions: { type: 'array', items: { type: 'string' } },
119
+ },
120
+ },
121
+ maxUploadSize: { type: 'number' },
122
+ checksum: {
123
+ type: 'object',
124
+ properties: { algorithms: { type: 'array', items: { type: 'string' } } },
125
+ },
126
+ },
127
+ },
128
+ },
129
+ };
130
+ /** Coerce the raw route param to a string; an invalid UUID is rejected by the core's own check either way. */
131
+ function paramToString(value) {
132
+ return typeof value === 'string' ? value : '';
133
+ }
150
134
  const pulseVaultRoutes = async (fastify, opts) => {
151
- const { storage, maxUploadSize, allowedExtensions, cache, authorize, validatePayload, onUploadComplete, } = opts;
152
- // `fastify.prefix` is `""` when the plugin is mounted at the root.
153
- const tusPath = `${fastify.prefix}/upload`;
154
- const tusServer = createPulsevaultTusServer({
135
+ const { storage, maxUploadSize, allowedExtensions, uploadUnit, cache, authorize, validatePayload, onUploadComplete, onArtifactEvent, } = opts;
136
+ // All hook orchestration, tus glue, and artifact GET/DELETE logic lives in
137
+ // the framework-agnostic core (`../core.js`); this file only wires Fastify's
138
+ // routing/schema/lifecycle around it, so behavior can't drift from the
139
+ // `./core` entry point non-Fastify hosts use.
140
+ const core = createPulseVaultCore({
155
141
  storage,
156
- tusPath,
157
- maxSize: maxUploadSize,
142
+ // `fastify.prefix` is `""` when the plugin is mounted at the root.
143
+ basePath: fastify.prefix,
144
+ maxUploadSize,
145
+ uploadUnit,
158
146
  allowedExtensions,
147
+ cache,
148
+ authorize,
159
149
  validatePayload,
160
150
  onUploadComplete,
151
+ onArtifactEvent,
152
+ logger: fastify.log,
161
153
  });
162
- fastify.addContentTypeParser("application/offset+octet-stream", (_request, _payload, done) => {
154
+ fastify.addContentTypeParser('application/offset+octet-stream', (_request, _payload, done) => {
163
155
  done(null);
164
156
  });
165
- /**
166
- * Run the consumer's `authorize` hook (if any) for a TUS request. Returns
167
- * `true` iff the request may proceed; on rejection, this function already
168
- * wrote the response.
169
- */
170
- const runAuthorize = async (request, reply, phase) => {
171
- let videoid;
172
- if (phase === "create") {
173
- const meta = request.headers["upload-metadata"];
174
- if (typeof meta === "string") {
175
- videoid = parseVideoidFromMetadata(meta);
176
- }
177
- }
178
- else {
179
- videoid = videoidFromTusUrl(request.url);
180
- }
181
- if (videoid) {
182
- request.pulseVault = { videoid };
183
- }
184
- if (!authorize) {
185
- return { ok: true, videoid };
186
- }
187
- // If we can't extract a videoid, let tus produce its own 4xx for malformed
188
- // input rather than synthesize a fake authorize failure.
189
- if (!videoid) {
190
- return { ok: true, videoid };
191
- }
192
- try {
193
- await authorize(request, { phase, videoid });
194
- return { ok: true, videoid };
195
- }
196
- catch (err) {
197
- const statusCode = extractAuthzStatus(err);
198
- const message = extractAuthzMessage(err);
199
- request.log.info({ err, videoid, phase, statusCode }, "pulsevault authorize rejected");
200
- await reply.code(statusCode).send(pulseVaultError(message));
201
- return { ok: false };
202
- }
203
- };
157
+ const tusMethods = ['POST', 'PATCH', 'HEAD', 'DELETE', 'OPTIONS'];
204
158
  const tusHandler = async (request, reply) => {
205
- const phase = request.method === "POST" ? "create" : "patch";
206
- const authz = await runAuthorize(request, reply, phase);
207
- if (!authz.ok)
208
- return;
209
- // Once hijacked, Fastify will not write to this reply on our behalf, so we
210
- // must translate any unexpected throw from `@tus/server` into a response
211
- // ourselves — otherwise the socket stays open until the client times out.
159
+ // The core writes directly to the raw response (tus dispatch, authorize
160
+ // rejections, error translation) — hijack so Fastify doesn't also try to
161
+ // manage this reply afterward.
212
162
  reply.hijack();
213
- try {
214
- await pulseVaultTusContext.run({ request, videoid: authz.videoid }, () => tusServer.handle(request.raw, reply.raw));
215
- }
216
- catch (err) {
217
- request.log.error({ err }, "pulsevault tus handler failed");
218
- if (reply.raw.headersSent || reply.raw.writableEnded) {
219
- reply.raw.destroy();
220
- return;
221
- }
222
- reply.raw.statusCode = 500;
223
- reply.raw.setHeader("content-type", "text/plain; charset=utf-8");
224
- reply.raw.end("Internal Server Error");
225
- }
163
+ await core.handleTus(request.raw, reply.raw);
226
164
  };
227
- const tusMethods = ["POST", "PATCH", "HEAD", "DELETE", "OPTIONS"];
228
165
  fastify.route({
229
166
  method: [...tusMethods],
230
- url: "/upload",
167
+ url: '/upload',
231
168
  schema: tusRouteSchema,
232
169
  handler: tusHandler,
233
170
  });
234
171
  fastify.route({
235
172
  method: [...tusMethods],
236
- url: "/upload/*",
173
+ url: '/upload/*',
237
174
  schema: tusRouteSchema,
238
175
  handler: tusHandler,
239
176
  });
240
- fastify.delete("/:videoid", { schema: videoDeleteSchema }, async (request, reply) => {
241
- const videoid = request.params?.videoid;
242
- if (!isUuid(videoid)) {
243
- return reply
244
- .code(400)
245
- .send(pulseVaultError("`videoid` must be a valid UUID"));
246
- }
247
- request.pulseVault = { videoid };
248
- if (authorize) {
249
- try {
250
- await authorize(request, { phase: "delete", videoid });
251
- }
252
- catch (err) {
253
- const statusCode = extractAuthzStatus(err);
254
- const message = extractAuthzMessage(err);
255
- request.log.info({ err, videoid, phase: "delete", statusCode }, "pulsevault authorize rejected");
256
- return reply.code(statusCode).send(pulseVaultError(message));
257
- }
258
- }
259
- if (typeof storage.remove !== "function") {
260
- return reply
261
- .code(501)
262
- .send(pulseVaultError("Storage adapter does not support delete"));
263
- }
264
- const removed = await storage.remove(videoid);
265
- if (!removed) {
266
- return reply.code(404).send(pulseVaultError("Video not found"));
267
- }
268
- return reply.code(204).send();
177
+ fastify.get('/capabilities', { schema: capabilitiesSchema }, async (request, reply) => {
178
+ reply.hijack();
179
+ await core.handleCapabilities(request.raw, reply.raw);
180
+ });
181
+ fastify.delete('/artifacts/:artifactId', { schema: artifactDeleteSchema }, async (request, reply) => {
182
+ const artifactId = paramToString(request.params?.artifactId);
183
+ reply.hijack();
184
+ await core.handleArtifactDelete(request.raw, reply.raw, artifactId);
269
185
  });
270
- fastify.get("/:videoid", { schema: videoGetSchema }, async (request, reply) => {
271
- const videoid = request.params?.videoid;
272
- if (!isUuid(videoid)) {
273
- return reply
274
- .code(400)
275
- .send(pulseVaultError("`videoid` must be a valid UUID"));
276
- }
277
- request.pulseVault = { videoid };
278
- // Run authorize *before* resolve so consumers can reject without the
279
- // response leaking "this videoid exists but you don't own it" vs. "no such
280
- // videoid".
281
- if (authorize) {
282
- try {
283
- await authorize(request, { phase: "resolve", videoid });
284
- }
285
- catch (err) {
286
- const statusCode = extractAuthzStatus(err);
287
- const message = extractAuthzMessage(err);
288
- request.log.info({ err, videoid, phase: "resolve", statusCode }, "pulsevault authorize rejected");
289
- return reply.code(statusCode).send(pulseVaultError(message));
290
- }
291
- }
292
- const resolved = await storage.resolve(videoid);
293
- if (!resolved) {
294
- return reply.code(404).send(pulseVaultError("Video not found"));
295
- }
296
- if (resolved.kind === "redirect") {
297
- return reply.redirect(resolved.url, resolved.statusCode ?? 302);
298
- }
299
- const result = await send(request.raw, resolved.filename, {
300
- root: resolved.root,
301
- ...cache,
302
- });
303
- if (result.type === "error") {
304
- return reply
305
- .code(result.statusCode)
306
- .send(pulseVaultError(result.metadata.error.message));
307
- }
308
- for (const [name, value] of Object.entries(result.headers)) {
309
- reply.header(name, value);
310
- }
311
- return reply.code(result.statusCode).send(result.stream);
186
+ fastify.get('/artifacts/:artifactId', { schema: artifactGetSchema }, async (request, reply) => {
187
+ const artifactId = paramToString(request.params?.artifactId);
188
+ const token = request.query?.token;
189
+ reply.hijack();
190
+ await core.handleArtifactGet(request.raw, reply.raw, artifactId, token);
312
191
  });
313
192
  };
314
193
  export default pulseVaultRoutes;
194
+ export { PROTOCOL_VERSION };
315
195
  //# sourceMappingURL=pulsevault.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pulsevault.js","sourceRoot":"","sources":["../../src/routes/pulsevault.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pulsevault.js","sourceRoot":"","sources":["../../src/routes/pulsevault.ts"],"names":[],"mappings":"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"}
@@ -1,34 +1,55 @@
1
- import type { PulseVaultStorage } from "./types.js";
1
+ import type { PulseVaultStorage, UploadKind } from './types.js';
2
2
  export type LocalStorageOptions = {
3
- /** Directory where per-video subdirectories are stored. Resolved against CWD if relative. */
3
+ /** Directory where uploads are stored (flat kind-scoped subdirs). Resolved against CWD if relative. */
4
4
  workspaceDir: string;
5
+ /**
6
+ * Max number of entries kept in the in-memory metadata cache before the
7
+ * oldest (by insertion order) is evicted. A cache miss falls back to
8
+ * reading the sidecar from disk, so eviction only costs an extra read, not
9
+ * correctness. Defaults to 10,000.
10
+ */
11
+ metaCacheLimit?: number;
5
12
  };
6
13
  /**
7
14
  * Local adapter storage. Layout contract (stable; downstream tools may rely
8
15
  * on it):
9
16
  *
10
17
  * ```text
11
- * <workspaceRoot>/<videoid>/
12
- * .pulsevault.json # our sidecar: { version, ext, filename, status }
13
- * video/<videoid><ext> # finalized upload bytes
14
- * video/<videoid><ext>.json # @tus/file-store's offset/metadata sidecar
18
+ * <workspaceRoot>/
19
+ * .pulsevault/<artifactId>.json # sidecar: { version, ext, filename, status, kind, relatedTo }
20
+ * video/<artifactId><ext> # finalized video bytes
21
+ * video/<artifactId><ext>.json # @tus/file-store offset/metadata sidecar
22
+ * project/<artifactId><ext> # finalized project bytes
23
+ * project/<artifactId><ext>.json # @tus/file-store offset/metadata sidecar
24
+ * captions/<artifactId><ext> # finalized captions bytes
25
+ * captions/<artifactId><ext>.json # @tus/file-store offset/metadata sidecar
15
26
  * ```
16
27
  *
17
28
  * `workspaceRoot` is exposed so consumers can layer post-processing (e.g.
18
29
  * hydrate an ArtiPod with `video/`, `transcripts/`, `frames/` mounts) against
19
30
  * the same on-disk tree from an `onUploadComplete` hook. `getLocalPath` is
20
31
  * exposed for `validatePayload` helpers that need to sniff the bytes before
21
- * the video is marked ready.
32
+ * the upload is marked ready.
22
33
  */
23
34
  export type LocalStorage = PulseVaultStorage & {
24
35
  readonly workspaceRoot: string;
25
36
  /**
26
- * Return the absolute path to the upload bytes for `videoid`, regardless
37
+ * Return the absolute path to the upload bytes for `artifactId`, regardless
27
38
  * of ready state. Falls back to reading the sidecar if the in-memory cache
28
39
  * is cold (so it works even after a server restart mid-upload). Returns
29
- * `null` if the videoid is unknown.
40
+ * `null` if the artifactId is unknown.
41
+ */
42
+ getLocalPath(artifactId: string): Promise<string | null>;
43
+ /**
44
+ * Return the artifact kind for a known artifactId without a full resolve.
45
+ * Returns `null` if the artifactId is unknown. Satisfies the optional
46
+ * `PulseVaultStorage.getKind` contract.
30
47
  */
31
- getLocalPath(videoid: string): Promise<string | null>;
48
+ getKind(artifactId: string): Promise<UploadKind | null>;
49
+ /** Satisfies the optional `PulseVaultStorage.getRelatedTo` contract. */
50
+ getRelatedTo(artifactId: string): Promise<string | null>;
51
+ /** Satisfies the optional `PulseVaultStorage.getChecksum` contract. */
52
+ getChecksum(artifactId: string): Promise<string | null>;
32
53
  };
33
54
  export declare function createLocalStorage(opts: LocalStorageOptions): LocalStorage;
34
55
  //# sourceMappingURL=local.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/storage/local.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,iBAAiB,EAElB,MAAM,YAAY,CAAC;AA6BpB,MAAM,MAAM,mBAAmB,GAAG;IAChC,6FAA6F;IAC7F,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,GAAG,iBAAiB,GAAG;IAC7C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACvD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,GAAG,YAAY,CAkL1E"}
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/storage/local.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,iBAAiB,EAEjB,UAAU,EACX,MAAM,YAAY,CAAC;AAwEpB,MAAM,MAAM,mBAAmB,GAAG;IAChC,uGAAuG;IACvG,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,YAAY,GAAG,iBAAiB,GAAG;IAC7C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzD;;;;OAIG;IACH,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,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,GAAG,YAAY,CAuO1E"}