@nodetool-ai/project-services 0.0.0-stage → 0.8.9-bootstrap.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline-bundle.d.ts","sourceRoot":"","sources":["../src/timeline-bundle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,360 @@
1
+ /**
2
+ * Timeline bundle codec — a portable zip holding one timeline document plus the
3
+ * bytes of every asset its clips point at, so a cut can move between installs
4
+ * instead of arriving with dangling `currentAssetId`s.
5
+ *
6
+ * Layout (a zip):
7
+ * manifest.json — format/version, the id → file table, checksums, the font
8
+ * families the document names, and the ids it references
9
+ * but does not carry
10
+ * timeline.json — the sequence wire shape minus id/project/user/timestamps
11
+ * assets/<sha256><ext> — content-addressed asset bytes, deduped
12
+ *
13
+ * Asset ids stay in their own fields inside `timeline.json` (they are bare ids,
14
+ * not media refs, so the `bundle://` rewrite the workflow bundle performs does
15
+ * not apply here). The manifest's `assets[].asset_id` is what maps an id in the
16
+ * document to the file carrying its bytes; import stores each file and rewrites
17
+ * the four id fields to the newly created assets.
18
+ *
19
+ * The codec is storage-agnostic: the caller supplies `fetchAsset` on the way out
20
+ * and `storeAsset` on the way in, so the same functions serve the HTTP route and
21
+ * a test with an in-memory adapter.
22
+ */
23
+ import { createHash } from "node:crypto";
24
+ import { strFromU8, strToU8, unzipSync } from "fflate";
25
+ import { z } from "zod";
26
+ import { timelineDocument } from "@nodetool-ai/protocol/api-schemas/timeline.js";
27
+ import { getAssetFileName } from "@nodetool-ai/asset-services/asset-paths";
28
+ import { collectZip, exportSourceSize, MAX_ZIP_ENTRY_BYTES, sha256OfSource } from "./zip-stream.js";
29
+ export const TIMELINE_BUNDLE_FORMAT = "nodetool-timeline-bundle";
30
+ export const TIMELINE_BUNDLE_VERSION = 1;
31
+ const TIMELINE_ENTRY = "timeline.json";
32
+ const ASSETS_PREFIX = "assets/";
33
+ /** The timeline payload carried in a bundle (no per-user/db fields). */
34
+ export const bundledTimeline = timelineDocument.extend({
35
+ name: z.string().min(1),
36
+ fps: z.number(),
37
+ width: z.number(),
38
+ height: z.number(),
39
+ durationMs: z.number(),
40
+ workflowId: z.string().optional()
41
+ });
42
+ function sha256Hex(bytes) {
43
+ return createHash("sha256").update(bytes).digest("hex");
44
+ }
45
+ function pushUnique(into, seen, value) {
46
+ if (typeof value !== "string" || value === "" || seen.has(value)) {
47
+ return;
48
+ }
49
+ seen.add(value);
50
+ into.push(value);
51
+ }
52
+ /**
53
+ * Every asset id the document points at, in document order, deduped. These are
54
+ * the only fields import rewrites — anything else stays as written.
55
+ */
56
+ export function collectTimelineAssetIds(doc) {
57
+ const ids = [];
58
+ const seen = new Set();
59
+ for (const clip of doc.clips ?? []) {
60
+ pushUnique(ids, seen, clip.currentAssetId);
61
+ pushUnique(ids, seen, clip.thumbnailAssetId);
62
+ pushUnique(ids, seen, clip.waveformAssetId);
63
+ for (const version of clip.versions ?? []) {
64
+ pushUnique(ids, seen, version.assetId);
65
+ }
66
+ }
67
+ for (const track of doc.tracks ?? []) {
68
+ if (track.instrument?.type === "sampler") {
69
+ for (const zone of track.instrument.zones ?? [])
70
+ pushUnique(ids, seen, zone.assetId);
71
+ }
72
+ }
73
+ return ids;
74
+ }
75
+ /** Font families the document names, so a reader knows what it needs installed. */
76
+ export function collectTimelineFonts(doc) {
77
+ const fonts = [];
78
+ const seen = new Set();
79
+ for (const clip of doc.clips ?? []) {
80
+ pushUnique(fonts, seen, clip.textStyle?.fontFamily);
81
+ pushUnique(fonts, seen, clip.caption?.style?.fontFamily);
82
+ }
83
+ return fonts;
84
+ }
85
+ /** Ids that resolve only on the exporting install; carried for information. */
86
+ export function collectTimelineForeignRefs(doc) {
87
+ const refs = {
88
+ workflow_ids: [],
89
+ storyboard_ids: [],
90
+ script_ids: [],
91
+ job_ids: []
92
+ };
93
+ const seen = {
94
+ workflow: new Set(),
95
+ storyboard: new Set(),
96
+ script: new Set(),
97
+ job: new Set()
98
+ };
99
+ pushUnique(refs.workflow_ids, seen.workflow, doc.workflowId);
100
+ for (const clip of doc.clips ?? []) {
101
+ pushUnique(refs.workflow_ids, seen.workflow, clip.workflowId);
102
+ pushUnique(refs.storyboard_ids, seen.storyboard, clip.storyboardBoardId);
103
+ pushUnique(refs.script_ids, seen.script, clip.scriptId);
104
+ for (const version of clip.versions ?? []) {
105
+ pushUnique(refs.job_ids, seen.job, version.jobId);
106
+ }
107
+ }
108
+ return refs;
109
+ }
110
+ /**
111
+ * A copy of `doc` with every asset id present in `map` replaced. An id the map
112
+ * does not name is left alone — that is how a missing asset stays dangling
113
+ * instead of silently pointing at someone else's bytes.
114
+ */
115
+ export function rewriteTimelineAssetIds(doc, map) {
116
+ const clone = structuredClone(doc);
117
+ const rewrite = (id) => typeof id === "string" ? (map.get(id) ?? id) : id;
118
+ for (const clip of clone.clips ?? []) {
119
+ if (clip.currentAssetId)
120
+ clip.currentAssetId = rewrite(clip.currentAssetId);
121
+ if (clip.thumbnailAssetId) {
122
+ clip.thumbnailAssetId = rewrite(clip.thumbnailAssetId);
123
+ }
124
+ if (clip.waveformAssetId) {
125
+ clip.waveformAssetId = rewrite(clip.waveformAssetId);
126
+ }
127
+ for (const version of clip.versions ?? []) {
128
+ if (version.assetId)
129
+ version.assetId = rewrite(version.assetId);
130
+ }
131
+ }
132
+ for (const track of clone.tracks ?? []) {
133
+ if (track.instrument?.type === "sampler") {
134
+ for (const zone of track.instrument.zones ?? [])
135
+ zone.assetId = map.get(zone.assetId) ?? zone.assetId;
136
+ }
137
+ }
138
+ return clone;
139
+ }
140
+ /**
141
+ * Write a timeline zip to `writer`, embedding every asset its clips name.
142
+ * A local file is hashed and then streamed from disk, so no asset is held in
143
+ * memory whole. Returns the manifest, which is written last.
144
+ */
145
+ export async function writeTimelineBundle(options, writer) {
146
+ const { sequence } = options;
147
+ const written = new Set();
148
+ const assets = [];
149
+ const missing = [];
150
+ for (const assetId of collectTimelineAssetIds(sequence)) {
151
+ const fetched = await options.fetchAsset(assetId);
152
+ const size = fetched ? exportSourceSize(fetched.source) : 0;
153
+ if (!fetched || size > MAX_ZIP_ENTRY_BYTES) {
154
+ // An asset the install no longer has does not fail the export: the id is
155
+ // recorded so the far side can say what it is missing. So is a file too
156
+ // large for a zip entry without Zip64.
157
+ missing.push(assetId);
158
+ continue;
159
+ }
160
+ const sha256 = await sha256OfSource(fetched.source);
161
+ const file = `${ASSETS_PREFIX}${getAssetFileName(sha256, fetched.contentType)}`;
162
+ // Content-addressed, so two ids sharing bytes pack one file and get two
163
+ // manifest rows pointing at it.
164
+ if (!written.has(file)) {
165
+ written.add(file);
166
+ await writer.add(file, fetched.source);
167
+ }
168
+ assets.push({
169
+ file,
170
+ asset_id: assetId,
171
+ name: fetched.name,
172
+ content_type: fetched.contentType,
173
+ bytes: size,
174
+ sha256
175
+ });
176
+ }
177
+ const manifest = {
178
+ format: TIMELINE_BUNDLE_FORMAT,
179
+ version: TIMELINE_BUNDLE_VERSION,
180
+ created_at: new Date().toISOString(),
181
+ timeline: { file: TIMELINE_ENTRY, name: sequence.name },
182
+ assets,
183
+ missing_assets: missing,
184
+ fonts: collectTimelineFonts(sequence),
185
+ foreign_refs: collectTimelineForeignRefs(sequence)
186
+ };
187
+ await writer.add(TIMELINE_ENTRY, strToU8(JSON.stringify(sequence, null, 2)), {
188
+ compress: true
189
+ });
190
+ await writer.add("manifest.json", strToU8(JSON.stringify(manifest, null, 2)), {
191
+ compress: true
192
+ });
193
+ return manifest;
194
+ }
195
+ /** Build a timeline zip in memory. The HTTP export streams instead. */
196
+ export async function packTimelineBundle(options) {
197
+ const written = {};
198
+ const bytes = await collectZip(async (writer) => {
199
+ written.manifest = await writeTimelineBundle(options, writer);
200
+ });
201
+ if (!written.manifest)
202
+ throw new Error("Timeline bundle wrote no manifest");
203
+ return { bytes, manifest: written.manifest };
204
+ }
205
+ /** Max total decompressed size of a bundle — guards against zip bombs. */
206
+ const MAX_BUNDLE_UNCOMPRESSED_BYTES = 512 * 1024 * 1024; // 512 MB
207
+ /** Max number of entries in a bundle. */
208
+ const MAX_BUNDLE_ENTRIES = 10_000;
209
+ /**
210
+ * Inflate an uploaded zip with bounds: `unzipSync` otherwise inflates every
211
+ * entry into memory with no size limit, so a small highly-compressible upload
212
+ * can expand to tens of GB and OOM the server. The filter runs per entry with
213
+ * the header-declared uncompressed size, before that entry is inflated.
214
+ */
215
+ export function unzipBounded(zipBytes, limits = {}) {
216
+ const maxBytes = limits.maxBytes ?? MAX_BUNDLE_UNCOMPRESSED_BYTES;
217
+ const maxEntries = limits.maxEntries ?? MAX_BUNDLE_ENTRIES;
218
+ let totalUncompressed = 0;
219
+ let entryCount = 0;
220
+ return unzipSync(zipBytes, {
221
+ filter: (file) => {
222
+ entryCount += 1;
223
+ if (entryCount > maxEntries) {
224
+ throw new Error(`Bundle rejected: too many entries (limit ${maxEntries})`);
225
+ }
226
+ totalUncompressed += file.originalSize;
227
+ if (totalUncompressed > maxBytes) {
228
+ throw new Error(`Bundle rejected: decompressed size exceeds ${maxBytes} bytes`);
229
+ }
230
+ return true;
231
+ }
232
+ });
233
+ }
234
+ /** The entry names of a zip, read from its directory without inflating. */
235
+ export function zipEntryNames(zipBytes) {
236
+ const names = [];
237
+ unzipSync(zipBytes, {
238
+ filter: (file) => {
239
+ names.push(file.name);
240
+ return false;
241
+ }
242
+ });
243
+ return names;
244
+ }
245
+ export function unpackTimelineBundle(zipBytes) {
246
+ const entries = unzipBounded(zipBytes);
247
+ const manifestEntry = entries["manifest.json"];
248
+ if (!manifestEntry) {
249
+ throw new Error("Invalid bundle: missing manifest.json");
250
+ }
251
+ const manifest = JSON.parse(strFromU8(manifestEntry));
252
+ if (manifest.format !== TIMELINE_BUNDLE_FORMAT) {
253
+ throw new Error(`Unrecognized bundle format: ${String(manifest.format)}`);
254
+ }
255
+ if (manifest.version > TIMELINE_BUNDLE_VERSION) {
256
+ throw new Error(`Bundle version ${manifest.version} is newer than supported (${TIMELINE_BUNDLE_VERSION})`);
257
+ }
258
+ const timelineFile = manifest.timeline?.file || TIMELINE_ENTRY;
259
+ const timelineEntry = entries[timelineFile];
260
+ if (!timelineEntry) {
261
+ throw new Error(`Invalid bundle: missing ${timelineFile}`);
262
+ }
263
+ const timeline = JSON.parse(strFromU8(timelineEntry));
264
+ if (!Array.isArray(timeline?.tracks) ||
265
+ !Array.isArray(timeline?.clips) ||
266
+ !Array.isArray(timeline?.markers)) {
267
+ throw new Error(`Invalid bundle: ${timelineFile} has no tracks, clips, and markers arrays`);
268
+ }
269
+ const assets = new Map();
270
+ for (const [path, bytes] of Object.entries(entries)) {
271
+ if (path.startsWith(ASSETS_PREFIX)) {
272
+ assets.set(path.slice(ASSETS_PREFIX.length), bytes);
273
+ }
274
+ }
275
+ return { manifest, timeline, assets };
276
+ }
277
+ /** Verify each asset's bytes against the manifest sha256. Returns mismatches. */
278
+ export function verifyTimelineBundleChecksums(bundle) {
279
+ const mismatches = [];
280
+ const checked = new Set();
281
+ for (const entry of bundle.manifest.assets ?? []) {
282
+ const fileName = entry.file.startsWith(ASSETS_PREFIX)
283
+ ? entry.file.slice(ASSETS_PREFIX.length)
284
+ : entry.file;
285
+ if (checked.has(fileName)) {
286
+ continue; // deduped bytes, hashed once
287
+ }
288
+ checked.add(fileName);
289
+ const bytes = bundle.assets.get(fileName);
290
+ if (!bytes) {
291
+ mismatches.push(`${entry.file} (missing)`);
292
+ continue;
293
+ }
294
+ if (entry.sha256 && sha256Hex(bytes) !== entry.sha256) {
295
+ mismatches.push(`${entry.file} (checksum)`);
296
+ }
297
+ }
298
+ return mismatches;
299
+ }
300
+ /**
301
+ * Store a bundle's assets and return a timeline ready to create. Assets shared
302
+ * by several ids are stored once; an id with no bytes keeps its original value
303
+ * and is reported in `missing`.
304
+ */
305
+ export async function importTimelineBundle(zipBytes, options) {
306
+ const bundle = unpackTimelineBundle(zipBytes);
307
+ const checksumMismatches = verifyTimelineBundleChecksums(bundle);
308
+ if (options.verifyChecksums && checksumMismatches.length > 0) {
309
+ throw new Error(`Bundle checksum verification failed: ${checksumMismatches.join(", ")}`);
310
+ }
311
+ const imported = [];
312
+ const missing = new Set(bundle.manifest.missing_assets ?? []);
313
+ const idMap = new Map();
314
+ const storedByFile = new Map();
315
+ for (const entry of bundle.manifest.assets ?? []) {
316
+ const fileName = entry.file.startsWith(ASSETS_PREFIX)
317
+ ? entry.file.slice(ASSETS_PREFIX.length)
318
+ : entry.file;
319
+ const bytes = bundle.assets.get(fileName);
320
+ if (!bytes) {
321
+ missing.add(entry.asset_id);
322
+ continue;
323
+ }
324
+ let assetId = storedByFile.get(fileName);
325
+ if (!assetId) {
326
+ const stored = await options.storeAsset({
327
+ bytes,
328
+ name: entry.name || fileName,
329
+ contentType: entry.content_type || "application/octet-stream",
330
+ sourceAssetId: entry.asset_id
331
+ });
332
+ assetId = stored.assetId;
333
+ storedByFile.set(fileName, assetId);
334
+ // One row per stored file, so `imported.length` is what was written.
335
+ imported.push({
336
+ file: entry.file,
337
+ source_asset_id: entry.asset_id,
338
+ asset_id: assetId
339
+ });
340
+ }
341
+ idMap.set(entry.asset_id, assetId);
342
+ }
343
+ // Validate at the boundary — the same gate the tRPC `update` applies, so an
344
+ // uploaded document cannot smuggle fields the editor would then write back.
345
+ const validated = bundledTimeline.parse(bundle.timeline);
346
+ const timeline = rewriteTimelineAssetIds(validated, idMap);
347
+ // An id the document names that no manifest row covered has no bytes here.
348
+ for (const id of collectTimelineAssetIds(bundle.timeline)) {
349
+ if (!idMap.has(id)) {
350
+ missing.add(id);
351
+ }
352
+ }
353
+ return {
354
+ timeline,
355
+ imported,
356
+ missing: [...missing],
357
+ checksumMismatches
358
+ };
359
+ }
360
+ //# sourceMappingURL=timeline-bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline-bundle.js","sourceRoot":"","sources":["../src/timeline-bundle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { Readable } from "node:stream";
2
+ /** A local file to read in place. */
3
+ export interface LocalFileSource {
4
+ path: string;
5
+ size: number;
6
+ }
7
+ /** What an export entry reads from: bytes in memory or a local file. */
8
+ export type ExportSource = Uint8Array | LocalFileSource;
9
+ /** Largest entry fflate can write without Zip64. */
10
+ export declare const MAX_ZIP_ENTRY_BYTES = 4294967295;
11
+ export declare function isLocalFileSource(source: ExportSource): source is LocalFileSource;
12
+ export declare function exportSourceSize(source: ExportSource): number;
13
+ /** Hex sha256 of `source`, streaming a local file. */
14
+ export declare function sha256OfSource(source: ExportSource): Promise<string>;
15
+ /** Receives archive chunks in order and resolves when it can take more. */
16
+ export type ZipChunkSink = (chunk: Uint8Array) => Promise<void>;
17
+ /**
18
+ * Writes zip entries one after another to a sink, waiting on the sink between
19
+ * chunks so a slow reader holds back the file reads.
20
+ */
21
+ export declare class ZipStreamWriter {
22
+ private readonly sink;
23
+ private readonly zip;
24
+ private pending;
25
+ private failure;
26
+ private writtenBytes;
27
+ /** Bytes already delivered to the sink, excluding the pending directory. */
28
+ get bytesWritten(): number;
29
+ constructor(sink: ZipChunkSink);
30
+ private flush;
31
+ /**
32
+ * Add one entry. `compress` deflates it, which suits text; media is already
33
+ * compressed and is stored as-is.
34
+ */
35
+ add(name: string, source: ExportSource, options?: {
36
+ compress?: boolean;
37
+ }): Promise<void>;
38
+ /** Write the central directory. No entry may be added afterwards. */
39
+ finish(): Promise<void>;
40
+ }
41
+ /** Run `write` against a writer that collects the whole archive in memory. */
42
+ export declare function collectZip(write: (writer: ZipStreamWriter) => Promise<void>): Promise<Uint8Array>;
43
+ /**
44
+ * A readable stream of the archive `write` produces. Writing starts at once
45
+ * and waits whenever the reader falls behind; a failure or a reader that goes
46
+ * away destroys the stream, which ends the download early.
47
+ */
48
+ export declare function streamZip(write: (writer: ZipStreamWriter) => Promise<void>): Readable;
49
+ //# sourceMappingURL=zip-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zip-stream.d.ts","sourceRoot":"","sources":["../src/zip-stream.ts"],"names":[],"mappings":"AAeA,OAAO,EAAe,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGpD,qCAAqC;AACrC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wEAAwE;AACxE,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,eAAe,CAAC;AAExD,oDAAoD;AACpD,eAAO,MAAM,mBAAmB,aAAc,CAAC;AAE/C,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,YAAY,GACnB,MAAM,IAAI,eAAe,CAE3B;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAE7D;AAaD,sDAAsD;AACtD,wBAAsB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAM1E;AAED,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEhE;;;GAGG;AACH,qBAAa,eAAe;IAWd,OAAO,CAAC,QAAQ,CAAC,IAAI;IAVjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAM;IAC1B,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,YAAY,CAAK;IAEzB,4EAA4E;IAC5E,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,YAA6B,IAAI,EAAE,YAAY,EAQ9C;YAEa,KAAK;IASnB;;;OAGG;IACG,GAAG,CACP,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAO,GACnC,OAAO,CAAC,IAAI,CAAC,CAWf;IAED,qEAAqE;IAC/D,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAG5B;CACF;AAED,8EAA8E;AAC9E,wBAAsB,UAAU,CAC9B,KAAK,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,GAChD,OAAO,CAAC,UAAU,CAAC,CAQrB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,GAChD,QAAQ,CAgCV"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Streaming zip output for the export surfaces (timeline and storyboard zips).
3
+ *
4
+ * An export used to load every asset into memory and build the archive with
5
+ * `zipSync`, so a timeline naming a two-hour video held the video, and then
6
+ * the whole archive, in memory. Here each entry is written as it is read: a
7
+ * local file (a managed asset under the local store, or an external asset in
8
+ * place) is streamed from disk, and only a backend with no local file hands
9
+ * over bytes. The archive itself goes out as a stream.
10
+ *
11
+ * fflate writes no Zip64 records, so an entry is limited to 4 GiB; callers
12
+ * check {@link MAX_ZIP_ENTRY_BYTES} and leave a larger file out.
13
+ */
14
+ import { createHash } from "node:crypto";
15
+ import { createReadStream } from "node:fs";
16
+ import { PassThrough } from "node:stream";
17
+ import { Zip, ZipDeflate, ZipPassThrough } from "fflate";
18
+ /** Largest entry fflate can write without Zip64. */
19
+ export const MAX_ZIP_ENTRY_BYTES = 0xffff_ffff;
20
+ export function isLocalFileSource(source) {
21
+ return !(source instanceof Uint8Array);
22
+ }
23
+ export function exportSourceSize(source) {
24
+ return isLocalFileSource(source) ? source.size : source.byteLength;
25
+ }
26
+ /** Chunks of `source`, read from disk for a local file. */
27
+ async function* readSource(source) {
28
+ if (!isLocalFileSource(source)) {
29
+ yield source;
30
+ return;
31
+ }
32
+ for await (const chunk of createReadStream(source.path)) {
33
+ yield chunk;
34
+ }
35
+ }
36
+ /** Hex sha256 of `source`, streaming a local file. */
37
+ export async function sha256OfSource(source) {
38
+ const hash = createHash("sha256");
39
+ for await (const chunk of readSource(source)) {
40
+ hash.update(chunk);
41
+ }
42
+ return hash.digest("hex");
43
+ }
44
+ /**
45
+ * Writes zip entries one after another to a sink, waiting on the sink between
46
+ * chunks so a slow reader holds back the file reads.
47
+ */
48
+ export class ZipStreamWriter {
49
+ sink;
50
+ zip;
51
+ pending = [];
52
+ failure = null;
53
+ writtenBytes = 0;
54
+ /** Bytes already delivered to the sink, excluding the pending directory. */
55
+ get bytesWritten() {
56
+ return this.writtenBytes;
57
+ }
58
+ constructor(sink) {
59
+ this.sink = sink;
60
+ this.zip = new Zip((err, chunk) => {
61
+ if (err) {
62
+ this.failure = err;
63
+ return;
64
+ }
65
+ this.pending.push(chunk);
66
+ });
67
+ }
68
+ async flush() {
69
+ if (this.failure)
70
+ throw this.failure;
71
+ while (this.pending.length > 0) {
72
+ const chunk = this.pending.shift();
73
+ await this.sink(chunk);
74
+ this.writtenBytes += chunk.byteLength;
75
+ }
76
+ }
77
+ /**
78
+ * Add one entry. `compress` deflates it, which suits text; media is already
79
+ * compressed and is stored as-is.
80
+ */
81
+ async add(name, source, options = {}) {
82
+ const entry = options.compress
83
+ ? new ZipDeflate(name, { level: 6 })
84
+ : new ZipPassThrough(name);
85
+ this.zip.add(entry);
86
+ for await (const chunk of readSource(source)) {
87
+ entry.push(chunk, false);
88
+ await this.flush();
89
+ }
90
+ entry.push(new Uint8Array(0), true);
91
+ await this.flush();
92
+ }
93
+ /** Write the central directory. No entry may be added afterwards. */
94
+ async finish() {
95
+ this.zip.end();
96
+ await this.flush();
97
+ }
98
+ }
99
+ /** Run `write` against a writer that collects the whole archive in memory. */
100
+ export async function collectZip(write) {
101
+ const chunks = [];
102
+ const writer = new ZipStreamWriter(async (chunk) => {
103
+ chunks.push(chunk);
104
+ });
105
+ await write(writer);
106
+ await writer.finish();
107
+ return new Uint8Array(Buffer.concat(chunks));
108
+ }
109
+ /**
110
+ * A readable stream of the archive `write` produces. Writing starts at once
111
+ * and waits whenever the reader falls behind; a failure or a reader that goes
112
+ * away destroys the stream, which ends the download early.
113
+ */
114
+ export function streamZip(write) {
115
+ const out = new PassThrough();
116
+ const sink = (chunk) => new Promise((resolve, reject) => {
117
+ if (out.destroyed) {
118
+ reject(new Error("zip reader went away"));
119
+ return;
120
+ }
121
+ if (out.write(chunk)) {
122
+ resolve();
123
+ return;
124
+ }
125
+ const onDrain = () => {
126
+ out.off("close", onClose);
127
+ resolve();
128
+ };
129
+ const onClose = () => {
130
+ out.off("drain", onDrain);
131
+ reject(new Error("zip reader went away"));
132
+ };
133
+ out.once("drain", onDrain);
134
+ out.once("close", onClose);
135
+ });
136
+ const writer = new ZipStreamWriter(sink);
137
+ void (async () => {
138
+ await write(writer);
139
+ await writer.finish();
140
+ out.end();
141
+ })().catch((err) => {
142
+ out.destroy(err instanceof Error ? err : new Error(String(err)));
143
+ });
144
+ return out;
145
+ }
146
+ //# sourceMappingURL=zip-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zip-stream.js","sourceRoot":"","sources":["../src/zip-stream.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,58 @@
1
1
  {
2
2
  "name": "@nodetool-ai/project-services",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.8.9-bootstrap.0",
4
+ "description": "NodeTool project services",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22.0.0"
8
+ },
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "nodetool-dev": "./src/index.ts",
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./*": {
19
+ "nodetool-dev": "./src/*.ts",
20
+ "types": "./dist/*.d.ts",
21
+ "import": "./dist/*.js",
22
+ "default": "./dist/*.js"
23
+ }
24
+ },
25
+ "scripts": {
26
+ "build": "node ../../scripts/build-typescript-workspace.mjs",
27
+ "lint": "node ../../scripts/run-tsc.mjs --noEmit"
28
+ },
29
+ "dependencies": {
30
+ "@nodetool-ai/asset-services": "0.8.9-bootstrap.0",
31
+ "@nodetool-ai/config": "0.8.9-bootstrap.0",
32
+ "@nodetool-ai/execution": "0.8.9-bootstrap.0",
33
+ "@nodetool-ai/models": "0.8.9-bootstrap.0",
34
+ "@nodetool-ai/protocol": "0.8.9-bootstrap.0",
35
+ "@nodetool-ai/runtime": "0.8.9-bootstrap.0",
36
+ "@nodetool-ai/storage": "0.8.9-bootstrap.0",
37
+ "fflate": "^0.8.2",
38
+ "zod": "^4.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^22.0.0",
42
+ "typescript": "npm:@typescript/typescript6@6.0.2",
43
+ "vitest": "^5.0.1"
44
+ },
45
+ "license": "MIT",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/nodetool-ai/nodetool.git",
49
+ "directory": "packages/project-services"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "files": [
55
+ "dist",
56
+ "README.md"
57
+ ]
58
+ }