@mieweb/pulsevault 0.0.2 → 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.
- package/CHANGELOG.md +184 -0
- package/LICENSE +15 -39
- package/OPERATIONS.md +223 -0
- package/PROTOCOL.md +312 -0
- package/README.md +532 -122
- package/augment.d.ts +1 -0
- package/augment.js +3 -0
- package/core.d.ts +1 -0
- package/core.js +7 -0
- package/dist/app.d.ts +90 -34
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +23 -33
- package/dist/app.js.map +1 -1
- package/dist/augment.d.ts +14 -7
- package/dist/augment.d.ts.map +1 -1
- package/dist/augment.js.map +1 -1
- package/dist/core.d.ts +108 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +431 -0
- package/dist/core.js.map +1 -0
- package/dist/lib/authorize.d.ts +19 -0
- package/dist/lib/authorize.d.ts.map +1 -0
- package/dist/lib/authorize.js +2 -0
- package/dist/lib/authorize.js.map +1 -0
- package/dist/lib/capability-token.d.ts +80 -0
- package/dist/lib/capability-token.d.ts.map +1 -0
- package/dist/lib/capability-token.js +126 -0
- package/dist/lib/capability-token.js.map +1 -0
- package/dist/lib/checksum.d.ts +45 -0
- package/dist/lib/checksum.d.ts.map +1 -0
- package/dist/lib/checksum.js +102 -0
- package/dist/lib/checksum.js.map +1 -0
- package/dist/lib/deeplinks.d.ts +37 -21
- package/dist/lib/deeplinks.d.ts.map +1 -1
- package/dist/lib/deeplinks.js +54 -13
- package/dist/lib/deeplinks.js.map +1 -1
- package/dist/lib/magic.d.ts +30 -5
- package/dist/lib/magic.d.ts.map +1 -1
- package/dist/lib/magic.js +51 -14
- package/dist/lib/magic.js.map +1 -1
- package/dist/lib/options.d.ts +39 -0
- package/dist/lib/options.d.ts.map +1 -0
- package/dist/lib/options.js +103 -0
- package/dist/lib/options.js.map +1 -0
- package/dist/lib/pulsevaultTus.d.ts +52 -26
- package/dist/lib/pulsevaultTus.d.ts.map +1 -1
- package/dist/lib/pulsevaultTus.js +111 -43
- package/dist/lib/pulsevaultTus.js.map +1 -1
- package/dist/lib/request.d.ts +24 -0
- package/dist/lib/request.d.ts.map +1 -0
- package/dist/lib/request.js +16 -0
- package/dist/lib/request.js.map +1 -0
- package/dist/lib/uuid.d.ts.map +1 -1
- package/dist/lib/uuid.js +1 -1
- package/dist/lib/uuid.js.map +1 -1
- package/dist/routes/pulsevault.d.ts +18 -16
- package/dist/routes/pulsevault.d.ts.map +1 -1
- package/dist/routes/pulsevault.js +120 -251
- package/dist/routes/pulsevault.js.map +1 -1
- package/dist/storage/local.d.ts +31 -10
- package/dist/storage/local.d.ts.map +1 -1
- package/dist/storage/local.js +166 -82
- package/dist/storage/local.js.map +1 -1
- package/dist/storage/s3.d.ts +109 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +432 -0
- package/dist/storage/s3.js.map +1 -0
- package/dist/storage/types.d.ts +69 -17
- package/dist/storage/types.d.ts.map +1 -1
- package/package.json +35 -7
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { PulseVaultValidatePayload } from './magic.js';
|
|
2
|
+
import type { PulseVaultOnUploadComplete } from './pulsevaultTus.js';
|
|
3
|
+
/** Per-kind form of `PulseVaultAllowedExtensionsInput`, named once so it isn't re-declared at every use site. */
|
|
4
|
+
type AllowedExtensionsByKind = {
|
|
5
|
+
video?: readonly string[];
|
|
6
|
+
project?: readonly string[];
|
|
7
|
+
captions?: readonly string[];
|
|
8
|
+
};
|
|
9
|
+
export type PulseVaultAllowedExtensionsInput = readonly string[] | AllowedExtensionsByKind;
|
|
10
|
+
export type PulseVaultAllowedExtensions = {
|
|
11
|
+
video: readonly string[];
|
|
12
|
+
project: readonly string[];
|
|
13
|
+
captions: readonly string[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Normalize the consumer's `allowedExtensions` option (legacy array or per-kind
|
|
17
|
+
* object) into the canonical `{ video, project, captions }` shape both
|
|
18
|
+
* adapters pass down to the tus layer.
|
|
19
|
+
*/
|
|
20
|
+
export declare function normalizeAllowedExtensions(raw: PulseVaultAllowedExtensionsInput | undefined): PulseVaultAllowedExtensions;
|
|
21
|
+
/** Shared by the Fastify plugin's `prefix` and the core's `basePath` — same shape, different option name. */
|
|
22
|
+
export declare function validateBasePath(basePath: string, optionName: string): void;
|
|
23
|
+
export declare function validateMaxUploadSize(maxUploadSize: number): void;
|
|
24
|
+
export declare function validateUploadUnit(uploadUnit: 'beat' | 'merged' | undefined): void;
|
|
25
|
+
export declare function validateAllowedExtensions(allowedExtensions: PulseVaultAllowedExtensionsInput | undefined): void;
|
|
26
|
+
/**
|
|
27
|
+
* One-time (not per-request) warning for the now-deprecated per-kind project
|
|
28
|
+
* hooks, so consumers notice the migration need instead of it being silently
|
|
29
|
+
* aliased away until a future major version removes it outright.
|
|
30
|
+
*/
|
|
31
|
+
export declare function warnIfUsingDeprecatedProjectHooks(opts: {
|
|
32
|
+
validateProjectPayload?: unknown;
|
|
33
|
+
onProjectUploadComplete?: unknown;
|
|
34
|
+
}): void;
|
|
35
|
+
/** Compose the legacy per-kind hook (if any) with the generic hook, mapped onto the `"project"` kind only. */
|
|
36
|
+
export declare function composeValidatePayload(generic: PulseVaultValidatePayload | undefined, legacyProject: PulseVaultValidatePayload | undefined): PulseVaultValidatePayload | undefined;
|
|
37
|
+
export declare function composeOnUploadComplete(generic: PulseVaultOnUploadComplete | undefined, legacyProject: PulseVaultOnUploadComplete | undefined): PulseVaultOnUploadComplete | undefined;
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/lib/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAErE,iHAAiH;AACjH,KAAK,uBAAuB,GAAG;IAC7B,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,gCAAgC,GAAG,SAAS,MAAM,EAAE,GAAG,uBAAuB,CAAC;AAE3F,MAAM,MAAM,2BAA2B,GAAG;IACxC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B,CAAC;AAOF;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,gCAAgC,GAAG,SAAS,GAChD,2BAA2B,CAsB7B;AAED,6GAA6G;AAC7G,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAM3E;AAED,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAIjE;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,IAAI,CAIlF;AAED,wBAAgB,yBAAyB,CACvC,iBAAiB,EAAE,gCAAgC,GAAG,SAAS,GAC9D,IAAI,CAgBN;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAAC,IAAI,EAAE;IACtD,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,GAAG,IAAI,CAaP;AAED,8GAA8G;AAC9G,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,yBAAyB,GAAG,SAAS,EAC9C,aAAa,EAAE,yBAAyB,GAAG,SAAS,GACnD,yBAAyB,GAAG,SAAS,CASvC;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,0BAA0B,GAAG,SAAS,EAC/C,aAAa,EAAE,0BAA0B,GAAG,SAAS,GACpD,0BAA0B,GAAG,SAAS,CASxC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const DEFAULT_VIDEO_EXTENSIONS = ['.mp4'];
|
|
2
|
+
const DEFAULT_PROJECT_EXTENSIONS = ['.pulse', '.zip'];
|
|
3
|
+
const DEFAULT_CAPTIONS_EXTENSIONS = ['.srt'];
|
|
4
|
+
const EXTENSION_REGEX = /^\.[^.\s/\\]+$/;
|
|
5
|
+
/**
|
|
6
|
+
* Normalize the consumer's `allowedExtensions` option (legacy array or per-kind
|
|
7
|
+
* object) into the canonical `{ video, project, captions }` shape both
|
|
8
|
+
* adapters pass down to the tus layer.
|
|
9
|
+
*/
|
|
10
|
+
export function normalizeAllowedExtensions(raw) {
|
|
11
|
+
if (!raw) {
|
|
12
|
+
return {
|
|
13
|
+
video: DEFAULT_VIDEO_EXTENSIONS,
|
|
14
|
+
project: DEFAULT_PROJECT_EXTENSIONS,
|
|
15
|
+
captions: DEFAULT_CAPTIONS_EXTENSIONS,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (Array.isArray(raw)) {
|
|
19
|
+
// Legacy flat array: treat as video-only, project/captions keep their defaults.
|
|
20
|
+
return {
|
|
21
|
+
video: raw.map((ext) => ext.toLowerCase()),
|
|
22
|
+
project: DEFAULT_PROJECT_EXTENSIONS,
|
|
23
|
+
captions: DEFAULT_CAPTIONS_EXTENSIONS,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const byKind = raw;
|
|
27
|
+
return {
|
|
28
|
+
video: (byKind.video ?? DEFAULT_VIDEO_EXTENSIONS).map((ext) => ext.toLowerCase()),
|
|
29
|
+
project: (byKind.project ?? DEFAULT_PROJECT_EXTENSIONS).map((ext) => ext.toLowerCase()),
|
|
30
|
+
captions: (byKind.captions ?? DEFAULT_CAPTIONS_EXTENSIONS).map((ext) => ext.toLowerCase()),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Shared by the Fastify plugin's `prefix` and the core's `basePath` — same shape, different option name. */
|
|
34
|
+
export function validateBasePath(basePath, optionName) {
|
|
35
|
+
if (basePath !== '' && (!basePath.startsWith('/') || basePath.endsWith('/'))) {
|
|
36
|
+
throw new TypeError(`\`${optionName}\` must be '' or start with '/' with no trailing slash (e.g. '/pulsevault')`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function validateMaxUploadSize(maxUploadSize) {
|
|
40
|
+
if (!(maxUploadSize > 0)) {
|
|
41
|
+
throw new TypeError('`maxUploadSize` must be a positive number (use Infinity for no cap)');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function validateUploadUnit(uploadUnit) {
|
|
45
|
+
if (uploadUnit && uploadUnit !== 'beat' && uploadUnit !== 'merged') {
|
|
46
|
+
throw new TypeError('`uploadUnit` must be "beat" or "merged"');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function validateAllowedExtensions(allowedExtensions) {
|
|
50
|
+
if (!allowedExtensions)
|
|
51
|
+
return;
|
|
52
|
+
const exts = Array.isArray(allowedExtensions)
|
|
53
|
+
? allowedExtensions
|
|
54
|
+
: [
|
|
55
|
+
...(allowedExtensions.video ?? []),
|
|
56
|
+
...(allowedExtensions.project ?? []),
|
|
57
|
+
...(allowedExtensions.captions ?? []),
|
|
58
|
+
];
|
|
59
|
+
for (const ext of exts) {
|
|
60
|
+
if (!EXTENSION_REGEX.test(ext)) {
|
|
61
|
+
throw new TypeError(`\`allowedExtensions\` entry ${JSON.stringify(ext)} must start with '.' and contain no nested dots, slashes, or whitespace (e.g. '.mp4')`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* One-time (not per-request) warning for the now-deprecated per-kind project
|
|
67
|
+
* hooks, so consumers notice the migration need instead of it being silently
|
|
68
|
+
* aliased away until a future major version removes it outright.
|
|
69
|
+
*/
|
|
70
|
+
export function warnIfUsingDeprecatedProjectHooks(opts) {
|
|
71
|
+
if (opts.validateProjectPayload) {
|
|
72
|
+
process.emitWarning('pulsevault: `validateProjectPayload` is deprecated — use `validatePayload` and branch on `ctx.kind === "project"` instead. Still honored this release.', 'DeprecationWarning');
|
|
73
|
+
}
|
|
74
|
+
if (opts.onProjectUploadComplete) {
|
|
75
|
+
process.emitWarning('pulsevault: `onProjectUploadComplete` is deprecated — use `onUploadComplete` and branch on `ctx.kind === "project"` instead. Still honored this release.', 'DeprecationWarning');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Compose the legacy per-kind hook (if any) with the generic hook, mapped onto the `"project"` kind only. */
|
|
79
|
+
export function composeValidatePayload(generic, legacyProject) {
|
|
80
|
+
if (!legacyProject)
|
|
81
|
+
return generic;
|
|
82
|
+
return async (request, ctx) => {
|
|
83
|
+
if (ctx.kind === 'project') {
|
|
84
|
+
await legacyProject(request, ctx);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (generic)
|
|
88
|
+
await generic(request, ctx);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function composeOnUploadComplete(generic, legacyProject) {
|
|
92
|
+
if (!legacyProject)
|
|
93
|
+
return generic;
|
|
94
|
+
return async (request, ctx) => {
|
|
95
|
+
if (ctx.kind === 'project') {
|
|
96
|
+
await legacyProject(request, ctx);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (generic)
|
|
100
|
+
await generic(request, ctx);
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/lib/options.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,23 +1,45 @@
|
|
|
1
|
-
import { Server } from
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import type
|
|
5
|
-
import type { PulseVaultStorage } from
|
|
1
|
+
import { Server } from '@tus/server';
|
|
2
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
|
+
import type { PulseVaultValidatePayload } from './magic.js';
|
|
4
|
+
import { type PulseVaultRequest, type PulseVaultLogger } from './request.js';
|
|
5
|
+
import type { PulseVaultStorage } from '../storage/types.js';
|
|
6
|
+
import type { UploadKind } from '../storage/types.js';
|
|
6
7
|
/**
|
|
7
|
-
* Context the plugin stashes on each incoming
|
|
8
|
-
*
|
|
9
|
-
* `@tus/server` v2 hooks receive a
|
|
8
|
+
* Context the plugin stashes on each incoming request for the lifetime of a
|
|
9
|
+
* TUS call. Shared with the tus hooks via `AsyncLocalStorage` because
|
|
10
|
+
* `@tus/server` v2 hooks receive a raw `req`/`res`, not the host's request
|
|
11
|
+
* wrapper (Fastify's `FastifyRequest`, Express's `req`, etc.).
|
|
10
12
|
*/
|
|
11
13
|
export type PulseVaultTusContext = {
|
|
12
|
-
request:
|
|
13
|
-
|
|
14
|
+
request: PulseVaultRequest;
|
|
15
|
+
artifactId?: string;
|
|
16
|
+
/** Kind resolved during TUS create; available on the same request only. */
|
|
17
|
+
kind?: UploadKind;
|
|
18
|
+
/** `relatedTo` resolved during TUS create; available on the same request only. */
|
|
19
|
+
relatedTo?: string;
|
|
20
|
+
/** Raw `checksum` metadata value (`<algorithm>:<hex>`), if the client sent one. */
|
|
21
|
+
checksum?: string;
|
|
14
22
|
};
|
|
15
23
|
export declare const pulseVaultTusContext: AsyncLocalStorage<PulseVaultTusContext>;
|
|
16
|
-
export type PulseVaultOnUploadComplete = (request:
|
|
17
|
-
|
|
24
|
+
export type PulseVaultOnUploadComplete = (request: PulseVaultRequest, ctx: {
|
|
25
|
+
artifactId: string;
|
|
26
|
+
kind: UploadKind;
|
|
18
27
|
size: number;
|
|
19
28
|
uploadId: string;
|
|
20
29
|
}) => void | Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Fired at low-frequency, audit-worthy moments — never per chunk — so an
|
|
32
|
+
* operator can wire one hook to get both ops metrics and a compliance audit
|
|
33
|
+
* trail without hand-rolling both from the lower-level hooks.
|
|
34
|
+
*/
|
|
35
|
+
export type PulseVaultArtifactEvent = {
|
|
36
|
+
phase: 'authorize' | 'complete' | 'reject';
|
|
37
|
+
artifactId: string;
|
|
38
|
+
kind: UploadKind;
|
|
39
|
+
size?: number;
|
|
40
|
+
reason?: string;
|
|
41
|
+
};
|
|
42
|
+
export type PulseVaultOnArtifactEvent = (event: PulseVaultArtifactEvent) => void | Promise<void>;
|
|
21
43
|
export type PulsevaultTusOptions = {
|
|
22
44
|
storage: PulseVaultStorage;
|
|
23
45
|
/** Absolute URL path where TUS is mounted, e.g. `/pulsevault/upload`. */
|
|
@@ -25,25 +47,27 @@ export type PulsevaultTusOptions = {
|
|
|
25
47
|
/** Max total upload size in bytes. Use `Infinity` for no cap. */
|
|
26
48
|
maxSize: number;
|
|
27
49
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
50
|
+
* Allowed extensions per kind. Must be pre-normalized to lowercase and
|
|
51
|
+
* include the leading dot.
|
|
30
52
|
*/
|
|
31
|
-
allowedExtensions:
|
|
53
|
+
allowedExtensions: {
|
|
54
|
+
video: readonly string[];
|
|
55
|
+
project: readonly string[];
|
|
56
|
+
captions: readonly string[];
|
|
57
|
+
};
|
|
32
58
|
/**
|
|
33
|
-
* Optional payload-validation hook
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* (
|
|
37
|
-
* scanning, size re-checks — anything that needs the final bytes.
|
|
59
|
+
* Optional payload-validation hook, called for every kind with `ctx.kind`
|
|
60
|
+
* set accordingly. Runs after TUS writes the final byte but before
|
|
61
|
+
* `markReady` and `onUploadComplete`. Throwing causes
|
|
62
|
+
* `storage.remove?.(artifactId)` and a 4xx (default 422).
|
|
38
63
|
*/
|
|
39
64
|
validatePayload?: PulseVaultValidatePayload;
|
|
40
|
-
/**
|
|
41
|
-
* Fired once the final byte of an upload has been written *and* any
|
|
42
|
-
* `validatePayload` has passed. Use this to flip consumer state (DB row,
|
|
43
|
-
* queue job, audit log). Throwing here returns a 500 — bytes are on disk
|
|
44
|
-
* and marked ready, but consumer-side work failed.
|
|
45
|
-
*/
|
|
65
|
+
/** Fired once the final byte has been written and any `validatePayload` has passed, for every kind. */
|
|
46
66
|
onUploadComplete?: PulseVaultOnUploadComplete;
|
|
67
|
+
/** See `PulseVaultOnArtifactEvent`. */
|
|
68
|
+
onArtifactEvent?: PulseVaultOnArtifactEvent;
|
|
69
|
+
/** Logger for internal diagnostics (cleanup failures, etc). Defaults to `console`. */
|
|
70
|
+
logger?: PulseVaultLogger;
|
|
47
71
|
};
|
|
48
72
|
/**
|
|
49
73
|
* Shape `@tus/server` recognizes for sending an error response. We tag both
|
|
@@ -52,5 +76,7 @@ export type PulsevaultTusOptions = {
|
|
|
52
76
|
* right HTTP status.
|
|
53
77
|
*/
|
|
54
78
|
export declare function tusError(status: number, body: string): Error;
|
|
79
|
+
/** Parse the artifactId UUID from a tus upload id of the form `<kind>/<artifactId><ext>`. */
|
|
80
|
+
export declare function artifactIdFromUploadId(id: string): string | undefined;
|
|
55
81
|
export declare function createPulsevaultTusServer(options: PulsevaultTusOptions): Server;
|
|
56
82
|
//# sourceMappingURL=pulsevaultTus.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pulsevaultTus.d.ts","sourceRoot":"","sources":["../../src/lib/pulsevaultTus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"pulsevaultTus.d.ts","sourceRoot":"","sources":["../../src/lib/pulsevaultTus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAiB,MAAM,cAAc,CAAC;AAC5F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,oBAAoB,yCAAgD,CAAC;AAElF,MAAM,MAAM,0BAA0B,GAAG,CACvC,OAAO,EAAE,iBAAiB,EAC1B,GAAG,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,KAC1E,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AACF,MAAM,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEjG,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,iBAAiB,EAAE;QACjB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QACzB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;KAC7B,CAAC;IACF;;;;;OAKG;IACH,eAAe,CAAC,EAAE,yBAAyB,CAAC;IAC5C,uGAAuG;IACvG,gBAAgB,CAAC,EAAE,0BAA0B,CAAC;IAC9C,uCAAuC;IACvC,eAAe,CAAC,EAAE,yBAAyB,CAAC;IAC5C,sFAAsF;IACtF,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,CAM5D;AAED,6FAA6F;AAC7F,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMrE;AAiDD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,oBAAoB,UA2ItE"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Server } from
|
|
2
|
-
import { AsyncLocalStorage } from
|
|
3
|
-
import path from
|
|
4
|
-
import { isUuid } from
|
|
1
|
+
import { Server } from '@tus/server';
|
|
2
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { isUuid } from './uuid.js';
|
|
5
|
+
import { consoleLogger } from './request.js';
|
|
5
6
|
export const pulseVaultTusContext = new AsyncLocalStorage();
|
|
6
7
|
/**
|
|
7
8
|
* Shape `@tus/server` recognizes for sending an error response. We tag both
|
|
@@ -16,10 +17,14 @@ export function tusError(status, body) {
|
|
|
16
17
|
body,
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
|
-
/** Parse the
|
|
20
|
-
function
|
|
21
|
-
const
|
|
22
|
-
|
|
20
|
+
/** Parse the artifactId UUID from a tus upload id of the form `<kind>/<artifactId><ext>`. */
|
|
21
|
+
export function artifactIdFromUploadId(id) {
|
|
22
|
+
const [, nameWithExt] = id.split('/');
|
|
23
|
+
if (!nameWithExt)
|
|
24
|
+
return undefined;
|
|
25
|
+
const ext = path.extname(nameWithExt);
|
|
26
|
+
const candidate = ext ? nameWithExt.slice(0, -ext.length) : nameWithExt;
|
|
27
|
+
return isUuid(candidate) ? candidate : undefined;
|
|
23
28
|
}
|
|
24
29
|
/**
|
|
25
30
|
* Extract a numeric HTTP status from a thrown error, honoring both
|
|
@@ -27,39 +32,69 @@ function videoidFromUploadId(id) {
|
|
|
27
32
|
*/
|
|
28
33
|
function statusCodeOf(err, fallback) {
|
|
29
34
|
const e = err;
|
|
30
|
-
if (typeof e?.statusCode ===
|
|
35
|
+
if (typeof e?.statusCode === 'number')
|
|
31
36
|
return e.statusCode;
|
|
32
|
-
if (typeof e?.status_code ===
|
|
37
|
+
if (typeof e?.status_code === 'number')
|
|
33
38
|
return e.status_code;
|
|
34
39
|
return fallback;
|
|
35
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Extract and normalize the fields `namingFunction` cares about from raw
|
|
43
|
+
* `Upload-Metadata`. Doesn't validate — the caller still checks `artifactId`
|
|
44
|
+
* is a UUID and `filename`'s extension is allowed for `kind`.
|
|
45
|
+
*/
|
|
46
|
+
function parseUploadMetadata(metadata) {
|
|
47
|
+
// Accept `artifactId` plus the legacy `videoid`/`projectid` aliases for
|
|
48
|
+
// back-compat with pre-`artifactId` clients.
|
|
49
|
+
const artifactId = (metadata?.artifactId ??
|
|
50
|
+
metadata?.videoid ??
|
|
51
|
+
metadata?.projectid ??
|
|
52
|
+
'').trim();
|
|
53
|
+
const filename = (metadata?.filename ?? '').trim();
|
|
54
|
+
// `kind` defaults to `"video"` so existing clients that don't send the field continue to work unchanged.
|
|
55
|
+
const rawKind = (metadata?.kind ?? '').trim().toLowerCase();
|
|
56
|
+
const kind = rawKind === 'project' ? 'project' : rawKind === 'captions' ? 'captions' : 'video';
|
|
57
|
+
const rawRelatedTo = (metadata?.relatedTo ?? '').trim();
|
|
58
|
+
const relatedTo = isUuid(rawRelatedTo) ? rawRelatedTo : undefined;
|
|
59
|
+
const checksum = (metadata?.checksum ?? '').trim() || undefined;
|
|
60
|
+
return { artifactId, filename, kind, relatedTo, checksum };
|
|
61
|
+
}
|
|
36
62
|
export function createPulsevaultTusServer(options) {
|
|
37
|
-
const { storage, tusPath, maxSize, allowedExtensions, validatePayload, onUploadComplete, } = options;
|
|
63
|
+
const { storage, tusPath, maxSize, allowedExtensions, validatePayload, onUploadComplete, onArtifactEvent, logger = consoleLogger, } = options;
|
|
38
64
|
return new Server({
|
|
39
65
|
path: tusPath,
|
|
40
66
|
datastore: storage.datastore,
|
|
41
67
|
maxSize,
|
|
42
68
|
namingFunction: async (_req, metadata) => {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
throw tusError(400, "Upload-Metadata must include a valid `videoid` (UUID).\n");
|
|
69
|
+
const { artifactId, filename, kind, relatedTo, checksum } = parseUploadMetadata(metadata);
|
|
70
|
+
if (!isUuid(artifactId)) {
|
|
71
|
+
throw tusError(400, 'Upload-Metadata must include a valid `artifactId` UUID.\n');
|
|
47
72
|
}
|
|
48
73
|
const ext = path.extname(filename).toLowerCase();
|
|
49
|
-
|
|
50
|
-
|
|
74
|
+
const allowed = allowedExtensions[kind];
|
|
75
|
+
if (!ext || !allowed.includes(ext)) {
|
|
76
|
+
throw tusError(400, `Upload-Metadata \`filename\` for kind="${kind}" must end with one of: ${allowed.join(', ')}\n`);
|
|
51
77
|
}
|
|
52
|
-
|
|
78
|
+
// Store kind/relatedTo/checksum in the AsyncLocalStorage context so the
|
|
79
|
+
// authorize hook (already running) and onUploadFinish (same-request
|
|
80
|
+
// uploads) can read them without a storage round-trip.
|
|
81
|
+
const store = pulseVaultTusContext.getStore();
|
|
82
|
+
if (store) {
|
|
83
|
+
store.kind = kind;
|
|
84
|
+
store.relatedTo = relatedTo;
|
|
85
|
+
store.checksum = checksum;
|
|
86
|
+
}
|
|
87
|
+
return storage.reserveUpload({ artifactId, filename, ext, kind, relatedTo, checksum });
|
|
53
88
|
},
|
|
54
89
|
generateUrl(_req, { proto, host, path: tusBasePath, id }) {
|
|
55
|
-
const encoded = Buffer.from(id,
|
|
90
|
+
const encoded = Buffer.from(id, 'utf8').toString('base64url');
|
|
56
91
|
return `${proto}://${host}${tusBasePath}/${encoded}`;
|
|
57
92
|
},
|
|
58
93
|
getFileIdFromRequest(_req, lastPath) {
|
|
59
94
|
if (!lastPath) {
|
|
60
95
|
return;
|
|
61
96
|
}
|
|
62
|
-
return Buffer.from(lastPath,
|
|
97
|
+
return Buffer.from(lastPath, 'base64url').toString('utf8');
|
|
63
98
|
},
|
|
64
99
|
onUploadFinish: async (_req, upload) => {
|
|
65
100
|
// Completion sequence: validate → markReady → consumer hook. Each step
|
|
@@ -71,76 +106,109 @@ export function createPulsevaultTusServer(options) {
|
|
|
71
106
|
// before calling into tus. Bail quietly rather than crash.
|
|
72
107
|
return {};
|
|
73
108
|
}
|
|
74
|
-
const
|
|
75
|
-
if (!
|
|
109
|
+
const artifactId = artifactIdFromUploadId(upload.id);
|
|
110
|
+
if (!artifactId) {
|
|
76
111
|
return {};
|
|
77
112
|
}
|
|
78
113
|
const size = upload.size ?? 0;
|
|
79
114
|
const uploadId = upload.id;
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
115
|
+
// Resolve kind/checksum: prefer the context value (set during the same
|
|
116
|
+
// request's namingFunction for single-request uploads), fall back to a
|
|
117
|
+
// storage lookup (cheap in-memory cache hit) for chunked uploads, or —
|
|
118
|
+
// just as commonly — a single-PATCH upload sent as two separate HTTP
|
|
119
|
+
// requests (create, then patch), where `onUploadFinish` runs on the
|
|
120
|
+
// PATCH request's own fresh context, not the one `namingFunction`
|
|
121
|
+
// populated during the earlier POST.
|
|
122
|
+
const kind = store.kind ?? (await resolveKind(storage, artifactId));
|
|
123
|
+
const checksum = store.checksum ?? (await resolveChecksum(storage, artifactId));
|
|
124
|
+
// 1. Validate payload (magic bytes, checksum, virus scan, etc.). If
|
|
125
|
+
// this throws we wipe the bytes from storage — the client gets a
|
|
126
|
+
// 4xx, the sidecar is gone, and they can safely retry with a
|
|
127
|
+
// corrected file. The same hook runs for every kind; consumers that
|
|
128
|
+
// want kind-specific behavior branch on `ctx.kind` themselves.
|
|
83
129
|
if (validatePayload) {
|
|
84
130
|
try {
|
|
85
131
|
await validatePayload(store.request, {
|
|
86
|
-
|
|
132
|
+
artifactId,
|
|
87
133
|
size,
|
|
88
134
|
uploadId,
|
|
89
|
-
localPath: await resolveLocalPath(storage,
|
|
135
|
+
localPath: await resolveLocalPath(storage, artifactId),
|
|
136
|
+
...(checksum ? { checksum } : {}),
|
|
137
|
+
kind,
|
|
90
138
|
});
|
|
91
139
|
}
|
|
92
140
|
catch (err) {
|
|
93
141
|
const status = statusCodeOf(err, 422);
|
|
94
|
-
const message = err instanceof Error ? err.message :
|
|
142
|
+
const message = err instanceof Error ? err.message : 'Payload validation failed';
|
|
95
143
|
try {
|
|
96
|
-
await storage.remove?.(
|
|
144
|
+
await storage.remove?.(artifactId);
|
|
97
145
|
}
|
|
98
146
|
catch (rmErr) {
|
|
99
|
-
|
|
147
|
+
logger.error({ err: rmErr, artifactId }, 'pulsevault failed to remove rejected upload');
|
|
100
148
|
}
|
|
149
|
+
await onArtifactEvent?.({ phase: 'reject', artifactId, kind, size, reason: message });
|
|
101
150
|
throw tusError(status, `${message}\n`);
|
|
102
151
|
}
|
|
103
152
|
}
|
|
104
153
|
// 2. Flip the sidecar to "ready" so `resolve` will serve the bytes.
|
|
105
154
|
// Done *before* the consumer hook so a downstream service that
|
|
106
|
-
// reacts to `onUploadComplete` can immediately GET the
|
|
155
|
+
// reacts to `onUploadComplete` can immediately GET the artifact.
|
|
107
156
|
try {
|
|
108
|
-
await storage.markReady?.(
|
|
157
|
+
await storage.markReady?.(artifactId);
|
|
109
158
|
}
|
|
110
159
|
catch (err) {
|
|
111
|
-
const message = err instanceof Error ? err.message :
|
|
160
|
+
const message = err instanceof Error ? err.message : 'markReady failed';
|
|
112
161
|
throw tusError(500, `${message}\n`);
|
|
113
162
|
}
|
|
114
163
|
// 3. Consumer hook — business logic (DB writes, queue jobs).
|
|
115
164
|
if (onUploadComplete) {
|
|
116
165
|
try {
|
|
117
|
-
await onUploadComplete(store.request, {
|
|
166
|
+
await onUploadComplete(store.request, { artifactId, kind, size, uploadId });
|
|
118
167
|
}
|
|
119
168
|
catch (err) {
|
|
120
169
|
// Propagate as a tus error so the client sees a non-2xx and can
|
|
121
170
|
// distinguish "bytes stored but completion hook failed" from
|
|
122
|
-
// success. The
|
|
171
|
+
// success. The artifact is marked ready at this point — consumers
|
|
123
172
|
// who want "all-or-nothing" should `storage.remove` before
|
|
124
173
|
// throwing.
|
|
125
|
-
const message = err instanceof Error ? err.message :
|
|
174
|
+
const message = err instanceof Error ? err.message : 'onUploadComplete failed';
|
|
126
175
|
throw tusError(500, `${message}\n`);
|
|
127
176
|
}
|
|
128
177
|
}
|
|
178
|
+
await onArtifactEvent?.({ phase: 'complete', artifactId, kind, size });
|
|
129
179
|
return {};
|
|
130
180
|
},
|
|
131
181
|
});
|
|
132
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Resolve the artifact kind from storage for a known artifactId. Used by
|
|
185
|
+
* `onUploadFinish` for chunked uploads where the kind is not in the current
|
|
186
|
+
* request's context. Defaults to `"video"` for adapters that don't implement
|
|
187
|
+
* `getKind` or when the artifactId is not found.
|
|
188
|
+
*/
|
|
189
|
+
async function resolveKind(storage, artifactId) {
|
|
190
|
+
const result = await storage.getKind?.(artifactId);
|
|
191
|
+
return result ?? 'video';
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Resolve the `checksum` metadata from storage for a known artifactId. Same
|
|
195
|
+
* rationale as `resolveKind` — `namingFunction`'s in-memory context doesn't
|
|
196
|
+
* survive past the request it ran on, so completion (which may run on a
|
|
197
|
+
* later, separate request) needs a storage-backed fallback.
|
|
198
|
+
*/
|
|
199
|
+
async function resolveChecksum(storage, artifactId) {
|
|
200
|
+
const result = await storage.getChecksum?.(artifactId);
|
|
201
|
+
return result ?? undefined;
|
|
202
|
+
}
|
|
133
203
|
/**
|
|
134
204
|
* If the adapter exposes `getLocalPath` (the built-in local adapter does),
|
|
135
|
-
* resolve the
|
|
205
|
+
* resolve the artifactId to an absolute disk path for `validatePayload`. For
|
|
136
206
|
* other adapters, returns `null` and the validator is expected to fetch
|
|
137
207
|
* bytes through whatever API it knows about.
|
|
138
208
|
*/
|
|
139
|
-
async function resolveLocalPath(storage,
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const result = await candidate(videoid);
|
|
144
|
-
return typeof result === "string" ? result : null;
|
|
209
|
+
async function resolveLocalPath(storage, artifactId) {
|
|
210
|
+
const adapter = storage;
|
|
211
|
+
const result = await adapter.getLocalPath?.(artifactId);
|
|
212
|
+
return typeof result === 'string' ? result : null;
|
|
145
213
|
}
|
|
146
214
|
//# sourceMappingURL=pulsevaultTus.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pulsevaultTus.js","sourceRoot":"","sources":["../../src/lib/pulsevaultTus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"pulsevaultTus.js","sourceRoot":"","sources":["../../src/lib/pulsevaultTus.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
2
|
+
/**
|
|
3
|
+
* Structural request shape the plugin's internals and hooks rely on.
|
|
4
|
+
* `FastifyRequest`, Express's `req`, and a raw `http.IncomingMessage` all
|
|
5
|
+
* satisfy this already (they all expose `.headers`), so the same hook
|
|
6
|
+
* implementations work unmodified under the Fastify plugin and the
|
|
7
|
+
* framework-agnostic core.
|
|
8
|
+
*/
|
|
9
|
+
export type PulseVaultRequest = {
|
|
10
|
+
headers: IncomingHttpHeaders;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Structural logger shape used for the plugin's own internal diagnostics
|
|
14
|
+
* (authorize rejections, tus handler failures, cleanup errors) — satisfied
|
|
15
|
+
* by Pino (Fastify's `request.log`) and by the `consoleLogger` fallback
|
|
16
|
+
* below, so non-Fastify hosts don't need to bring their own logger.
|
|
17
|
+
*/
|
|
18
|
+
export type PulseVaultLogger = {
|
|
19
|
+
info(obj: unknown, msg?: string): void;
|
|
20
|
+
error(obj: unknown, msg?: string): void;
|
|
21
|
+
};
|
|
22
|
+
/** Default logger for hosts that don't supply one. */
|
|
23
|
+
export declare const consoleLogger: PulseVaultLogger;
|
|
24
|
+
//# sourceMappingURL=request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/lib/request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,mBAAmB,CAAC;CAC9B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,aAAa,EAAE,gBAS3B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Default logger for hosts that don't supply one. */
|
|
2
|
+
export const consoleLogger = {
|
|
3
|
+
info(obj, msg) {
|
|
4
|
+
if (msg)
|
|
5
|
+
console.info(msg, obj);
|
|
6
|
+
else
|
|
7
|
+
console.info(obj);
|
|
8
|
+
},
|
|
9
|
+
error(obj, msg) {
|
|
10
|
+
if (msg)
|
|
11
|
+
console.error(msg, obj);
|
|
12
|
+
else
|
|
13
|
+
console.error(obj);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/lib/request.ts"],"names":[],"mappings":"AAwBA,sDAAsD;AACtD,MAAM,CAAC,MAAM,aAAa,GAAqB;IAC7C,IAAI,CAAC,GAAG,EAAE,GAAG;QACX,IAAI,GAAG;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;;YAC3B,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IACD,KAAK,CAAC,GAAG,EAAE,GAAG;QACZ,IAAI,GAAG;YAAE,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;;YAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;CACF,CAAC"}
|
package/dist/lib/uuid.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../../src/lib/uuid.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../../src/lib/uuid.ts"],"names":[],"mappings":"AAEA,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEtD"}
|
package/dist/lib/uuid.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2
2
|
export function isUuid(value) {
|
|
3
|
-
return typeof value ===
|
|
3
|
+
return typeof value === 'string' && UUID_REGEX.test(value);
|
|
4
4
|
}
|
|
5
5
|
//# sourceMappingURL=uuid.js.map
|
package/dist/lib/uuid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../../src/lib/uuid.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../../src/lib/uuid.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAAG,iEAAiE,CAAC;AAErF,MAAM,UAAU,MAAM,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC7D,CAAC"}
|