@x12i/catalox 4.2.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/src/catalox/catalox.d.ts +3 -4
  2. package/dist/src/catalox/catalox.d.ts.map +1 -1
  3. package/dist/src/catalox/catalox.js +108 -154
  4. package/dist/src/catalox/catalox.js.map +1 -1
  5. package/dist/src/catalox/item-scope-match.d.ts +39 -0
  6. package/dist/src/catalox/item-scope-match.d.ts.map +1 -0
  7. package/dist/src/catalox/item-scope-match.js +114 -0
  8. package/dist/src/catalox/item-scope-match.js.map +1 -0
  9. package/dist/src/catalox/item-scope.d.ts +36 -0
  10. package/dist/src/catalox/item-scope.d.ts.map +1 -0
  11. package/dist/src/catalox/item-scope.js +137 -0
  12. package/dist/src/catalox/item-scope.js.map +1 -0
  13. package/dist/src/catalox/native-catalog-merge.d.ts +7 -9
  14. package/dist/src/catalox/native-catalog-merge.d.ts.map +1 -1
  15. package/dist/src/catalox/native-catalog-merge.js +23 -106
  16. package/dist/src/catalox/native-catalog-merge.js.map +1 -1
  17. package/dist/src/catalox/scope-legacy-convert.d.ts +19 -0
  18. package/dist/src/catalox/scope-legacy-convert.d.ts.map +1 -0
  19. package/dist/src/catalox/scope-legacy-convert.js +177 -0
  20. package/dist/src/catalox/scope-legacy-convert.js.map +1 -0
  21. package/dist/src/cli/index.js +94 -2
  22. package/dist/src/cli/index.js.map +1 -1
  23. package/dist/src/contracts/catalogs.d.ts +21 -25
  24. package/dist/src/contracts/catalogs.d.ts.map +1 -1
  25. package/dist/src/contracts/catalogs.js.map +1 -1
  26. package/dist/src/contracts/context.d.ts +9 -4
  27. package/dist/src/contracts/context.d.ts.map +1 -1
  28. package/dist/src/contracts/index.d.ts +1 -1
  29. package/dist/src/contracts/index.d.ts.map +1 -1
  30. package/dist/src/contracts/index.js.map +1 -1
  31. package/dist/src/contracts/items.d.ts +8 -51
  32. package/dist/src/contracts/items.d.ts.map +1 -1
  33. package/dist/src/firebase/native-item-store.d.ts +2 -1
  34. package/dist/src/firebase/native-item-store.d.ts.map +1 -1
  35. package/dist/src/firebase/native-item-store.js +2 -5
  36. package/dist/src/firebase/native-item-store.js.map +1 -1
  37. package/dist/src/migrations/index.d.ts +1 -0
  38. package/dist/src/migrations/index.d.ts.map +1 -1
  39. package/dist/src/migrations/index.js +1 -0
  40. package/dist/src/migrations/index.js.map +1 -1
  41. package/dist/src/migrations/migrate-native-item-scope.d.ts +36 -0
  42. package/dist/src/migrations/migrate-native-item-scope.d.ts.map +1 -0
  43. package/dist/src/migrations/migrate-native-item-scope.js +204 -0
  44. package/dist/src/migrations/migrate-native-item-scope.js.map +1 -0
  45. package/package.json +4 -2
  46. package/dist/src/catalox/native-scope.d.ts +0 -29
  47. package/dist/src/catalox/native-scope.d.ts.map +0 -1
  48. package/dist/src/catalox/native-scope.js +0 -324
  49. package/dist/src/catalox/native-scope.js.map +0 -1
@@ -0,0 +1,177 @@
1
+ const CX = "cx~";
2
+ const V2 = "v2";
3
+ function b64uDec(s) {
4
+ return Buffer.from(s, "base64url").toString("utf8");
5
+ }
6
+ /** Decode legacy `cx~` storage document ids (migration only). */
7
+ export function decodeNativeItemStorageDocId(docId) {
8
+ if (!docId.startsWith(CX)) {
9
+ return { logicalItemId: docId, legacyScope: { kind: "global" } };
10
+ }
11
+ const rest = docId.slice(CX.length);
12
+ const parts = rest.split("~");
13
+ if (parts[0] === V2) {
14
+ const last = parts[parts.length - 1];
15
+ if (!last)
16
+ return null;
17
+ const logicalItemId = b64uDec(last);
18
+ const scoped = { kind: "scoped", tenant: {}, agent: {} };
19
+ for (let i = 1; i < parts.length - 1; i += 2) {
20
+ const k = parts[i];
21
+ const v = parts[i + 1];
22
+ if (!k || !v)
23
+ break;
24
+ const dec = b64uDec(v);
25
+ const t = scoped.tenant;
26
+ const a = scoped.agent;
27
+ if (k === "ac")
28
+ t.accountId = dec;
29
+ else if (k === "g")
30
+ t.groupId = dec;
31
+ else if (k === "u")
32
+ t.userId = dec;
33
+ else if (k === "ch")
34
+ t.channelId = dec;
35
+ else if (k === "v")
36
+ t.visitorId = dec;
37
+ else if (k === "d")
38
+ a.domainId = dec;
39
+ else if (k === "ag")
40
+ a.agentId = dec;
41
+ }
42
+ return { logicalItemId, legacyScope: scoped };
43
+ }
44
+ if (parts[0] === "a" && parts.length === 3) {
45
+ return { logicalItemId: b64uDec(parts[2]), legacyScope: { kind: "account", accountId: b64uDec(parts[1]) } };
46
+ }
47
+ if (parts[0] === "ag" && parts.length === 4) {
48
+ return {
49
+ logicalItemId: b64uDec(parts[3]),
50
+ legacyScope: { kind: "agent", accountId: b64uDec(parts[1]), agentId: b64uDec(parts[2]) },
51
+ };
52
+ }
53
+ if (parts[0] === "u" && parts.length === 4) {
54
+ return {
55
+ logicalItemId: b64uDec(parts[3]),
56
+ legacyScope: { kind: "user", accountId: b64uDec(parts[1]), userId: b64uDec(parts[2]) },
57
+ };
58
+ }
59
+ return null;
60
+ }
61
+ function arr1(v) {
62
+ if (v == null || String(v).trim() === "")
63
+ return undefined;
64
+ return [String(v).trim()];
65
+ }
66
+ function unionDim(target, dim, vals) {
67
+ const prev = target[dim] ?? [];
68
+ target[dim] = [...new Set([...prev, ...vals])];
69
+ }
70
+ function normalizeStringArray(raw) {
71
+ if (!Array.isArray(raw))
72
+ return [];
73
+ return [...new Set(raw.filter((x) => typeof x === "string" && x.trim() !== "").map((s) => s.trim()))];
74
+ }
75
+ /**
76
+ * Convert legacy stored scope + metadata tags to flexible scope (migration / allowLegacy reads).
77
+ */
78
+ export function convertLegacyScopeToFlexible(scope, metadata) {
79
+ const out = {};
80
+ const meta = metadata ?? {};
81
+ const domainIds = normalizeStringArray(meta.domainIds);
82
+ const agentIds = normalizeStringArray(meta.agentIds);
83
+ if (domainIds.length)
84
+ unionDim(out, "domains", domainIds);
85
+ if (agentIds.length)
86
+ unionDim(out, "agents", agentIds);
87
+ if (scope == null || typeof scope !== "object") {
88
+ return out;
89
+ }
90
+ const o = scope;
91
+ const kind = o.kind;
92
+ if (kind === "global" || kind == null) {
93
+ return out;
94
+ }
95
+ if (kind === "scoped") {
96
+ const tenant = o.tenant;
97
+ const agent = o.agent;
98
+ if (tenant) {
99
+ const ac = arr1(tenant.accountId);
100
+ if (ac)
101
+ unionDim(out, "accounts", ac);
102
+ const g = arr1(tenant.groupId);
103
+ if (g)
104
+ unionDim(out, "groups", g);
105
+ const u = arr1(tenant.userId);
106
+ if (u)
107
+ unionDim(out, "users", u);
108
+ const ch = arr1(tenant.channelId);
109
+ if (ch)
110
+ unionDim(out, "channels", ch);
111
+ const v = arr1(tenant.visitorId);
112
+ if (v)
113
+ unionDim(out, "visitors", v);
114
+ }
115
+ if (agent) {
116
+ const d = arr1(agent.domainId);
117
+ if (d)
118
+ unionDim(out, "domains", d);
119
+ const ag = arr1(agent.agentId);
120
+ if (ag)
121
+ unionDim(out, "agents", ag);
122
+ }
123
+ return out;
124
+ }
125
+ if (kind === "account" && typeof o.accountId === "string") {
126
+ unionDim(out, "accounts", [o.accountId]);
127
+ unionDim(out, "agents", normalizeStringArray(o.agentIds));
128
+ unionDim(out, "users", normalizeStringArray(o.userIds));
129
+ return out;
130
+ }
131
+ if (kind === "agent" && typeof o.accountId === "string" && typeof o.agentId === "string") {
132
+ unionDim(out, "accounts", [o.accountId]);
133
+ unionDim(out, "agents", [o.agentId]);
134
+ return out;
135
+ }
136
+ if (kind === "user" && typeof o.accountId === "string" && typeof o.userId === "string") {
137
+ unionDim(out, "accounts", [o.accountId]);
138
+ unionDim(out, "users", [o.userId]);
139
+ return out;
140
+ }
141
+ return out;
142
+ }
143
+ export function applyAgentScopeMapping(scope, accountId, agentId, mapping) {
144
+ if (!mapping)
145
+ return scope;
146
+ const key = `${accountId}:${agentId}`;
147
+ const m = mapping[key];
148
+ if (!m?.domains?.length)
149
+ return scope;
150
+ const out = { ...scope };
151
+ unionDim(out, "domains", m.domains);
152
+ if (m.agents?.length)
153
+ unionDim(out, "agents", m.agents);
154
+ return out;
155
+ }
156
+ /** Stable fingerprint for migration payload equivalence checks. */
157
+ export function payloadFingerprint(data) {
158
+ return JSON.stringify(data, Object.keys(data).sort());
159
+ }
160
+ export function unionFlexibleScopes(scopes) {
161
+ const out = {};
162
+ for (const s of scopes) {
163
+ for (const [dim, vals] of Object.entries(normalizeFlexibleOnly(s))) {
164
+ unionDim(out, dim, vals);
165
+ }
166
+ }
167
+ return out;
168
+ }
169
+ function normalizeFlexibleOnly(scope) {
170
+ const out = {};
171
+ for (const [k, v] of Object.entries(scope)) {
172
+ if (v?.length)
173
+ out[k] = [...new Set(v)];
174
+ }
175
+ return out;
176
+ }
177
+ //# sourceMappingURL=scope-legacy-convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-legacy-convert.js","sourceRoot":"","sources":["../../../src/catalox/scope-legacy-convert.ts"],"names":[],"mappings":"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"}
@@ -24,16 +24,37 @@ function resolvedStoreId(cliStore) {
24
24
  const id = (cliStore ?? process.env.CATALOX_STORE_ID ?? "").trim();
25
25
  return id || undefined;
26
26
  }
27
+ function parseScopeJson(raw) {
28
+ const t = raw?.trim();
29
+ if (!t)
30
+ return undefined;
31
+ const parsed = JSON.parse(t);
32
+ if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
33
+ throw new Error("scope JSON must be an object of string arrays");
34
+ }
35
+ const out = {};
36
+ for (const [k, v] of Object.entries(parsed)) {
37
+ if (!Array.isArray(v))
38
+ throw new Error(`scope.${k} must be a string array`);
39
+ out[k] = v.filter((x) => typeof x === "string" && x.trim() !== "");
40
+ }
41
+ return out;
42
+ }
27
43
  function baseContext(opts) {
28
44
  const appId = (opts.app ?? process.env.CATALOX_APP_ID ?? "").trim();
29
45
  if (!appId)
30
46
  throw new Error("Missing --app or CATALOX_APP_ID");
31
47
  const userId = (process.env.CATALOX_USER_ID ?? "").trim() || undefined;
32
48
  const storeId = resolvedStoreId(opts.store);
49
+ const scope = parseScopeJson(opts.scopeJson) ??
50
+ parseScopeJson(process.env.CATALOX_SCOPE_JSON) ??
51
+ undefined;
33
52
  return {
34
53
  appId,
35
54
  ...(storeId ? { storeId } : {}),
36
55
  ...(opts.god ? { superAdmin: true } : {}),
56
+ ...(opts.unrestricted ? { unrestrictedScope: true } : {}),
57
+ ...(scope ? { scope } : {}),
37
58
  ...(userId ? { userId, actor: { type: "user", id: userId } } : {}),
38
59
  };
39
60
  }
@@ -441,16 +462,35 @@ itemsCmd
441
462
  .requiredOption("--catalog <catalogId>", "Catalog id")
442
463
  .requiredOption("--item <itemId>", "Logical item id")
443
464
  .option("--storage-doc-id <id>", "Native: target a specific physical storage doc id")
465
+ .option("--scope-json <json>", "Fetch scope narrowing (JSON object of string arrays)")
466
+ .option("--scoped-only", "Exclude generic items", false)
467
+ .option("--generic-only", "Return only generic items", false)
444
468
  .option("--out <path>", "Write JSON to file instead of stdout")
445
469
  .option("--god", "God mode", false)
470
+ .option("--scope-json-token <json>", "Token scope on context (defaults to CATALOX_SCOPE_JSON)")
446
471
  .action(async (opts) => {
447
472
  const catalox = createCatalox();
448
- const ctx = baseContext({ app: opts.app, store: opts.store, god: opts.god });
473
+ const fetchScope = parseScopeJson(opts.scopeJson != null ? String(opts.scopeJson) : undefined);
474
+ const ctx = baseContext({
475
+ app: opts.app,
476
+ store: opts.store,
477
+ god: opts.god,
478
+ ...(opts.scopeJsonToken != null ? { scopeJson: String(opts.scopeJsonToken) } : {}),
479
+ });
449
480
  const catalogId = String(opts.catalog);
450
481
  const itemId = String(opts.item);
451
482
  const storageDocId = opts.storageDocId != null && String(opts.storageDocId).trim() ? String(opts.storageDocId).trim() : undefined;
452
483
  const got = await catalox.getCatalogItem(ctx, catalogId, itemId, {
453
484
  ...(storageDocId ? { storageDocId } : {}),
485
+ ...(fetchScope || opts.scopedOnly || opts.genericOnly
486
+ ? {
487
+ scope: {
488
+ ...(fetchScope ? { scope: fetchScope } : {}),
489
+ ...(opts.scopedOnly ? { scopedOnly: true } : {}),
490
+ ...(opts.genericOnly ? { genericOnly: true } : {}),
491
+ },
492
+ }
493
+ : {}),
454
494
  });
455
495
  await writeOrStdout(JSON.stringify(got, null, 2), opts.out);
456
496
  if (got.outcome !== "found")
@@ -466,11 +506,22 @@ itemsCmd
466
506
  .option("--offset <n>", "Offset (native)", (v) => Number(v), 0)
467
507
  .option("--cursor <token>", "Mapped catalogs: pass nextCursor from a prior response")
468
508
  .option("--filter-json <json>", "JSON object merged into list filter (compact empty strings ignored by server)")
509
+ .option("--scope-json <json>", "Fetch scope narrowing (JSON object of string arrays)")
510
+ .option("--scope-json-token <json>", "Token scope on context (defaults to CATALOX_SCOPE_JSON)")
511
+ .option("--scoped-only", "Exclude generic items", false)
512
+ .option("--generic-only", "Return only generic items", false)
513
+ .option("--no-generic", "Same as --scoped-only", false)
469
514
  .option("--out <path>", "Write JSON to file instead of stdout")
470
515
  .option("--god", "God mode", false)
471
516
  .action(async (opts) => {
472
517
  const catalox = createCatalox();
473
- const ctx = baseContext({ app: opts.app, store: opts.store, god: opts.god });
518
+ const fetchScope = parseScopeJson(opts.scopeJson != null ? String(opts.scopeJson) : undefined);
519
+ const ctx = baseContext({
520
+ app: opts.app,
521
+ store: opts.store,
522
+ god: opts.god,
523
+ ...(opts.scopeJsonToken != null ? { scopeJson: String(opts.scopeJsonToken) } : {}),
524
+ });
474
525
  const catalogId = String(opts.catalog);
475
526
  let filter;
476
527
  const fj = opts.filterJson != null ? String(opts.filterJson).trim() : "";
@@ -481,9 +532,19 @@ itemsCmd
481
532
  }
482
533
  filter = parsed;
483
534
  }
535
+ const scopedOnly = Boolean(opts.scopedOnly || opts.noGeneric);
484
536
  const res = await catalox.listCatalogItemsWithOutcome(ctx, catalogId, {
485
537
  limit: opts.limit,
486
538
  offset: opts.offset,
539
+ ...(fetchScope || scopedOnly || opts.genericOnly
540
+ ? {
541
+ scope: {
542
+ ...(fetchScope ? { scope: fetchScope } : {}),
543
+ ...(scopedOnly ? { scopedOnly: true } : {}),
544
+ ...(opts.genericOnly ? { genericOnly: true } : {}),
545
+ },
546
+ }
547
+ : {}),
487
548
  ...(filter ? { filter } : {}),
488
549
  ...(opts.cursor != null && String(opts.cursor).trim()
489
550
  ? { cursor: String(opts.cursor).trim() }
@@ -873,6 +934,37 @@ firestoreCmd
873
934
  if (!m.ok)
874
935
  process.exitCode = 1;
875
936
  });
937
+ firestoreCmd
938
+ .command("migrate-native-item-scope")
939
+ .description("One-time: convert legacy/native scope shapes to flexible scope maps (domains[], agents[], …)")
940
+ .requiredOption("--app <appId>", "AppId for Catalox context")
941
+ .option("--dry-run", "Report only; no writes", false)
942
+ .option("--verify-only", "Fail if any legacy scope shapes remain", false)
943
+ .option("--i-have-backups", "Required to apply writes (not dry-run/verify-only)", false)
944
+ .option("--continue-on-error", "Continue other catalogs if one fails", false)
945
+ .option("--catalog <catalogId...>", "Limit to catalog ids")
946
+ .option("--scope-mapping <path>", "JSON map accountId:agentId → { domains, agents }")
947
+ .option("--also-export-files", "Write NDJSON under .catalox-migration-backups/", false)
948
+ .option("--god", "God mode", false)
949
+ .action(async (opts) => {
950
+ const catalox = createCatalox();
951
+ const ctx = baseContext({ app: opts.app, god: opts.god });
952
+ const exportDir = opts.alsoExportFiles
953
+ ? resolvePath(process.cwd(), ".catalox-migration-backups")
954
+ : undefined;
955
+ const m = await catalox.migrateNativeItemScope(ctx, {
956
+ dryRun: Boolean(opts.dryRun),
957
+ verifyOnly: Boolean(opts.verifyOnly),
958
+ iHaveBackups: Boolean(opts.iHaveBackups),
959
+ continueOnError: Boolean(opts.continueOnError),
960
+ ...(opts.catalog?.length ? { catalogIds: opts.catalog } : {}),
961
+ ...(opts.scopeMapping ? { scopeMappingPath: String(opts.scopeMapping) } : {}),
962
+ ...(exportDir ? { exportFilesDir: exportDir } : {}),
963
+ });
964
+ await writeOrStdout(JSON.stringify({ phase: "migrate-native-item-scope", result: m }, null, 2));
965
+ if (!m.ok)
966
+ process.exitCode = 1;
967
+ });
876
968
  firestoreCmd
877
969
  .command("export-gcs")
878
970
  .description("Export Firestore collection(s) to a GCS bucket as NDJSON + manifest (for --all). Uses Application Default Credentials for GCS.")