@mieweb/pulsevault 0.1.0 → 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.
- package/CHANGELOG.md +82 -8
- package/OPERATIONS.md +4 -4
- package/PROTOCOL.md +60 -27
- package/README.md +42 -26
- package/dist/app.d.ts +5 -4
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +1 -1
- package/dist/app.js.map +1 -1
- package/dist/core.d.ts +2 -2
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +141 -78
- package/dist/core.js.map +1 -1
- package/dist/lib/authorize.d.ts +1 -1
- package/dist/lib/authorize.d.ts.map +1 -1
- package/dist/lib/capability-token.d.ts +3 -3
- package/dist/lib/capability-token.d.ts.map +1 -1
- package/dist/lib/capability-token.js +14 -6
- package/dist/lib/capability-token.js.map +1 -1
- package/dist/lib/checksum.d.ts.map +1 -1
- package/dist/lib/checksum.js +4 -1
- package/dist/lib/checksum.js.map +1 -1
- package/dist/lib/deeplinks.d.ts +2 -2
- package/dist/lib/deeplinks.d.ts.map +1 -1
- package/dist/lib/deeplinks.js +2 -2
- package/dist/lib/deeplinks.js.map +1 -1
- package/dist/lib/errors.d.ts +7 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +18 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/options.d.ts +3 -1
- package/dist/lib/options.d.ts.map +1 -1
- package/dist/lib/options.js +12 -4
- package/dist/lib/options.js.map +1 -1
- package/dist/lib/pulsevaultTus.d.ts +1 -0
- package/dist/lib/pulsevaultTus.d.ts.map +1 -1
- package/dist/lib/pulsevaultTus.js +72 -21
- package/dist/lib/pulsevaultTus.js.map +1 -1
- package/dist/lib/request.d.ts +2 -0
- package/dist/lib/request.d.ts.map +1 -1
- package/dist/lib/request.js +6 -0
- package/dist/lib/request.js.map +1 -1
- package/dist/routes/pulsevault.d.ts +1 -1
- package/dist/routes/pulsevault.d.ts.map +1 -1
- package/dist/routes/pulsevault.js +4 -3
- package/dist/routes/pulsevault.js.map +1 -1
- package/dist/storage/local.d.ts.map +1 -1
- package/dist/storage/local.js +46 -9
- package/dist/storage/local.js.map +1 -1
- package/dist/storage/s3.d.ts.map +1 -1
- package/dist/storage/s3.js +10 -4
- package/dist/storage/s3.js.map +1 -1
- package/dist/storage/types.d.ts +14 -3
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +13 -1
- package/dist/storage/types.js.map +1 -1
- package/package.json +1 -7
package/CHANGELOG.md
CHANGED
|
@@ -47,6 +47,23 @@ if you've evaluated against an intermediate build.
|
|
|
47
47
|
requirement on server implementations generally, not just this package).
|
|
48
48
|
- Both are covered by new regression tests in `test/plugin.test.mjs` that
|
|
49
49
|
fail against the pre-fix code and pass against the fix.
|
|
50
|
+
- **Removed `s3rver`** (unmaintained) from the dev/test toolchain, clearing
|
|
51
|
+
its open `npm audit` findings: its dependency chain carried high-severity
|
|
52
|
+
advisories (`busboy`/`dicer`) and forced pinning `fast-xml-parser` via an
|
|
53
|
+
`overrides` entry plus a hand-written API-compat shim. The S3 suite now runs
|
|
54
|
+
against `test/mock-s3.mjs`, a zero-dependency in-memory S3 double
|
|
55
|
+
implementing exactly the eleven operations the code under test uses. Unlike
|
|
56
|
+
s3rver it enforces `If-None-Match: "*"` (412), so the adapter's atomic
|
|
57
|
+
reserve collision guard is now genuinely exercised — a concurrent-reserve
|
|
58
|
+
test that was impossible against s3rver has been added. The `ListParts` shim
|
|
59
|
+
(s3rver never implemented it) is gone too.
|
|
60
|
+
- **5xx responses no longer echo internal error messages.** A `markReady`,
|
|
61
|
+
`onUploadComplete`, or 5xx-class `validatePayload` failure now logs the real
|
|
62
|
+
error server-side and returns a generic body, so a consumer's DB/storage error
|
|
63
|
+
text (schema names, paths, infra detail) can't reach the uploading client. 4xx
|
|
64
|
+
validation rejections (e.g. checksum mismatch) keep their descriptive messages.
|
|
65
|
+
- **Storage subdirectories are created with mode `0o750`** so the upload tree
|
|
66
|
+
isn't world-readable under a permissive umask.
|
|
50
67
|
|
|
51
68
|
### Breaking
|
|
52
69
|
|
|
@@ -77,12 +94,15 @@ if you've evaluated against an intermediate build.
|
|
|
77
94
|
|
|
78
95
|
### Added
|
|
79
96
|
|
|
80
|
-
- **`kind: "captions"`** artifact type (default extension `.
|
|
97
|
+
- **`kind: "captions"`** artifact type (default extension `.vtt`), running
|
|
81
98
|
through the same generic `validatePayload`/`onUploadComplete` hooks as
|
|
82
|
-
every other kind.
|
|
99
|
+
every other kind. WebVTT carries word-level inline cue timestamps
|
|
100
|
+
(`<00:00:01.500>word`) for karaoke rendering, and both storage adapters
|
|
101
|
+
serve `.vtt` artifacts with the `text/vtt` content type.
|
|
83
102
|
- **`relatedTo`** — an optional `Upload-Metadata` key (and matching
|
|
84
103
|
`ReserveUploadParams`/storage field) linking one artifact to another (e.g.
|
|
85
|
-
a video's captions, or a
|
|
104
|
+
a merged video's captions/beat-manifest/thumbnail, or a clip belonging to a
|
|
105
|
+
segment ordering manifest's session).
|
|
86
106
|
Storage adapters expose it via the new optional `getRelatedTo` method.
|
|
87
107
|
- **`checksum`** — an optional `Upload-Metadata` key (`<algorithm>:<hex
|
|
88
108
|
digest>`) verified post-upload via the new `createChecksumValidator`
|
|
@@ -102,14 +122,21 @@ if you've evaluated against an intermediate build.
|
|
|
102
122
|
`uploadUnit`, `kinds`, `allowedExtensions`, `maxUploadSize`, and supported
|
|
103
123
|
checksum algorithms. Lets independently-versioned deployments and clients
|
|
104
124
|
detect compatibility before pairing.
|
|
105
|
-
- **`uploadUnit` plugin option** (`"
|
|
125
|
+
- **`uploadUnit` plugin option** (`"segment" | "merged"`, default `"segment"`) —
|
|
106
126
|
purely advertised via `/capabilities`; the operator declares which upload
|
|
107
127
|
strategy this deployment expects, the client branches on it. Not enforced
|
|
108
|
-
by the plugin.
|
|
128
|
+
by the plugin. `"segment"` uploads each recorded clip plus an ordering
|
|
129
|
+
manifest; `"merged"` uploads one pre-merged video plus its captions, a
|
|
130
|
+
beat-timecode manifest, and a thumbnail. (A "beat" is a timecode range on
|
|
131
|
+
the merged timeline, not an upload unit.)
|
|
132
|
+
- **`kind: "thumbnail"`** artifact type (default extensions `.jpg`/`.jpeg`/
|
|
133
|
+
`.png`) — the pulse poster frame uploaded alongside a merged video, running
|
|
134
|
+
through the same generic `validatePayload`/`onUploadComplete` hooks and a
|
|
135
|
+
`thumbnail/` storage subdir like every other kind.
|
|
109
136
|
- **`buildUploadLink({ uploadUnit })`** (PROTOCOL.md §3, §8) — optional
|
|
110
137
|
per-session override of the deployment-wide `uploadUnit`, carried on the
|
|
111
138
|
pairing link itself instead of only `/capabilities`. Lets an operator run
|
|
112
|
-
`"
|
|
139
|
+
`"segment"` and `"merged"` sessions concurrently (staged rollout, A/B test,
|
|
113
140
|
per-tenant policy) without racing a client's separate `/capabilities` fetch
|
|
114
141
|
against a single, deployment-wide value. Fully backward compatible: omit it
|
|
115
142
|
and a client falls back to `/capabilities` exactly as before.
|
|
@@ -144,8 +171,55 @@ if you've evaluated against an intermediate build.
|
|
|
144
171
|
counterpart to `readAll`, now used internally by
|
|
145
172
|
`createS3ChecksumValidator` (see "Fixed" below).
|
|
146
173
|
|
|
174
|
+
### Changed
|
|
175
|
+
|
|
176
|
+
- Replaced `examples/rn-demo` with two focused Fastify examples:
|
|
177
|
+
`examples/fastify-demo` (the smallest runnable server — no auth, local
|
|
178
|
+
storage, QR pairing; start here) and `examples/fastify-auth-demo`
|
|
179
|
+
(production-shaped reference — capability tokens always on, failing fast at
|
|
180
|
+
boot without `PULSEVAULT_SECRET`, pulse-session grouping via `relatedTo`,
|
|
181
|
+
WebVTT captions, Swagger UI, artifact-event feed). Both pairing pages
|
|
182
|
+
are React (pinned ESM CDN builds, no bundler). Both examples are
|
|
183
|
+
local-storage only; S3 wiring is documented in the README's S3 section
|
|
184
|
+
rather than demonstrated in an example. `npm run e2e` now drives
|
|
185
|
+
`fastify-auth-demo` and additionally asserts the fail-fast boot contract.
|
|
186
|
+
- `examples/fastify-auth-demo` gained a [Better Auth](https://better-auth.com)
|
|
187
|
+
dashboard layer: the human-facing routes (`/deeplinks`, `/pulses`,
|
|
188
|
+
`/events`, `/reserve`) require an email+password session, while the vault
|
|
189
|
+
routes the Pulse app talks to stay capability-token authorized — two auth
|
|
190
|
+
systems for two audiences, per PROTOCOL.md §5. The demo is Postgres-only,
|
|
191
|
+
run through Docker Compose (`compose.yaml` + `Dockerfile`; `npm run db:up`
|
|
192
|
+
+ `npm start` for bare-metal dev against the same db), with one Prisma
|
|
193
|
+
schema (`prisma/schema.prisma`, migrations committed and applied by
|
|
194
|
+
`npm start`) holding both the auth tables and an **artifact index**:
|
|
195
|
+
`onUploadComplete` writes each finished upload once, authorized deletes
|
|
196
|
+
prune it, a boot-time reconcile keeps it honest against the filesystem
|
|
197
|
+
sidecars (still the source of truth), and `/pulses` becomes one indexed
|
|
198
|
+
query instead of a per-request sidecar crawl. `npm run e2e` provisions the
|
|
199
|
+
compose db, applies migrations, and covers the 401 gate plus the
|
|
200
|
+
sign-up/sign-in flow.
|
|
201
|
+
|
|
147
202
|
### Fixed
|
|
148
203
|
|
|
204
|
+
- **Artifact `GET`/`DELETE` handlers now fail closed.** They run on a hijacked
|
|
205
|
+
socket, so a thrown storage error (S3 unreachable, disk fault) previously left
|
|
206
|
+
the socket hung until timeout; each now emits a `500` (or destroys the socket
|
|
207
|
+
if headers are already sent), mirroring the TUS handler. `GET` streams also
|
|
208
|
+
catch a mid-stream read error instead of letting an unhandled `'error'` crash
|
|
209
|
+
the process, and destroy the source stream on client disconnect so file
|
|
210
|
+
descriptors aren't leaked. Covered by `test/fail-closed.test.mjs`.
|
|
211
|
+
- A consumer error carrying an out-of-range `statusCode` (e.g. `42`) now degrades
|
|
212
|
+
to the handler's fallback status instead of crashing `res.writeHead`; a
|
|
213
|
+
malformed request-target that `URL` can't parse returns `400` instead of an
|
|
214
|
+
uncaught throw.
|
|
215
|
+
- Checksum verification requested against an adapter with no local path now
|
|
216
|
+
reports `500` (server misconfiguration) instead of `422` (which wrongly
|
|
217
|
+
implied the client's file was rejected).
|
|
218
|
+
- `verifyCapabilityToken` no longer throws on a token whose payload is valid
|
|
219
|
+
JSON but not an object (e.g. the literal `null`) — it returns `null` like
|
|
220
|
+
every other failure, per its single-failure-shape contract.
|
|
221
|
+
- Bearer-token extraction accepts the auth scheme case-insensitively per
|
|
222
|
+
RFC 7235 (`bearer`/`BEARER`), not only the canonical `Bearer `.
|
|
149
223
|
- `maxUploadSize` enforcement and the in-progress-upload 404 behavior now
|
|
150
224
|
have explicit test coverage proving bytes are rejected/hidden at the right
|
|
151
225
|
point, not just implied by the implementation.
|
|
@@ -175,8 +249,8 @@ if you've evaluated against an intermediate build.
|
|
|
175
249
|
- **Silent TOCTOU on S3-compatible backends without `IfNoneMatch` support.**
|
|
176
250
|
`reserveUpload`'s conditional-write collision guard falls back to a
|
|
177
251
|
weaker check-then-write on backends that reject `IfNoneMatch` (some
|
|
178
|
-
S3-compatible stores
|
|
179
|
-
|
|
252
|
+
S3-compatible stores) — this reopens the race between concurrent/retried
|
|
253
|
+
creates
|
|
180
254
|
for the same artifactId. There's no way to close it without an external
|
|
181
255
|
lock, so this is now at least surfaced: a one-time `console.warn` fires
|
|
182
256
|
per process the first time a deployment falls into this degraded mode.
|
package/OPERATIONS.md
CHANGED
|
@@ -66,8 +66,8 @@ you have a specific reason to use local disk.
|
|
|
66
66
|
`reserveUpload`'s collision guard normally uses `PutObjectCommand`'s
|
|
67
67
|
`IfNoneMatch: "*"` to atomically reject a second create for an artifactId
|
|
68
68
|
that already has an upload. Some S3-compatible backends (older/less-complete
|
|
69
|
-
implementations
|
|
70
|
-
|
|
69
|
+
implementations) don't support conditional writes and reject that header
|
|
70
|
+
outright.
|
|
71
71
|
On those backends, `reserveUpload` falls back to a weaker check-then-write —
|
|
72
72
|
functionally the same guard, but with the original race reopened: two
|
|
73
73
|
truly concurrent (or retried) creates for the same artifactId can both pass
|
|
@@ -153,8 +153,8 @@ upload routes.
|
|
|
153
153
|
## Backup and restore (local storage)
|
|
154
154
|
|
|
155
155
|
The entire state of the local adapter lives under `workspaceDir`:
|
|
156
|
-
`.pulsevault/` (sidecars), `video/`, `project/`, `captions/`
|
|
157
|
-
up as a normal filesystem tree — there's no separate database to keep in
|
|
156
|
+
`.pulsevault/` (sidecars), `video/`, `project/`, `captions/`, `thumbnail/`
|
|
157
|
+
(bytes). Back it up as a normal filesystem tree — there's no separate database to keep in
|
|
158
158
|
sync. To restore, copy the tree back and restart; in-progress uploads at
|
|
159
159
|
backup time will resume correctly via the normal TUS `HEAD`-then-resume path
|
|
160
160
|
once the client retries, or will simply sit as abandoned partial uploads
|
package/PROTOCOL.md
CHANGED
|
@@ -37,9 +37,9 @@ The response body MUST be a JSON object with at least the following fields:
|
|
|
37
37
|
"protocolVersion": 1,
|
|
38
38
|
"minSupportedVersion": 1,
|
|
39
39
|
"maxSupportedVersion": 1,
|
|
40
|
-
"uploadUnit": "
|
|
41
|
-
"kinds": ["video", "project", "captions"],
|
|
42
|
-
"allowedExtensions": { "video": [".mp4"], "project": [".pulse", ".zip"], "captions": [".
|
|
40
|
+
"uploadUnit": "segment",
|
|
41
|
+
"kinds": ["video", "project", "captions", "thumbnail"],
|
|
42
|
+
"allowedExtensions": { "video": [".mp4"], "project": [".pulse", ".zip"], "captions": [".vtt"], "thumbnail": [".jpg", ".jpeg", ".png"] },
|
|
43
43
|
"maxUploadSize": 5368709120,
|
|
44
44
|
"checksum": { "algorithms": ["sha256", "sha1", "md5"] }
|
|
45
45
|
}
|
|
@@ -52,9 +52,10 @@ The response body MUST be a JSON object with at least the following fields:
|
|
|
52
52
|
SHOULD refuse to pair if its own version falls outside this range, and
|
|
53
53
|
SHOULD show the user an actionable message ("update the app" /
|
|
54
54
|
"this server needs an update") rather than a generic error.
|
|
55
|
-
- `uploadUnit` (`"
|
|
55
|
+
- `uploadUnit` (`"segment"` or `"merged"`, REQUIRED): which upload strategy this
|
|
56
56
|
deployment expects (§8). The client MUST read this before doing any
|
|
57
|
-
merge/upload work and branch accordingly.
|
|
57
|
+
merge/upload work and branch accordingly. (Note: "beat" no longer refers to
|
|
58
|
+
an upload unit — it now names a timecode range on the merged timeline; see §8.)
|
|
58
59
|
- `kinds` (array of string, REQUIRED): artifact kinds this server accepts.
|
|
59
60
|
- `allowedExtensions` (object, REQUIRED): allowed file extensions per kind.
|
|
60
61
|
- `maxUploadSize` (integer, REQUIRED): maximum artifact size in bytes.
|
|
@@ -74,7 +75,7 @@ the integer from `protocolVersion`.
|
|
|
74
75
|
A server presents a pairing link or QR code of the form:
|
|
75
76
|
|
|
76
77
|
```
|
|
77
|
-
pulsecam://?v=1&artifactId=<uuid>&server=<origin>&token=<opaque>&uploadUnit=<
|
|
78
|
+
pulsecam://?v=1&artifactId=<uuid>&server=<origin>&token=<opaque>&uploadUnit=<segment|merged>
|
|
78
79
|
```
|
|
79
80
|
|
|
80
81
|
- `v` (REQUIRED): the deep-link schema version. A client MUST refuse and
|
|
@@ -96,14 +97,14 @@ pulsecam://?v=1&artifactId=<uuid>&server=<origin>&token=<opaque>&uploadUnit=<bea
|
|
|
96
97
|
`artifactId` (or a session it anchors, §5.4), not a standing general
|
|
97
98
|
credential — see §5.4 for the recommended (but not required) capability-
|
|
98
99
|
token shape.
|
|
99
|
-
- `uploadUnit` (OPTIONAL, `"
|
|
100
|
+
- `uploadUnit` (OPTIONAL, `"segment"` or `"merged"`): per-session override of the
|
|
100
101
|
deployment-wide value reported by `GET /capabilities` (§2, §8). When
|
|
101
102
|
present, a client MUST use this value for the session anchored to this
|
|
102
103
|
link instead of whatever `/capabilities` currently reports, and MUST NOT
|
|
103
104
|
perform a separate `/capabilities` fetch just to decide merge/upload
|
|
104
105
|
strategy for it. When absent, a client MUST fall back to `/capabilities`
|
|
105
106
|
exactly as before this field existed — an operator that never sets it sees
|
|
106
|
-
no change in client behavior. This lets one deployment run "
|
|
107
|
+
no change in client behavior. This lets one deployment run "segment" and
|
|
107
108
|
"merged" sessions concurrently (e.g. a staged rollout) without racing a
|
|
108
109
|
single, deployment-wide `/capabilities` value against whichever moment a
|
|
109
110
|
client happened to fetch it.
|
|
@@ -138,7 +139,7 @@ pairs) MUST be parsed for at least:
|
|
|
138
139
|
|---|---|---|
|
|
139
140
|
| `artifactId` | Yes (or a legacy `videoid`/`projectid` alias) | UUID for this artifact. |
|
|
140
141
|
| `filename` | Yes | Original filename; extension validated against `kind`'s allowed list. |
|
|
141
|
-
| `kind` | No, defaults to `video` | `video`, `project`, or `
|
|
142
|
+
| `kind` | No, defaults to `video` | `video`, `project`, `captions`, or `thumbnail`. |
|
|
142
143
|
| `relatedTo` | No | UUID of another artifact this one belongs to (§8). |
|
|
143
144
|
| `checksum` | No | `<algorithm>:<hex digest>` of the finished file (§6.3). |
|
|
144
145
|
|
|
@@ -239,8 +240,9 @@ expired token. `issuer` prevents a token minted by one deployment from being
|
|
|
239
240
|
replayed against a different one that happens to share a secret. A token MAY
|
|
240
241
|
authorize an artifact other than the one it names if that artifact declares
|
|
241
242
|
the token's `artifactId` as its `relatedTo` (§8) — this lets one token cover
|
|
242
|
-
an entire upload session (a video
|
|
243
|
-
|
|
243
|
+
an entire upload session (a merged video plus its captions, beat manifest and
|
|
244
|
+
thumbnail, or every clip plus the ordering manifest under
|
|
245
|
+
`uploadUnit: "segment"`) rather than requiring one token per artifact.
|
|
244
246
|
|
|
245
247
|
This shape is exactly what `@mieweb/pulsevault`'s `issueCapabilityToken`/
|
|
246
248
|
`verifyCapabilityToken`/`createCapabilityAuthorize` implement, but any server
|
|
@@ -282,22 +284,53 @@ clear, specific message rather than a generic failure.
|
|
|
282
284
|
|
|
283
285
|
## 8. Artifact relationships (`relatedTo`) and `uploadUnit`
|
|
284
286
|
|
|
285
|
-
A pulse (a short composed of one or more
|
|
286
|
-
pre-merged video (`uploadUnit: "merged"`) or as individual
|
|
287
|
-
plus
|
|
288
|
-
`/capabilities` (§2), optionally overridden per
|
|
289
|
-
link's `uploadUnit` param (§3); this document does not
|
|
290
|
-
other.
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
287
|
+
A pulse (a short composed of one or more recorded **segments**) MAY be uploaded
|
|
288
|
+
as a single pre-merged video (`uploadUnit: "merged"`) or as individual
|
|
289
|
+
per-segment artifacts plus an ordering manifest (`uploadUnit: "segment"`) — the
|
|
290
|
+
operator declares which via `/capabilities` (§2), optionally overridden per
|
|
291
|
+
session via the pairing link's `uploadUnit` param (§3); this document does not
|
|
292
|
+
prefer one over the other.
|
|
293
|
+
|
|
294
|
+
> **Terminology:** a **segment** is a recorded clip (the source unit). A
|
|
295
|
+
> **beat** is a *timecode range on the merged timeline* — one recorded
|
|
296
|
+
> segment's start/end within the merged video — carried only in the merged
|
|
297
|
+
> mode's beat manifest below. (Earlier revisions used "beat" for what is now
|
|
298
|
+
> called the `segment` upload unit; that meaning is retired.)
|
|
299
|
+
|
|
300
|
+
Under `uploadUnit: "segment"`, a client uploads each segment under its own
|
|
301
|
+
`artifactId`, plus exactly one **ordering manifest** artifact (`kind:
|
|
302
|
+
"project"`, e.g. `<draftId>-segments.pulse`, a JSON document listing the
|
|
303
|
+
ordered segment `artifactId`s). Segmented mode carries **no** captions and
|
|
304
|
+
**no** thumbnail.
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{ "version": 1, "segments": [ { "artifactId": "<uuid>", "order": 0 } ] }
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Under `uploadUnit: "merged"`, a client uploads one pre-merged video as the
|
|
311
|
+
session anchor, plus (all as related artifacts):
|
|
312
|
+
|
|
313
|
+
- **captions** (`kind: "captions"`, `<draftId>.vtt`) — WebVTT for the whole
|
|
314
|
+
merged video, OPTIONAL (absent when the video has no speech or no on-device
|
|
315
|
+
model was available); MAY carry word-level inline cue timestamps like
|
|
316
|
+
`<00:00:01.500>word` for karaoke rendering;
|
|
317
|
+
- a **beat manifest** (`kind: "project"`, e.g. `<draftId>-beats.pulse`) giving
|
|
318
|
+
each recorded segment's precise `startMs`/`endMs` on the merged timeline,
|
|
319
|
+
contiguous and summing to the true merged `durationMs` (groundwork for
|
|
320
|
+
keyframe-aligned deep links / HLS);
|
|
321
|
+
- a **thumbnail** (`kind: "thumbnail"`, `<draftId>.jpg`) — the pulse's poster
|
|
322
|
+
frame.
|
|
323
|
+
|
|
324
|
+
```json
|
|
325
|
+
{ "version": 1, "type": "beat-manifest", "durationMs": 47320,
|
|
326
|
+
"beats": [ { "segmentId": "<local id>", "order": 0, "startMs": 0, "endMs": 4210 } ] }
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Every non-anchor artifact in either session SHOULD declare `relatedTo` pointing
|
|
330
|
+
at the session's anchor `artifactId` (the one named in the pairing link) so a
|
|
331
|
+
single capability token can authorize the whole session (§5.4).
|
|
332
|
+
|
|
333
|
+
The relational graph of replies between segments/pulses (who replied to what,
|
|
301
334
|
rendering a thread) is explicitly **out of scope** for this document. That
|
|
302
335
|
graph is a query/relational concern for the operator's own systems, built
|
|
303
336
|
from `relatedTo` and whatever additional metadata the operator chooses to
|
package/README.md
CHANGED
|
@@ -173,14 +173,28 @@ diagnostics — the Fastify plugin always uses `request.log` for these
|
|
|
173
173
|
automatically, but a non-Fastify host has no equivalent to infer one from,
|
|
174
174
|
so it falls back to `console` when omitted.
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
176
|
+
Four runnable example servers live under [`examples/`](examples):
|
|
177
|
+
|
|
178
|
+
- [`examples/fastify-demo`](examples/fastify-demo) — the smallest runnable
|
|
179
|
+
server: Fastify plugin mount, QR pairing, flat upload listing, no auth,
|
|
180
|
+
local storage. Start here.
|
|
181
|
+
- [`examples/fastify-auth-demo`](examples/fastify-auth-demo) —
|
|
182
|
+
production-shaped Fastify reference, run through Docker Compose (server +
|
|
183
|
+
Postgres, one `docker compose up --build`): capability tokens always on
|
|
184
|
+
(fails fast at boot without `PULSEVAULT_SECRET`), a [Better
|
|
185
|
+
Auth](https://better-auth.com)-protected React dashboard (email+password),
|
|
186
|
+
a Prisma schema holding both the auth tables and an artifact index
|
|
187
|
+
(`onUploadComplete` writes each finished upload once; `/pulses` is one
|
|
188
|
+
query instead of a sidecar crawl), uploads grouped by recording session
|
|
189
|
+
via `relatedTo`, WebVTT captions, Swagger UI, and an `onArtifactEvent`
|
|
190
|
+
live feed.
|
|
191
|
+
- [`examples/express-demo`](examples/express-demo) and
|
|
192
|
+
[`examples/meteor-demo`](examples/meteor-demo) — the same demo on
|
|
193
|
+
`@mieweb/pulsevault/core` instead of the plugin, proving the core needs
|
|
194
|
+
about the same amount of glue code under a different framework. Both are
|
|
195
|
+
verified against the real frameworks, not just the test suite —
|
|
196
|
+
`meteor-demo` in particular against a real `meteor create` app, since
|
|
197
|
+
Meteor's bundler needed the compatibility fixes described above.
|
|
184
198
|
|
|
185
199
|
## How a pairing + upload session flows
|
|
186
200
|
|
|
@@ -229,12 +243,12 @@ type PulseVaultPluginOptions = {
|
|
|
229
243
|
storage: PulseVaultStorage;
|
|
230
244
|
prefix: string;
|
|
231
245
|
maxUploadSize: number;
|
|
232
|
-
uploadUnit?: "
|
|
246
|
+
uploadUnit?: "segment" | "merged"; // default: "segment" — see "Upload unit"
|
|
233
247
|
decoratorName?: string; // default: "pulseVault"
|
|
234
248
|
allowedExtensions?:
|
|
235
|
-
| string[]
|
|
236
|
-
| { video?: string[]; project?: string[]; captions?: string[] };
|
|
237
|
-
// defaults: { video: [".mp4"], project: [".pulse", ".zip"], captions: [".
|
|
249
|
+
| string[] // legacy — treated as video-only
|
|
250
|
+
| { video?: string[]; project?: string[]; captions?: string[]; thumbnail?: string[] }; // per-kind (recommended)
|
|
251
|
+
// defaults: { video: [".mp4"], project: [".pulse", ".zip"], captions: [".vtt"], thumbnail: [".jpg", ".jpeg", ".png"] }
|
|
238
252
|
cache?: PulseVaultCacheOptions;
|
|
239
253
|
authorize?: PulseVaultAuthorize;
|
|
240
254
|
validatePayload?: PulseVaultValidatePayload; // runs for every kind; branch on ctx.kind
|
|
@@ -263,12 +277,14 @@ Maximum upload size in bytes. Use `Infinity` for no cap.
|
|
|
263
277
|
|
|
264
278
|
### Upload unit
|
|
265
279
|
|
|
266
|
-
`uploadUnit: "
|
|
280
|
+
`uploadUnit: "segment" | "merged"` — purely advertised via `GET /pulsevault/capabilities`; **the plugin doesn't enforce either.** It tells the client which upload strategy this deployment expects:
|
|
267
281
|
|
|
268
|
-
- `"
|
|
269
|
-
- `"merged"`: the client pre-merges a pulse's clips into one video before uploading. Simpler server-side mental model (one artifact per pulse), more client-side work.
|
|
282
|
+
- `"segment"` (default): the client uploads each recorded clip individually (no merge/re-encode pass), plus one `kind=project` ordering manifest (`<draftId>-segments.pulse`). No captions or thumbnail. Lower client-side cost, finer-grained resumability.
|
|
283
|
+
- `"merged"`: the client pre-merges a pulse's clips into one video before uploading, alongside its captions (`kind=captions`), a beat-timecode manifest (`kind=project`, `<draftId>-beats.pulse` — per-segment `startMs`/`endMs` on the merged timeline), and a poster `kind=thumbnail`. Simpler server-side mental model (one primary artifact per pulse), more client-side work.
|
|
270
284
|
|
|
271
|
-
A
|
|
285
|
+
(A "beat" is a timecode range on the merged timeline — one recorded segment's placement within the merged video — not an upload unit. The old `"beat"` upload unit was renamed to `"segment"`.)
|
|
286
|
+
|
|
287
|
+
A client reads `uploadUnit` at pairing time, before doing any merge or upload work, and branches accordingly. See `PROTOCOL.md` §8 for the full contract.
|
|
272
288
|
|
|
273
289
|
Need both strategies live at once instead of one fixed value for the whole deployment? Pass `uploadUnit` to [`buildUploadLink`](#deep-link-helper) per session — it overrides `/capabilities` for that pairing only, with no server-side option to change.
|
|
274
290
|
|
|
@@ -288,13 +304,13 @@ File extensions accepted per artifact kind. Three accepted forms:
|
|
|
288
304
|
|
|
289
305
|
```ts
|
|
290
306
|
// 1. Omit entirely — uses all three defaults:
|
|
291
|
-
// video: [".mp4"] project: [".pulse", ".zip"] captions: [".
|
|
307
|
+
// video: [".mp4"] project: [".pulse", ".zip"] captions: [".vtt"]
|
|
292
308
|
|
|
293
309
|
// 2. Flat array (legacy) — video-only; project/captions keep their defaults:
|
|
294
310
|
allowedExtensions: [".mp4"]
|
|
295
311
|
|
|
296
312
|
// 3. Per-kind object — unset keys fall back to their defaults:
|
|
297
|
-
allowedExtensions: { video: [".mp4"], project: [".pulse"], captions: [".
|
|
313
|
+
allowedExtensions: { video: [".mp4"], project: [".pulse"], captions: [".vtt"] }
|
|
298
314
|
```
|
|
299
315
|
|
|
300
316
|
All extensions must include the leading dot and are matched case-insensitively. The `kind` field in `Upload-Metadata` determines which list is checked.
|
|
@@ -457,9 +473,9 @@ When the final PATCH lands the plugin runs the following steps in order, for eve
|
|
|
457
473
|
"protocolVersion": 1,
|
|
458
474
|
"minSupportedVersion": 1,
|
|
459
475
|
"maxSupportedVersion": 1,
|
|
460
|
-
"uploadUnit": "
|
|
461
|
-
"kinds": ["video", "project", "captions"],
|
|
462
|
-
"allowedExtensions": { "video": [".mp4"], "project": [".pulse", ".zip"], "captions": [".
|
|
476
|
+
"uploadUnit": "segment",
|
|
477
|
+
"kinds": ["video", "project", "captions", "thumbnail"],
|
|
478
|
+
"allowedExtensions": { "video": [".mp4"], "project": [".pulse", ".zip"], "captions": [".vtt"], "thumbnail": [".jpg", ".jpeg", ".png"] },
|
|
463
479
|
"maxUploadSize": 5368709120,
|
|
464
480
|
"checksum": { "algorithms": ["sha256", "sha1", "md5"] }
|
|
465
481
|
}
|
|
@@ -506,7 +522,7 @@ app.post("/pair", async (_req, reply) => {
|
|
|
506
522
|
});
|
|
507
523
|
```
|
|
508
524
|
|
|
509
|
-
A token authorizes either the artifact it names, or any artifact that declares that one as its `relatedTo` — so one token issued for a video also covers its captions and (under `uploadUnit: "
|
|
525
|
+
A token authorizes either the artifact it names, or any artifact that declares that one as its `relatedTo` — so one token issued for a merged video also covers its captions, beat manifest and thumbnail (or, under `uploadUnit: "segment"`, every clip and the ordering manifest) in the same session, without minting a token per artifact. See `PROTOCOL.md` §5.4 for the full claim shape (`kid`/`iat`/`exp`/`issuer`/`artifactId`) and rationale.
|
|
510
526
|
|
|
511
527
|
## Upload-Metadata protocol
|
|
512
528
|
|
|
@@ -524,7 +540,7 @@ The TUS `Upload-Metadata` header is a comma-separated list of `<key> <base64>` p
|
|
|
524
540
|
Example (`kind=captions`, linked to a video):
|
|
525
541
|
|
|
526
542
|
```
|
|
527
|
-
Upload-Metadata: artifactId <base64(uuid)>, filename <base64("clip.
|
|
543
|
+
Upload-Metadata: artifactId <base64(uuid)>, filename <base64("clip.vtt")>, kind <base64("captions")>, relatedTo <base64(videoArtifactId)>
|
|
528
544
|
```
|
|
529
545
|
|
|
530
546
|
## Local storage
|
|
@@ -664,7 +680,7 @@ Credentials are optional — omit `accessKeyId`/`secretAccessKey` to use the AWS
|
|
|
664
680
|
| `metaCacheLimit` | `10000` | Caps the in-memory metadata cache before evicting the oldest entry. |
|
|
665
681
|
| `clientConfig` | — | Advanced: extra `S3ClientConfig` merged into the client. |
|
|
666
682
|
|
|
667
|
-
>
|
|
683
|
+
> A typical deployment wires these to environment variables (e.g. `S3_BUCKET`, `S3_ENDPOINT`, `AWS_REGION`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`) and picks the storage adapter with a `STORAGE=local|s3` switch — the options table above maps one-to-one onto `createS3Storage(...)`.
|
|
668
684
|
|
|
669
685
|
### Payload validation on remote storage
|
|
670
686
|
|
|
@@ -745,7 +761,7 @@ const uploadLink = buildUploadLink({
|
|
|
745
761
|
// pulsecam://?v=1&artifactId=...&server=https%3A%2F%2Fexample.com%2Fpulsevault&token=secret&uploadUnit=merged
|
|
746
762
|
```
|
|
747
763
|
|
|
748
|
-
`uploadUnit` on the link is a **per-session override** of whatever `GET /capabilities` currently reports (PROTOCOL.md §3, §8). Omit it and nothing changes — the client falls back to `/capabilities` exactly as before. Set it when you want "
|
|
764
|
+
`uploadUnit` on the link is a **per-session override** of whatever `GET /capabilities` currently reports (PROTOCOL.md §3, §8). Omit it and nothing changes — the client falls back to `/capabilities` exactly as before. Set it when you want "segment" and "merged" sessions live at the same time (staged rollout, A/B test, per-tenant policy) instead of one fixed value for the whole deployment — `/capabilities` can only ever report one current value, and a client reading it separately from opening the link is racing whatever the server happened to be serving at that moment, not the value this specific session was paired under.
|
|
749
765
|
|
|
750
766
|
## Tests
|
|
751
767
|
|
|
@@ -770,7 +786,7 @@ Runs a Node `--test` suite against the built plugin. Coverage includes:
|
|
|
770
786
|
- `createChecksumValidator`/`createS3ChecksumValidator`: matching digest accepted, mismatch rejected with cleanup
|
|
771
787
|
- `issueCapabilityToken`/`verifyCapabilityToken`/`createCapabilityAuthorize`: round-trip, tampered signature/payload, expiry + clock tolerance, unknown `kid`, issuer mismatch, key-rotation overlap, `relatedTo`-based session authorization — both as fast unit tests (no server) and wired into real HTTP requests
|
|
772
788
|
- `GET /capabilities`
|
|
773
|
-
- S3/R2 backend (`createS3Storage`): full resumable upload → presigned-redirect playback, `createS3Mp4Sniffer`, `createS3ChecksumValidator`, `DELETE`, `kind=project`/`captions`, run against an in-process
|
|
789
|
+
- S3/R2 backend (`createS3Storage`): full resumable upload → presigned-redirect playback, `createS3Mp4Sniffer`, `createS3ChecksumValidator`, `DELETE`, `kind=project`/`captions`, run against an in-process zero-dependency S3 mock (`test/mock-s3.mjs`, no cloud credentials needed)
|
|
774
790
|
- `@mieweb/pulsevault/core` (the framework-agnostic entry point): the same protocol suite re-run against a bare `http.createServer` wrapping `core.handler`, plus an Express-specific smoke test proving `app.use(prefix, handler)` composition
|
|
775
791
|
|
|
776
792
|
## Accessing storage outside the plugin routes
|
package/dist/app.d.ts
CHANGED
|
@@ -40,13 +40,14 @@ export type PulseVaultPluginOptions = {
|
|
|
40
40
|
*/
|
|
41
41
|
maxUploadSize: number;
|
|
42
42
|
/**
|
|
43
|
-
* Which upload strategy this deployment expects: `"
|
|
43
|
+
* Which upload strategy this deployment expects: `"segment"` uploads each
|
|
44
44
|
* clip individually (no client-side merge/re-encode pass) plus a manifest
|
|
45
45
|
* artifact for ordering, while `"merged"` expects one pre-merged video per
|
|
46
|
-
* pulse
|
|
47
|
-
* on — `pulsevault` doesn't
|
|
46
|
+
* pulse (plus its captions, beat manifest and thumbnail). Purely advertised
|
|
47
|
+
* via `GET /capabilities` for the client to branch on — `pulsevault` doesn't
|
|
48
|
+
* enforce either. Defaults to `"segment"`.
|
|
48
49
|
*/
|
|
49
|
-
uploadUnit?: '
|
|
50
|
+
uploadUnit?: 'segment' | 'merged';
|
|
50
51
|
/**
|
|
51
52
|
* Fastify instance decorator name under which the storage adapter is
|
|
52
53
|
* exposed. Defaults to `"pulseVault"`. Override when registering this
|
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,OAAyB,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACpF,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACpG,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAY5D;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,sEAAsE;IACtE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,wFAAwF;IACxF,OAAO,EAAE,iBAAiB,CAAC;IAC3B;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
package/dist/app.js
CHANGED
|
@@ -22,7 +22,7 @@ const app = async (fastify, opts) => {
|
|
|
22
22
|
prefix: opts.prefix,
|
|
23
23
|
storage: opts.storage,
|
|
24
24
|
maxUploadSize: opts.maxUploadSize,
|
|
25
|
-
uploadUnit: opts.uploadUnit ?? '
|
|
25
|
+
uploadUnit: opts.uploadUnit ?? 'segment',
|
|
26
26
|
allowedExtensions,
|
|
27
27
|
cache: opts.cache,
|
|
28
28
|
authorize: opts.authorize,
|
package/dist/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAChC,OAAO,gBAAgB,EAAE,EAA4B,MAAM,wBAAwB,CAAC;AAIpF,OAAO,EACL,0BAA0B,EAC1B,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,yBAAyB,EACzB,iCAAiC,EACjC,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAChC,OAAO,gBAAgB,EAAE,EAA4B,MAAM,wBAAwB,CAAC;AAIpF,OAAO,EACL,0BAA0B,EAC1B,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,yBAAyB,EACzB,iCAAiC,EACjC,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAqJ1B,MAAM,sBAAsB,GAAG,YAAY,CAAC;AAE5C,MAAM,GAAG,GAAgD,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAC/E,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACxC,qBAAqB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC1C,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpC,yBAAyB,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAClD,iCAAiC,CAAC,IAAI,CAAC,CAAC;IAExC,2EAA2E;IAC3E,wEAAwE;IACxE,+BAA+B;IAC/B,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QACpC,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;IAElC,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,sBAAsB,CAAC;IACnE,MAAM,iBAAiB,GAAG,0BAA0B,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAE7E,OAAO,CAAC,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAE9C,MAAM,OAAO,CAAC,QAAQ,CAAC,gBAAgB,EAAE;QACvC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,SAAS;QACxC,iBAAiB;QACjB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,eAAe,EAAE,sBAAsB,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,sBAAsB,CAAC;QAC1F,gBAAgB,EAAE,uBAAuB,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,uBAAuB,CAAC;QAC9F,eAAe,EAAE,IAAI,CAAC,eAAe;KACtC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,eAAe,EAAE,CAAC,GAAG,EAAE;IACrB,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,KAAK;CACf,CAAC,CAAC;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAkBlD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,2BAA2B,CAAC;AAOnC,OAAO,EACL,uBAAuB,EACvB,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC"}
|
package/dist/core.d.ts
CHANGED
|
@@ -38,8 +38,8 @@ export type PulseVaultCoreOptions = {
|
|
|
38
38
|
stripBasePath?: boolean;
|
|
39
39
|
/** Max TUS upload size in bytes. Required — consumers must choose an explicit cap. Use `Infinity` for no cap. */
|
|
40
40
|
maxUploadSize: number;
|
|
41
|
-
/** Which upload strategy this deployment expects. Purely advertised via `GET /capabilities`. Defaults to `"
|
|
42
|
-
uploadUnit?: '
|
|
41
|
+
/** Which upload strategy this deployment expects. Purely advertised via `GET /capabilities`. Defaults to `"segment"`. */
|
|
42
|
+
uploadUnit?: 'segment' | 'merged';
|
|
43
43
|
/** File extensions allowed per artifact kind. See the Fastify plugin's `allowedExtensions` for the full shape. */
|
|
44
44
|
allowedExtensions?: PulseVaultAllowedExtensionsInput;
|
|
45
45
|
/** Cache-control options forwarded to `@fastify/send` for the GET route. */
|
package/dist/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,EAIL,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D,OAAO,EAAE,KAAK,gBAAgB,EAAiB,MAAM,kBAAkB,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAc,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|