@starkeep/protocol-primitives 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/dist/index.cjs ADDED
@@ -0,0 +1,627 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ APP_GRANTABLE_CATEGORIES: () => APP_GRANTABLE_CATEGORIES,
34
+ CATEGORIES: () => CATEGORIES,
35
+ CATEGORY_IDS: () => CATEGORY_IDS,
36
+ ConflictError: () => ConflictError,
37
+ EXTENSIONS: () => EXTENSIONS,
38
+ KNOWN_EXTENSIONS: () => KNOWN_EXTENSIONS,
39
+ NotFoundError: () => NotFoundError,
40
+ StarkeepError: () => StarkeepError,
41
+ ValidationError: () => ValidationError,
42
+ ZERO_HLC: () => ZERO_HLC,
43
+ appSyncableObjectKey: () => appSyncableObjectKey,
44
+ categoryOf: () => categoryOf,
45
+ compareHLC: () => compareHLC,
46
+ createDataRecord: () => createDataRecord,
47
+ createHLCClock: () => createHLCClock,
48
+ createStarkeepId: () => createStarkeepId,
49
+ createTypeRegistry: () => createTypeRegistry,
50
+ dataRecordObjectKey: () => dataRecordObjectKey,
51
+ dataRecordSchema: () => dataRecordSchema,
52
+ deserializeHLC: () => deserializeHLC,
53
+ err: () => err,
54
+ generateId: () => generateId,
55
+ generateIdAt: () => generateIdAt,
56
+ getCategory: () => getCategory,
57
+ isCategoryId: () => isCategoryId,
58
+ isStarkeepId: () => isStarkeepId,
59
+ maxHLC: () => maxHLC,
60
+ ok: () => ok,
61
+ pgMetadataDdl: () => pgMetadataDdl,
62
+ pgMetadataTableName: () => pgMetadataTableName,
63
+ serializeHLC: () => serializeHLC,
64
+ sqliteMetadataDdl: () => sqliteMetadataDdl,
65
+ sqliteMetadataTableName: () => sqliteMetadataTableName,
66
+ validateDataRecord: () => validateDataRecord
67
+ });
68
+ module.exports = __toCommonJS(index_exports);
69
+
70
+ // src/identifiers/types.ts
71
+ function createStarkeepId(value) {
72
+ return value;
73
+ }
74
+ function isStarkeepId(value) {
75
+ return typeof value === "string" && value.length === 26;
76
+ }
77
+
78
+ // src/identifiers/ulid.ts
79
+ var import_ulidx = require("ulidx");
80
+ var monotonic = (0, import_ulidx.monotonicFactory)();
81
+ function generateId() {
82
+ return createStarkeepId(monotonic());
83
+ }
84
+ function generateIdAt(timestamp) {
85
+ return createStarkeepId((0, import_ulidx.ulid)(timestamp));
86
+ }
87
+
88
+ // src/hlc/clock.ts
89
+ function createHLCClock(options) {
90
+ const { nodeId, wallClockFunction = Date.now, initialState, onTick } = options;
91
+ let lastWallTime = initialState?.wallTime ?? 0;
92
+ let lastCounter = initialState?.counter ?? 0;
93
+ function emit() {
94
+ if (onTick) onTick({ wallTime: lastWallTime, counter: lastCounter });
95
+ }
96
+ function now() {
97
+ const physicalTime = wallClockFunction();
98
+ if (physicalTime > lastWallTime) {
99
+ lastWallTime = physicalTime;
100
+ lastCounter = 0;
101
+ } else {
102
+ lastCounter++;
103
+ }
104
+ emit();
105
+ return { wallTime: lastWallTime, counter: lastCounter, nodeId };
106
+ }
107
+ function send() {
108
+ return now();
109
+ }
110
+ function receive(remote) {
111
+ const physicalTime = wallClockFunction();
112
+ if (physicalTime > lastWallTime && physicalTime > remote.wallTime) {
113
+ lastWallTime = physicalTime;
114
+ lastCounter = 0;
115
+ } else if (remote.wallTime > lastWallTime) {
116
+ lastWallTime = remote.wallTime;
117
+ lastCounter = remote.counter + 1;
118
+ } else if (lastWallTime === remote.wallTime) {
119
+ lastCounter = Math.max(lastCounter, remote.counter) + 1;
120
+ } else {
121
+ lastCounter++;
122
+ }
123
+ emit();
124
+ return { wallTime: lastWallTime, counter: lastCounter, nodeId };
125
+ }
126
+ return { now, send, receive };
127
+ }
128
+
129
+ // src/hlc/compare.ts
130
+ var ZERO_HLC = { wallTime: 0, counter: 0, nodeId: "" };
131
+ function compareHLC(a, b) {
132
+ if (a.wallTime < b.wallTime) return -1;
133
+ if (a.wallTime > b.wallTime) return 1;
134
+ if (a.counter < b.counter) return -1;
135
+ if (a.counter > b.counter) return 1;
136
+ if (a.nodeId < b.nodeId) return -1;
137
+ if (a.nodeId > b.nodeId) return 1;
138
+ return 0;
139
+ }
140
+ function maxHLC(a, b) {
141
+ return compareHLC(a, b) >= 0 ? a : b;
142
+ }
143
+
144
+ // src/hlc/serialize.ts
145
+ var SEPARATOR = ":";
146
+ function serializeHLC(timestamp) {
147
+ const wallTimeHex = timestamp.wallTime.toString(16).padStart(12, "0");
148
+ const counterHex = timestamp.counter.toString(16).padStart(4, "0");
149
+ return `${wallTimeHex}${SEPARATOR}${counterHex}${SEPARATOR}${timestamp.nodeId}`;
150
+ }
151
+ function deserializeHLC(serializedString) {
152
+ const parts = serializedString.split(SEPARATOR);
153
+ if (parts.length !== 3) {
154
+ throw new Error(`Invalid HLC timestamp string: ${serializedString}`);
155
+ }
156
+ return {
157
+ wallTime: parseInt(parts[0], 16),
158
+ counter: parseInt(parts[1], 16),
159
+ nodeId: parts[2]
160
+ };
161
+ }
162
+
163
+ // src/records/builders.ts
164
+ function createDataRecord(input, clock) {
165
+ const now = clock.now();
166
+ return {
167
+ id: generateId(),
168
+ kind: "data",
169
+ type: input.type,
170
+ createdAt: now,
171
+ updatedAt: now,
172
+ ownerId: input.ownerId,
173
+ deletedAt: null,
174
+ version: 1,
175
+ contentHash: input.contentHash,
176
+ objectStorageKey: input.objectStorageKey,
177
+ mimeType: input.mimeType,
178
+ sizeBytes: input.sizeBytes,
179
+ originalFilename: input.originalFilename ?? null,
180
+ originAppId: input.originAppId,
181
+ parentId: input.parentId ?? null
182
+ };
183
+ }
184
+
185
+ // src/schema/registry.ts
186
+ function createTypeRegistry() {
187
+ const types = /* @__PURE__ */ new Map();
188
+ return {
189
+ register(definition) {
190
+ const key = `${definition.namespace}:${definition.name}`;
191
+ if (types.has(key)) {
192
+ throw new Error(`Type "${key}" is already registered`);
193
+ }
194
+ types.set(key, definition);
195
+ },
196
+ get(namespace, name) {
197
+ return types.get(`${namespace}:${name}`);
198
+ },
199
+ getByKey(key) {
200
+ return types.get(key);
201
+ },
202
+ has(namespace, name) {
203
+ return types.has(`${namespace}:${name}`);
204
+ },
205
+ list() {
206
+ return Array.from(types.values());
207
+ }
208
+ };
209
+ }
210
+
211
+ // src/schema/validator.ts
212
+ var v = __toESM(require("valibot"), 1);
213
+ var hlcTimestampSchema = v.object({
214
+ wallTime: v.pipe(v.number(), v.integer(), v.minValue(0)),
215
+ counter: v.pipe(v.number(), v.integer(), v.minValue(0)),
216
+ nodeId: v.pipe(v.string(), v.minLength(1))
217
+ });
218
+ var baseRecordSchema = v.object({
219
+ id: v.pipe(v.string(), v.length(26)),
220
+ type: v.pipe(v.string(), v.minLength(1)),
221
+ createdAt: hlcTimestampSchema,
222
+ updatedAt: hlcTimestampSchema,
223
+ ownerId: v.pipe(v.string(), v.minLength(1)),
224
+ deletedAt: v.nullable(hlcTimestampSchema),
225
+ version: v.pipe(v.number(), v.integer(), v.minValue(1))
226
+ });
227
+ var dataRecordSchema = v.object({
228
+ ...baseRecordSchema.entries,
229
+ kind: v.literal("data"),
230
+ contentHash: v.pipe(v.string(), v.minLength(1)),
231
+ objectStorageKey: v.pipe(v.string(), v.minLength(1)),
232
+ mimeType: v.pipe(v.string(), v.minLength(1)),
233
+ sizeBytes: v.pipe(v.number(), v.integer(), v.minValue(0)),
234
+ originalFilename: v.nullable(v.string()),
235
+ originAppId: v.pipe(v.string(), v.minLength(1)),
236
+ parentId: v.nullable(v.string())
237
+ });
238
+ function validateDataRecord(data) {
239
+ return v.safeParse(dataRecordSchema, data);
240
+ }
241
+
242
+ // src/types/errors.ts
243
+ var StarkeepError = class extends Error {
244
+ constructor(message, code, cause) {
245
+ super(message);
246
+ this.code = code;
247
+ this.cause = cause;
248
+ this.name = "StarkeepError";
249
+ }
250
+ };
251
+ var ValidationError = class extends StarkeepError {
252
+ constructor(message, cause) {
253
+ super(message, "VALIDATION_ERROR", cause);
254
+ this.name = "ValidationError";
255
+ }
256
+ };
257
+ var NotFoundError = class extends StarkeepError {
258
+ constructor(entity, id) {
259
+ super(`${entity} not found: ${id}`, "NOT_FOUND");
260
+ this.name = "NotFoundError";
261
+ }
262
+ };
263
+ var ConflictError = class extends StarkeepError {
264
+ constructor(message) {
265
+ super(message, "CONFLICT");
266
+ this.name = "ConflictError";
267
+ }
268
+ };
269
+
270
+ // src/types/common.ts
271
+ function ok(value) {
272
+ return { ok: true, value };
273
+ }
274
+ function err(error) {
275
+ return { ok: false, error };
276
+ }
277
+
278
+ // src/types/core-types.ts
279
+ var IMAGE_METADATA_COLUMNS = [
280
+ { name: "width", type: "integer" },
281
+ { name: "height", type: "integer" },
282
+ { name: "color_space", type: "text" },
283
+ { name: "orientation", type: "integer" },
284
+ { name: "captured_at", type: "timestamp" },
285
+ { name: "camera_make", type: "text" },
286
+ { name: "camera_model", type: "text" },
287
+ { name: "lens_model", type: "text" },
288
+ { name: "f_number", type: "real" },
289
+ { name: "exposure_time", type: "text" },
290
+ { name: "iso", type: "integer" },
291
+ { name: "focal_length_mm", type: "real" },
292
+ { name: "gps_lat", type: "real" },
293
+ { name: "gps_lon", type: "real" }
294
+ ];
295
+ var VIDEO_METADATA_COLUMNS = [
296
+ { name: "width", type: "integer" },
297
+ { name: "height", type: "integer" },
298
+ { name: "duration_ms", type: "bigint" },
299
+ { name: "frame_rate", type: "real" },
300
+ { name: "video_codec", type: "text" },
301
+ { name: "audio_codec", type: "text" },
302
+ { name: "bitrate", type: "bigint" },
303
+ { name: "captured_at", type: "timestamp" },
304
+ { name: "gps_lat", type: "real" },
305
+ { name: "gps_lon", type: "real" }
306
+ ];
307
+ var AUDIO_METADATA_COLUMNS = [
308
+ { name: "duration_ms", type: "bigint" },
309
+ { name: "sample_rate", type: "integer" },
310
+ { name: "channels", type: "integer" },
311
+ { name: "bitrate", type: "bigint" },
312
+ { name: "codec", type: "text" },
313
+ { name: "title", type: "text" },
314
+ { name: "artist", type: "text" },
315
+ { name: "album", type: "text" },
316
+ { name: "track_number", type: "integer" },
317
+ { name: "year", type: "integer" },
318
+ { name: "genre", type: "text" }
319
+ ];
320
+ var DOCUMENT_METADATA_COLUMNS = [
321
+ { name: "page_count", type: "integer" },
322
+ { name: "word_count", type: "integer" },
323
+ { name: "author", type: "text" },
324
+ { name: "title", type: "text" },
325
+ { name: "created_at", type: "timestamp" },
326
+ { name: "modified_at", type: "timestamp" },
327
+ { name: "language", type: "text" }
328
+ ];
329
+ var TEXT_METADATA_COLUMNS = [
330
+ { name: "line_count", type: "integer" },
331
+ { name: "encoding", type: "text" }
332
+ ];
333
+ var CODE_METADATA_COLUMNS = [
334
+ { name: "line_count", type: "integer" },
335
+ { name: "encoding", type: "text" }
336
+ ];
337
+ var FONT_METADATA_COLUMNS = [
338
+ { name: "family", type: "text" },
339
+ { name: "subfamily", type: "text" },
340
+ { name: "weight", type: "integer" },
341
+ { name: "style", type: "text" },
342
+ { name: "format", type: "text" }
343
+ ];
344
+ var ARCHIVE_METADATA_COLUMNS = [
345
+ { name: "entry_count", type: "integer" },
346
+ { name: "uncompressed_bytes", type: "bigint" },
347
+ { name: "compression", type: "text" }
348
+ ];
349
+ var DATA_METADATA_COLUMNS = [
350
+ { name: "row_count", type: "bigint" },
351
+ { name: "column_count", type: "integer" },
352
+ { name: "schema_json", type: "text" }
353
+ ];
354
+ var MODEL3D_METADATA_COLUMNS = [
355
+ { name: "vertex_count", type: "bigint" },
356
+ { name: "face_count", type: "bigint" },
357
+ { name: "has_textures", type: "boolean" },
358
+ { name: "has_animation", type: "boolean" }
359
+ ];
360
+ var CATEGORIES = [
361
+ { id: "image", description: "Raster and vector still images. Bytes in object storage; metadata holds dimensions, capture time, EXIF, orientation.", metadataColumns: IMAGE_METADATA_COLUMNS },
362
+ { id: "video", description: "Moving-picture containers.", metadataColumns: VIDEO_METADATA_COLUMNS },
363
+ { id: "audio", description: "Sound-only containers.", metadataColumns: AUDIO_METADATA_COLUMNS },
364
+ { id: "document", description: "Office-suite and structured documents meant for human reading (incl. markdown, html, spreadsheets).", metadataColumns: DOCUMENT_METADATA_COLUMNS },
365
+ { id: "text", description: "Plain-text formats: prose, config, structured serialization.", metadataColumns: TEXT_METADATA_COLUMNS },
366
+ { id: "code", description: "Programming-language source files.", metadataColumns: CODE_METADATA_COLUMNS },
367
+ { id: "font", description: "Typeface files.", metadataColumns: FONT_METADATA_COLUMNS },
368
+ { id: "archive", description: "Compressed bundles.", metadataColumns: ARCHIVE_METADATA_COLUMNS },
369
+ { id: "data", description: "Tabular / columnar / embedded-DB data files.", metadataColumns: DATA_METADATA_COLUMNS },
370
+ { id: "model3d", description: "3D meshes and scenes.", metadataColumns: MODEL3D_METADATA_COLUMNS },
371
+ { id: "other", description: "Terminal catch-all for unmapped or extension-less files. Drive-only; no metadata table; no installable-app grants.", metadataColumns: [] }
372
+ ];
373
+ var EXTENSIONS = {
374
+ // image
375
+ jpg: "image",
376
+ jpeg: "image",
377
+ png: "image",
378
+ gif: "image",
379
+ webp: "image",
380
+ heic: "image",
381
+ heif: "image",
382
+ avif: "image",
383
+ bmp: "image",
384
+ tiff: "image",
385
+ tif: "image",
386
+ svg: "image",
387
+ ico: "image",
388
+ // video
389
+ mp4: "video",
390
+ mov: "video",
391
+ m4v: "video",
392
+ avi: "video",
393
+ mkv: "video",
394
+ webm: "video",
395
+ mpg: "video",
396
+ mpeg: "video",
397
+ wmv: "video",
398
+ flv: "video",
399
+ // audio
400
+ mp3: "audio",
401
+ wav: "audio",
402
+ flac: "audio",
403
+ aac: "audio",
404
+ ogg: "audio",
405
+ oga: "audio",
406
+ opus: "audio",
407
+ m4a: "audio",
408
+ aiff: "audio",
409
+ wma: "audio",
410
+ // document
411
+ pdf: "document",
412
+ md: "document",
413
+ markdown: "document",
414
+ html: "document",
415
+ htm: "document",
416
+ doc: "document",
417
+ docx: "document",
418
+ xls: "document",
419
+ xlsx: "document",
420
+ ppt: "document",
421
+ pptx: "document",
422
+ odt: "document",
423
+ ods: "document",
424
+ odp: "document",
425
+ rtf: "document",
426
+ epub: "document",
427
+ pages: "document",
428
+ numbers: "document",
429
+ key: "document",
430
+ // text
431
+ txt: "text",
432
+ log: "text",
433
+ env: "text",
434
+ json: "text",
435
+ jsonc: "text",
436
+ xml: "text",
437
+ yaml: "text",
438
+ yml: "text",
439
+ toml: "text",
440
+ ini: "text",
441
+ conf: "text",
442
+ tex: "text",
443
+ rst: "text",
444
+ adoc: "text",
445
+ // code
446
+ js: "code",
447
+ mjs: "code",
448
+ cjs: "code",
449
+ ts: "code",
450
+ tsx: "code",
451
+ jsx: "code",
452
+ py: "code",
453
+ rb: "code",
454
+ go: "code",
455
+ rs: "code",
456
+ java: "code",
457
+ kt: "code",
458
+ swift: "code",
459
+ c: "code",
460
+ h: "code",
461
+ cpp: "code",
462
+ hpp: "code",
463
+ cs: "code",
464
+ php: "code",
465
+ sh: "code",
466
+ bash: "code",
467
+ zsh: "code",
468
+ fish: "code",
469
+ ps1: "code",
470
+ lua: "code",
471
+ r: "code",
472
+ sql: "code",
473
+ css: "code",
474
+ scss: "code",
475
+ sass: "code",
476
+ less: "code",
477
+ vue: "code",
478
+ svelte: "code",
479
+ dockerfile: "code",
480
+ gitignore: "code",
481
+ gitattributes: "code",
482
+ // font
483
+ ttf: "font",
484
+ otf: "font",
485
+ woff: "font",
486
+ woff2: "font",
487
+ eot: "font",
488
+ // archive
489
+ zip: "archive",
490
+ tar: "archive",
491
+ gz: "archive",
492
+ tgz: "archive",
493
+ bz2: "archive",
494
+ tbz2: "archive",
495
+ xz: "archive",
496
+ txz: "archive",
497
+ "7z": "archive",
498
+ rar: "archive",
499
+ zst: "archive",
500
+ // data
501
+ csv: "data",
502
+ tsv: "data",
503
+ parquet: "data",
504
+ arrow: "data",
505
+ feather: "data",
506
+ sqlite: "data",
507
+ sqlite3: "data",
508
+ db: "data",
509
+ jsonl: "data",
510
+ ndjson: "data",
511
+ hdf5: "data",
512
+ h5: "data",
513
+ orc: "data",
514
+ // model3d
515
+ obj: "model3d",
516
+ stl: "model3d",
517
+ gltf: "model3d",
518
+ glb: "model3d",
519
+ fbx: "model3d",
520
+ dae: "model3d",
521
+ "3ds": "model3d",
522
+ blend: "model3d",
523
+ ply: "model3d",
524
+ usd: "model3d",
525
+ usdz: "model3d"
526
+ };
527
+ var CATEGORY_IDS = CATEGORIES.map((c) => c.id);
528
+ var APP_GRANTABLE_CATEGORIES = CATEGORY_IDS.filter(
529
+ (c) => c !== "other"
530
+ );
531
+ var KNOWN_EXTENSIONS = new Set(Object.keys(EXTENSIONS));
532
+ function categoryOf(ext) {
533
+ const normalized = ext.toLowerCase().replace(/^\./, "");
534
+ return EXTENSIONS[normalized] ?? "other";
535
+ }
536
+ function getCategory(id) {
537
+ return CATEGORIES.find((c) => c.id === id);
538
+ }
539
+ function isCategoryId(id) {
540
+ return CATEGORIES.some((c) => c.id === id);
541
+ }
542
+ function pgColumnType(t) {
543
+ switch (t) {
544
+ case "integer":
545
+ return "integer";
546
+ case "bigint":
547
+ return "bigint";
548
+ case "real":
549
+ return "double precision";
550
+ case "text":
551
+ return "text";
552
+ case "timestamp":
553
+ return "timestamptz";
554
+ case "boolean":
555
+ return "boolean";
556
+ }
557
+ }
558
+ function sqliteColumnType(t) {
559
+ switch (t) {
560
+ case "integer":
561
+ return "INTEGER";
562
+ case "bigint":
563
+ return "INTEGER";
564
+ case "real":
565
+ return "REAL";
566
+ case "text":
567
+ return "TEXT";
568
+ case "timestamp":
569
+ return "TEXT";
570
+ case "boolean":
571
+ return "INTEGER";
572
+ }
573
+ }
574
+ function pgMetadataDdl(c) {
575
+ const cols = [
576
+ ` record_id text PRIMARY KEY`,
577
+ ...c.metadataColumns.map((col) => {
578
+ const nullSuffix = col.nullable === false ? " NOT NULL" : "";
579
+ return ` ${col.name} ${pgColumnType(col.type)}${nullSuffix}`;
580
+ })
581
+ ];
582
+ return `CREATE TABLE IF NOT EXISTS ${pgMetadataTableName(c.id)} (
583
+ ${cols.join(",\n")}
584
+ )`;
585
+ }
586
+ function sqliteMetadataDdl(c) {
587
+ const cols = [
588
+ ` record_id TEXT PRIMARY KEY`,
589
+ ...c.metadataColumns.map((col) => {
590
+ const nullSuffix = col.nullable === false ? " NOT NULL" : "";
591
+ return ` ${col.name} ${sqliteColumnType(col.type)}${nullSuffix}`;
592
+ })
593
+ ];
594
+ return `CREATE TABLE IF NOT EXISTS ${sqliteMetadataTableName(c.id)} (
595
+ ${cols.join(",\n")}
596
+ )`;
597
+ }
598
+ function sqliteMetadataTableName(typeOrCategory) {
599
+ const category = isCategoryId(typeOrCategory) ? typeOrCategory : categoryOf(typeOrCategory);
600
+ return `shared_record_${category}_metadata`;
601
+ }
602
+ function pgMetadataTableName(typeOrCategory) {
603
+ const category = isCategoryId(typeOrCategory) ? typeOrCategory : categoryOf(typeOrCategory);
604
+ return `shared.record_${category}_metadata`;
605
+ }
606
+
607
+ // src/storage/object-keys.ts
608
+ function dataRecordObjectKey(typeOrExt, contentHash) {
609
+ const shard = contentHash.slice(0, 2);
610
+ return `shared/${categoryOf(typeOrExt)}/${shard}/${contentHash}`;
611
+ }
612
+ function appSyncableObjectKey(appId, subKey) {
613
+ if (!appId || /[/\s]/.test(appId)) {
614
+ throw new Error(`appSyncableObjectKey: invalid appId ${JSON.stringify(appId)}`);
615
+ }
616
+ const prefix = `apps/${appId}/syncable/`;
617
+ const relative = subKey.startsWith(prefix) ? subKey.slice(prefix.length) : subKey;
618
+ if (relative.startsWith("/")) {
619
+ throw new Error(`appSyncableObjectKey: subKey must not start with "/" (got ${JSON.stringify(subKey)})`);
620
+ }
621
+ const segments = relative.split("/");
622
+ if (segments.some((s) => s === "..")) {
623
+ throw new Error(`appSyncableObjectKey: subKey must not contain ".." (got ${JSON.stringify(subKey)})`);
624
+ }
625
+ return `${prefix}${relative}`;
626
+ }
627
+ //# sourceMappingURL=index.cjs.map