@x12i/catalox 4.2.1 → 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.
- package/dist/src/catalox/catalox.d.ts +3 -4
- package/dist/src/catalox/catalox.d.ts.map +1 -1
- package/dist/src/catalox/catalox.js +108 -154
- package/dist/src/catalox/catalox.js.map +1 -1
- package/dist/src/catalox/item-scope-match.d.ts +39 -0
- package/dist/src/catalox/item-scope-match.d.ts.map +1 -0
- package/dist/src/catalox/item-scope-match.js +114 -0
- package/dist/src/catalox/item-scope-match.js.map +1 -0
- package/dist/src/catalox/item-scope.d.ts +36 -0
- package/dist/src/catalox/item-scope.d.ts.map +1 -0
- package/dist/src/catalox/item-scope.js +137 -0
- package/dist/src/catalox/item-scope.js.map +1 -0
- package/dist/src/catalox/native-catalog-merge.d.ts +7 -9
- package/dist/src/catalox/native-catalog-merge.d.ts.map +1 -1
- package/dist/src/catalox/native-catalog-merge.js +23 -106
- package/dist/src/catalox/native-catalog-merge.js.map +1 -1
- package/dist/src/catalox/scope-legacy-convert.d.ts +19 -0
- package/dist/src/catalox/scope-legacy-convert.d.ts.map +1 -0
- package/dist/src/catalox/scope-legacy-convert.js +177 -0
- package/dist/src/catalox/scope-legacy-convert.js.map +1 -0
- package/dist/src/cli/index.js +94 -2
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/contracts/catalogs.d.ts +21 -25
- package/dist/src/contracts/catalogs.d.ts.map +1 -1
- package/dist/src/contracts/catalogs.js.map +1 -1
- package/dist/src/contracts/context.d.ts +9 -4
- package/dist/src/contracts/context.d.ts.map +1 -1
- package/dist/src/contracts/index.d.ts +1 -1
- package/dist/src/contracts/index.d.ts.map +1 -1
- package/dist/src/contracts/index.js.map +1 -1
- package/dist/src/contracts/items.d.ts +8 -51
- package/dist/src/contracts/items.d.ts.map +1 -1
- package/dist/src/firebase/native-item-store.d.ts +2 -1
- package/dist/src/firebase/native-item-store.d.ts.map +1 -1
- package/dist/src/firebase/native-item-store.js +2 -5
- package/dist/src/firebase/native-item-store.js.map +1 -1
- package/dist/src/migrations/index.d.ts +1 -0
- package/dist/src/migrations/index.d.ts.map +1 -1
- package/dist/src/migrations/index.js +1 -0
- package/dist/src/migrations/index.js.map +1 -1
- package/dist/src/migrations/migrate-native-item-scope.d.ts +36 -0
- package/dist/src/migrations/migrate-native-item-scope.d.ts.map +1 -0
- package/dist/src/migrations/migrate-native-item-scope.js +204 -0
- package/dist/src/migrations/migrate-native-item-scope.js.map +1 -0
- package/package.json +1 -1
- package/dist/src/catalox/native-scope.d.ts +0 -29
- package/dist/src/catalox/native-scope.d.ts.map +0 -1
- package/dist/src/catalox/native-scope.js +0 -324
- package/dist/src/catalox/native-scope.js.map +0 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { isGenericScope, normalizeItemScope } from "./item-scope.js";
|
|
2
|
+
function intersectArrays(a, b) {
|
|
3
|
+
const set = new Set(b);
|
|
4
|
+
return a.filter((x) => set.has(x));
|
|
5
|
+
}
|
|
6
|
+
function unionScopeKeys(...scopes) {
|
|
7
|
+
const keys = new Set();
|
|
8
|
+
for (const s of scopes) {
|
|
9
|
+
for (const k of Object.keys(s))
|
|
10
|
+
keys.add(k);
|
|
11
|
+
}
|
|
12
|
+
return [...keys];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Merge token scope (authorization boundary) with fetch scope (request narrowing).
|
|
16
|
+
* Fetch never expands beyond token; shared dimensions are intersected.
|
|
17
|
+
*/
|
|
18
|
+
export function mergeEffectiveScope(tokenScope, fetchScope, opts) {
|
|
19
|
+
const token = normalizeItemScope(tokenScope ?? {});
|
|
20
|
+
const fetch = normalizeItemScope(fetchScope ?? {});
|
|
21
|
+
const unrestricted = opts?.unrestricted === true;
|
|
22
|
+
if (unrestricted) {
|
|
23
|
+
const effectiveScope = {};
|
|
24
|
+
for (const dim of unionScopeKeys(token, fetch)) {
|
|
25
|
+
const t = token[dim];
|
|
26
|
+
const f = fetch[dim];
|
|
27
|
+
if (t?.length && f?.length) {
|
|
28
|
+
const inter = intersectArrays(t, f);
|
|
29
|
+
if (inter.length)
|
|
30
|
+
effectiveScope[dim] = inter;
|
|
31
|
+
else
|
|
32
|
+
effectiveScope[dim] = [];
|
|
33
|
+
}
|
|
34
|
+
else if (f?.length)
|
|
35
|
+
effectiveScope[dim] = f;
|
|
36
|
+
else if (t?.length)
|
|
37
|
+
effectiveScope[dim] = t;
|
|
38
|
+
}
|
|
39
|
+
return { effectiveScope, scopedAccessAllowed: true };
|
|
40
|
+
}
|
|
41
|
+
const tokenEmpty = isGenericScope(token);
|
|
42
|
+
const fetchEmpty = isGenericScope(fetch);
|
|
43
|
+
if (tokenEmpty && fetchEmpty) {
|
|
44
|
+
return { effectiveScope: {}, scopedAccessAllowed: false };
|
|
45
|
+
}
|
|
46
|
+
if (tokenEmpty && !fetchEmpty) {
|
|
47
|
+
return { effectiveScope: {}, scopedAccessAllowed: false };
|
|
48
|
+
}
|
|
49
|
+
const effectiveScope = { ...token };
|
|
50
|
+
for (const dim of unionScopeKeys(token, fetch)) {
|
|
51
|
+
const t = token[dim];
|
|
52
|
+
const f = fetch[dim];
|
|
53
|
+
if (t?.length && f?.length) {
|
|
54
|
+
const inter = intersectArrays(t, f);
|
|
55
|
+
effectiveScope[dim] = inter;
|
|
56
|
+
}
|
|
57
|
+
else if (f?.length) {
|
|
58
|
+
effectiveScope[dim] = f;
|
|
59
|
+
}
|
|
60
|
+
else if (t?.length) {
|
|
61
|
+
effectiveScope[dim] = t;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const hasEmptyIntersection = Object.values(effectiveScope).some((v) => v.length === 0);
|
|
65
|
+
if (hasEmptyIntersection) {
|
|
66
|
+
return { effectiveScope, scopedAccessAllowed: false };
|
|
67
|
+
}
|
|
68
|
+
return { effectiveScope, scopedAccessAllowed: true };
|
|
69
|
+
}
|
|
70
|
+
export function itemMatchesEffectiveScope(itemScope, effectiveScope) {
|
|
71
|
+
const item = normalizeItemScope(itemScope);
|
|
72
|
+
const eff = normalizeItemScope(effectiveScope);
|
|
73
|
+
if (isGenericScope(eff))
|
|
74
|
+
return !isGenericScope(item);
|
|
75
|
+
if (isGenericScope(item))
|
|
76
|
+
return false;
|
|
77
|
+
for (const [dimension, requested] of Object.entries(eff)) {
|
|
78
|
+
if (!requested?.length)
|
|
79
|
+
continue;
|
|
80
|
+
const itemValues = item[dimension];
|
|
81
|
+
if (!itemValues?.length)
|
|
82
|
+
return false;
|
|
83
|
+
if (!requested.some((v) => itemValues.includes(v)))
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
export function resolveCatalogScopeFetchMode(options) {
|
|
89
|
+
const genericOnly = options?.genericOnly === true;
|
|
90
|
+
const scopedOnly = options?.scopedOnly === true || options?.includeGeneric === false;
|
|
91
|
+
const includeGeneric = genericOnly ? false : scopedOnly ? false : options?.includeGeneric !== false;
|
|
92
|
+
return { includeGeneric, genericOnly, scopedOnly };
|
|
93
|
+
}
|
|
94
|
+
export function shouldIncludeItemInList(params) {
|
|
95
|
+
const { itemScope, effectiveScope, scopedAccessAllowed, fetchMode } = params;
|
|
96
|
+
const item = normalizeItemScope(itemScope);
|
|
97
|
+
const generic = isGenericScope(item);
|
|
98
|
+
if (fetchMode.genericOnly)
|
|
99
|
+
return generic;
|
|
100
|
+
if (generic) {
|
|
101
|
+
return fetchMode.includeGeneric;
|
|
102
|
+
}
|
|
103
|
+
if (!scopedAccessAllowed)
|
|
104
|
+
return false;
|
|
105
|
+
return itemMatchesEffectiveScope(item, effectiveScope);
|
|
106
|
+
}
|
|
107
|
+
/** Build token scope from context.scope only (not singular legacy coords). */
|
|
108
|
+
export function tokenScopeFromContext(context) {
|
|
109
|
+
return normalizeItemScope(context.scope ?? {});
|
|
110
|
+
}
|
|
111
|
+
export function isUnrestrictedScopeContext(context) {
|
|
112
|
+
return context.superAdmin === true || context.unrestrictedScope === true;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=item-scope-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"item-scope-match.js","sourceRoot":"","sources":["../../../src/catalox/item-scope-match.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CatalogItemScope } from "../contracts/items.js";
|
|
2
|
+
/** True when scope has no dimensions (generic / catalog-wide item). */
|
|
3
|
+
export declare function isGenericScope(scope: CatalogItemScope | undefined | null): boolean;
|
|
4
|
+
/** Detect legacy native scope shapes (v1 kind / v2 scoped). */
|
|
5
|
+
export declare function isLegacyScopeShape(raw: unknown): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Normalize persisted or write input to canonical flexible scope.
|
|
8
|
+
* Rejects legacy shapes at runtime (post-migration contract).
|
|
9
|
+
*/
|
|
10
|
+
export declare function normalizeItemScope(raw: unknown, opts?: {
|
|
11
|
+
allowLegacy?: boolean;
|
|
12
|
+
}): CatalogItemScope;
|
|
13
|
+
export declare function parseWriteScopeInput(raw: unknown): CatalogItemScope | undefined;
|
|
14
|
+
/** Firestore document ids cannot contain `/`. */
|
|
15
|
+
export declare function itemIdNeedsEncodedStorageDocId(itemId: string): boolean;
|
|
16
|
+
/** Stable Firestore document id for a logical catalog item id. */
|
|
17
|
+
export declare function storageDocIdForItem(itemId: string): string;
|
|
18
|
+
/** Resolve logical itemId from a Firestore storage document id. */
|
|
19
|
+
export declare function logicalItemIdFromStorageDocId(docId: string): string;
|
|
20
|
+
export declare function storageDocIdForNativeRecord(record: {
|
|
21
|
+
itemId: string;
|
|
22
|
+
}): string;
|
|
23
|
+
/** Mirror non-empty scope dimensions under indexed.scope.<dimension> for queries. */
|
|
24
|
+
export declare function indexedScopeFields(scope: CatalogItemScope): Record<string, unknown>;
|
|
25
|
+
/** Sync optional metadata display copies from canonical scope (not used for visibility). */
|
|
26
|
+
export declare function metadataFromScope(scope: CatalogItemScope): {
|
|
27
|
+
domainIds: string[];
|
|
28
|
+
agentIds: string[];
|
|
29
|
+
};
|
|
30
|
+
export declare function scopeFromRecordField(scope: unknown): CatalogItemScope;
|
|
31
|
+
export declare function assertWriteScopeAllowed(context: {
|
|
32
|
+
superAdmin?: boolean;
|
|
33
|
+
unrestrictedScope?: boolean;
|
|
34
|
+
scope?: CatalogItemScope;
|
|
35
|
+
}, itemScope: CatalogItemScope): void;
|
|
36
|
+
//# sourceMappingURL=item-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"item-scope.d.ts","sourceRoot":"","sources":["../../../src/catalox/item-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAI9D,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAGlF;AAED,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAOxD;AAQD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,gBAAgB,CAuBnG;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS,CAG/E;AAaD,iDAAiD;AACjD,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGtE;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED,mEAAmE;AACnE,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAInE;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE9E;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOnF;AAED,4FAA4F;AAC5F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG;IAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAKtG;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAErE;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,gBAAgB,CAAA;CAAE,EACxF,SAAS,EAAE,gBAAgB,GAC1B,IAAI,CAmBN"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { CatalogValidationError } from "../contracts/errors.js";
|
|
2
|
+
import { convertLegacyScopeToFlexible } from "./scope-legacy-convert.js";
|
|
3
|
+
/** True when scope has no dimensions (generic / catalog-wide item). */
|
|
4
|
+
export function isGenericScope(scope) {
|
|
5
|
+
if (!scope || typeof scope !== "object")
|
|
6
|
+
return true;
|
|
7
|
+
return Object.keys(scope).length === 0;
|
|
8
|
+
}
|
|
9
|
+
/** Detect legacy native scope shapes (v1 kind / v2 scoped). */
|
|
10
|
+
export function isLegacyScopeShape(raw) {
|
|
11
|
+
if (raw == null || typeof raw !== "object")
|
|
12
|
+
return false;
|
|
13
|
+
const o = raw;
|
|
14
|
+
if ("kind" in o)
|
|
15
|
+
return true;
|
|
16
|
+
if ("tenant" in o || "agent" in o)
|
|
17
|
+
return true;
|
|
18
|
+
if (o.scopeLayer != null)
|
|
19
|
+
return true;
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
function normalizeStringArray(raw) {
|
|
23
|
+
if (!Array.isArray(raw))
|
|
24
|
+
return undefined;
|
|
25
|
+
const out = raw.filter((x) => typeof x === "string" && x.trim() !== "").map((s) => s.trim());
|
|
26
|
+
return out.length ? [...new Set(out)] : undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Normalize persisted or write input to canonical flexible scope.
|
|
30
|
+
* Rejects legacy shapes at runtime (post-migration contract).
|
|
31
|
+
*/
|
|
32
|
+
export function normalizeItemScope(raw, opts) {
|
|
33
|
+
if (raw == null)
|
|
34
|
+
return {};
|
|
35
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
36
|
+
throw new CatalogValidationError({ reason: "invalid_scope_shape", message: "scope must be an object" });
|
|
37
|
+
}
|
|
38
|
+
if (isLegacyScopeShape(raw)) {
|
|
39
|
+
if (!opts?.allowLegacy) {
|
|
40
|
+
throw new CatalogValidationError({
|
|
41
|
+
reason: "invalid_scope_shape",
|
|
42
|
+
message: "legacy scope shape is not supported; run migrate-native-item-scope",
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return convertLegacyScopeToFlexible(raw);
|
|
46
|
+
}
|
|
47
|
+
const o = raw;
|
|
48
|
+
const out = {};
|
|
49
|
+
for (const [key, val] of Object.entries(o)) {
|
|
50
|
+
if (key === "kind" || key === "tenant" || key === "agent" || key.startsWith("scope"))
|
|
51
|
+
continue;
|
|
52
|
+
const arr = normalizeStringArray(val);
|
|
53
|
+
if (arr?.length)
|
|
54
|
+
out[key] = arr;
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
export function parseWriteScopeInput(raw) {
|
|
59
|
+
if (raw == null)
|
|
60
|
+
return undefined;
|
|
61
|
+
return normalizeItemScope(raw);
|
|
62
|
+
}
|
|
63
|
+
const ID_ENC = "id~";
|
|
64
|
+
const LEGACY_MODEL_DOC = "cx~m~";
|
|
65
|
+
function b64u(s) {
|
|
66
|
+
return Buffer.from(s, "utf8").toString("base64url");
|
|
67
|
+
}
|
|
68
|
+
function b64uDec(s) {
|
|
69
|
+
return Buffer.from(s, "base64url").toString("utf8");
|
|
70
|
+
}
|
|
71
|
+
/** Firestore document ids cannot contain `/`. */
|
|
72
|
+
export function itemIdNeedsEncodedStorageDocId(itemId) {
|
|
73
|
+
const s = String(itemId);
|
|
74
|
+
return s.includes("/") || s.includes("\0") || s === "." || s === "..";
|
|
75
|
+
}
|
|
76
|
+
/** Stable Firestore document id for a logical catalog item id. */
|
|
77
|
+
export function storageDocIdForItem(itemId) {
|
|
78
|
+
const s = String(itemId);
|
|
79
|
+
if (!itemIdNeedsEncodedStorageDocId(s))
|
|
80
|
+
return s;
|
|
81
|
+
return `${ID_ENC}${b64u(s)}`;
|
|
82
|
+
}
|
|
83
|
+
/** Resolve logical itemId from a Firestore storage document id. */
|
|
84
|
+
export function logicalItemIdFromStorageDocId(docId) {
|
|
85
|
+
if (docId.startsWith(ID_ENC))
|
|
86
|
+
return b64uDec(docId.slice(ID_ENC.length));
|
|
87
|
+
if (docId.startsWith(LEGACY_MODEL_DOC))
|
|
88
|
+
return b64uDec(docId.slice(LEGACY_MODEL_DOC.length));
|
|
89
|
+
return docId;
|
|
90
|
+
}
|
|
91
|
+
export function storageDocIdForNativeRecord(record) {
|
|
92
|
+
return storageDocIdForItem(String(record.itemId));
|
|
93
|
+
}
|
|
94
|
+
/** Mirror non-empty scope dimensions under indexed.scope.<dimension> for queries. */
|
|
95
|
+
export function indexedScopeFields(scope) {
|
|
96
|
+
const out = { scopeGeneric: isGenericScope(scope) };
|
|
97
|
+
if (isGenericScope(scope))
|
|
98
|
+
return out;
|
|
99
|
+
for (const [dim, vals] of Object.entries(scope)) {
|
|
100
|
+
if (vals?.length)
|
|
101
|
+
out[`scope.${dim}`] = vals;
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
/** Sync optional metadata display copies from canonical scope (not used for visibility). */
|
|
106
|
+
export function metadataFromScope(scope) {
|
|
107
|
+
return {
|
|
108
|
+
domainIds: scope.domains ?? [],
|
|
109
|
+
agentIds: scope.agents ?? [],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export function scopeFromRecordField(scope) {
|
|
113
|
+
return normalizeItemScope(scope);
|
|
114
|
+
}
|
|
115
|
+
export function assertWriteScopeAllowed(context, itemScope) {
|
|
116
|
+
if (isGenericScope(itemScope))
|
|
117
|
+
return;
|
|
118
|
+
if (context.superAdmin || context.unrestrictedScope)
|
|
119
|
+
return;
|
|
120
|
+
const token = normalizeItemScope(context.scope ?? {});
|
|
121
|
+
if (isGenericScope(token)) {
|
|
122
|
+
throw new CatalogValidationError({
|
|
123
|
+
reason: "scope_write_denied",
|
|
124
|
+
message: "non-generic item scope requires token scope, superAdmin, or unrestrictedScope",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
for (const [dim, vals] of Object.entries(itemScope)) {
|
|
128
|
+
const allowed = token[dim] ?? [];
|
|
129
|
+
if (!vals.every((v) => allowed.includes(v))) {
|
|
130
|
+
throw new CatalogValidationError({
|
|
131
|
+
reason: "scope_write_denied",
|
|
132
|
+
message: `item scope dimension ${dim} exceeds token allowance`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=item-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"item-scope.js","sourceRoot":"","sources":["../../../src/catalox/item-scope.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import type { CatalogIdentityDescriptor } from "../contracts/descriptors.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { CatalogItemScope } from "../contracts/items.js";
|
|
3
3
|
import type { NativeCatalogItemRecord, UnifiedCatalogItem } from "../contracts/items.js";
|
|
4
|
-
export declare function toUnifiedNativeItem(r: NativeCatalogItemRecord, appId: string,
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export declare function
|
|
9
|
-
|
|
10
|
-
export declare function pickWinningPhysicalRow(rows: NativeCatalogItemRecord[], fetch?: CatalogFetchScope): NativeCatalogItemRecord | null;
|
|
11
|
-
export declare function mergeNativePhysicalRows(records: NativeCatalogItemRecord[], identity: CatalogIdentityDescriptor, appId: string, superAdminList: boolean): UnifiedCatalogItem[];
|
|
4
|
+
export declare function toUnifiedNativeItem(r: NativeCatalogItemRecord, appId: string, includeScopeInResponse: boolean): UnifiedCatalogItem;
|
|
5
|
+
/** One physical row per logical itemId after flexible scope migration. */
|
|
6
|
+
export declare function mergeNativePhysicalRows(records: NativeCatalogItemRecord[], _identity: CatalogIdentityDescriptor, appId: string, includeScopeInResponse: boolean, _superListAll?: boolean): UnifiedCatalogItem[];
|
|
7
|
+
export declare function pickWinningPhysicalRow(rows: NativeCatalogItemRecord[]): NativeCatalogItemRecord | null;
|
|
8
|
+
export declare function recordItemScope(r: NativeCatalogItemRecord): CatalogItemScope;
|
|
9
|
+
export declare function isGenericPhysicalRow(r: NativeCatalogItemRecord): boolean;
|
|
12
10
|
//# sourceMappingURL=native-catalog-merge.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-catalog-merge.d.ts","sourceRoot":"","sources":["../../../src/catalox/native-catalog-merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"native-catalog-merge.d.ts","sourceRoot":"","sources":["../../../src/catalox/native-catalog-merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAGzF,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,uBAAuB,EAC1B,KAAK,EAAE,MAAM,EACb,sBAAsB,EAAE,OAAO,GAC9B,kBAAkB,CAkBpB;AAED,0EAA0E;AAC1E,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,uBAAuB,EAAE,EAClC,SAAS,EAAE,yBAAyB,EACpC,KAAK,EAAE,MAAM,EACb,sBAAsB,EAAE,OAAO,EAC/B,aAAa,CAAC,EAAE,OAAO,GACtB,kBAAkB,EAAE,CAOtB;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,uBAAuB,EAAE,GAAG,uBAAuB,GAAG,IAAI,CAEtG;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,uBAAuB,GAAG,gBAAgB,CAE5E;AAED,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAExE"}
|
|
@@ -1,33 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
function
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
return 0;
|
|
6
|
-
if (s.kind === "scoped") {
|
|
7
|
-
const t = s.tenant;
|
|
8
|
-
const a = s.agent;
|
|
9
|
-
const count = Number(Boolean(t?.accountId)) +
|
|
10
|
-
Number(Boolean(t?.groupId)) +
|
|
11
|
-
Number(Boolean(t?.userId)) +
|
|
12
|
-
Number(Boolean(t?.channelId)) +
|
|
13
|
-
Number(Boolean(t?.visitorId)) +
|
|
14
|
-
Number(Boolean(a?.domainId)) +
|
|
15
|
-
Number(Boolean(a?.agentId));
|
|
16
|
-
// scoped rows are more specific than legacy account, but order within scoped is by specificity.
|
|
17
|
-
return 10 + count;
|
|
18
|
-
}
|
|
19
|
-
if (s.kind === "account")
|
|
20
|
-
return 1;
|
|
21
|
-
if (s.kind === "agent")
|
|
22
|
-
return 2;
|
|
23
|
-
return 3;
|
|
24
|
-
}
|
|
25
|
-
export function toUnifiedNativeItem(r, appId, includeStoredScope) {
|
|
1
|
+
import { isGenericScope, metadataFromScope, scopeFromRecordField } from "./item-scope.js";
|
|
2
|
+
export function toUnifiedNativeItem(r, appId, includeScopeInResponse) {
|
|
3
|
+
const itemScope = scopeFromRecordField(r.scope);
|
|
4
|
+
const metaDisplay = metadataFromScope(itemScope);
|
|
26
5
|
const meta = {
|
|
27
6
|
...(r.metadata && typeof r.metadata === "object" ? r.metadata : {}),
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
...(r.metadata?.agentIds == null ? { agentIds: [] } : {}),
|
|
7
|
+
domainIds: metaDisplay.domainIds,
|
|
8
|
+
agentIds: metaDisplay.agentIds,
|
|
31
9
|
};
|
|
32
10
|
return {
|
|
33
11
|
itemId: r.itemId,
|
|
@@ -36,88 +14,27 @@ export function toUnifiedNativeItem(r, appId, includeStoredScope) {
|
|
|
36
14
|
sourceMode: "native",
|
|
37
15
|
sourceType: "firebase",
|
|
38
16
|
data: r.data,
|
|
17
|
+
...(includeScopeInResponse ? { scope: itemScope } : {}),
|
|
39
18
|
metadata: meta,
|
|
40
19
|
};
|
|
41
20
|
}
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return true;
|
|
50
|
-
return physicalRowMatchesFetchScope(sc, fetch);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
/** For updates/deletes: choose the most specific physical row matching optional tenant fetch, else global. */
|
|
54
|
-
export function pickWinningPhysicalRow(rows, fetch) {
|
|
55
|
-
if (!rows.length)
|
|
56
|
-
return null;
|
|
57
|
-
const hasScope = Boolean(fetch?.accountId) ||
|
|
58
|
-
Boolean(fetch?.groupId) ||
|
|
59
|
-
Boolean(fetch?.userId) ||
|
|
60
|
-
Boolean(fetch?.channelId) ||
|
|
61
|
-
Boolean(fetch?.visitorId) ||
|
|
62
|
-
Boolean(fetch?.domainId) ||
|
|
63
|
-
Boolean(fetch?.agentId);
|
|
64
|
-
if (!hasScope) {
|
|
65
|
-
const g = rows.filter((r) => isGlobalPhysicalRow(r));
|
|
66
|
-
return g[0] ?? rows[0] ?? null;
|
|
21
|
+
/** One physical row per logical itemId after flexible scope migration. */
|
|
22
|
+
export function mergeNativePhysicalRows(records, _identity, appId, includeScopeInResponse, _superListAll) {
|
|
23
|
+
const byItem = new Map();
|
|
24
|
+
for (const r of records) {
|
|
25
|
+
const id = String(r.itemId);
|
|
26
|
+
if (!byItem.has(id))
|
|
27
|
+
byItem.set(id, r);
|
|
67
28
|
}
|
|
68
|
-
|
|
69
|
-
if (!matched.length)
|
|
70
|
-
return null;
|
|
71
|
-
const sorted = [...matched].sort((a, b) => layerRank(scopeFromRecordField(a.scope)) - layerRank(scopeFromRecordField(b.scope)));
|
|
72
|
-
return sorted[sorted.length - 1] ?? null;
|
|
29
|
+
return [...byItem.values()].map((r) => toUnifiedNativeItem(r, appId, includeScopeInResponse));
|
|
73
30
|
}
|
|
74
|
-
export function
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
const groups = new Map();
|
|
84
|
-
for (const r of sorted) {
|
|
85
|
-
const key = mergeIdentityKey(identity, r.data, r.indexed) ??
|
|
86
|
-
`${r.itemId}@${JSON.stringify(normalizeStoredScope(scopeFromRecordField(r.scope)))}`;
|
|
87
|
-
const g = groups.get(key);
|
|
88
|
-
if (g)
|
|
89
|
-
g.push(r);
|
|
90
|
-
else
|
|
91
|
-
groups.set(key, [r]);
|
|
92
|
-
}
|
|
93
|
-
const out = [];
|
|
94
|
-
for (const group of groups.values()) {
|
|
95
|
-
let data = {};
|
|
96
|
-
let itemId = group[0].itemId;
|
|
97
|
-
let catalogId = group[0].catalogId;
|
|
98
|
-
let createdAt = String(group[0].metadata?.createdAt ?? "");
|
|
99
|
-
let lastUpdate = String(group[0].metadata?.lastUpdate ?? "");
|
|
100
|
-
for (const r of group) {
|
|
101
|
-
data = shallowMergeData(data, r.data);
|
|
102
|
-
const rc = String(r.metadata?.createdAt ?? "");
|
|
103
|
-
const ru = String(r.metadata?.lastUpdate ?? "");
|
|
104
|
-
if (ru && (!lastUpdate || ru > lastUpdate))
|
|
105
|
-
lastUpdate = ru;
|
|
106
|
-
if (rc && (!createdAt || rc < createdAt))
|
|
107
|
-
createdAt = rc;
|
|
108
|
-
itemId = r.itemId;
|
|
109
|
-
catalogId = r.catalogId;
|
|
110
|
-
}
|
|
111
|
-
out.push({
|
|
112
|
-
itemId,
|
|
113
|
-
catalogId,
|
|
114
|
-
appId,
|
|
115
|
-
sourceMode: "native",
|
|
116
|
-
sourceType: "firebase",
|
|
117
|
-
data,
|
|
118
|
-
metadata: { createdAt, lastUpdate },
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
return out;
|
|
31
|
+
export function pickWinningPhysicalRow(rows) {
|
|
32
|
+
return rows[0] ?? null;
|
|
33
|
+
}
|
|
34
|
+
export function recordItemScope(r) {
|
|
35
|
+
return scopeFromRecordField(r.scope);
|
|
36
|
+
}
|
|
37
|
+
export function isGenericPhysicalRow(r) {
|
|
38
|
+
return isGenericScope(scopeFromRecordField(r.scope));
|
|
122
39
|
}
|
|
123
40
|
//# sourceMappingURL=native-catalog-merge.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-catalog-merge.js","sourceRoot":"","sources":["../../../src/catalox/native-catalog-merge.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"native-catalog-merge.js","sourceRoot":"","sources":["../../../src/catalox/native-catalog-merge.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE1F,MAAM,UAAU,mBAAmB,CACjC,CAA0B,EAC1B,KAAa,EACb,sBAA+B;IAE/B,MAAM,SAAS,GAAG,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,IAAI,GAA4B;QACpC,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,SAAS,EAAE,WAAW,CAAC,SAAS;QAChC,QAAQ,EAAE,WAAW,CAAC,QAAQ;KAC/B,CAAC;IACF,OAAO;QACL,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,KAAK;QACL,UAAU,EAAE,QAAQ;QACpB,UAAU,EAAE,UAAU;QACtB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,QAAQ,EAAE,IAAI;KACf,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,uBAAuB,CACrC,OAAkC,EAClC,SAAoC,EACpC,KAAa,EACb,sBAA+B,EAC/B,aAAuB;IAEvB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmC,CAAC;IAC1D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,EAAE,KAAK,EAAE,sBAAsB,CAAC,CAAC,CAAC;AAChG,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,IAA+B;IACpE,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,CAA0B;IACxD,OAAO,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,CAA0B;IAC7D,OAAO,cAAc,CAAC,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CatalogItemScope } from "../contracts/items.js";
|
|
2
|
+
/** Decode legacy `cx~` storage document ids (migration only). */
|
|
3
|
+
export declare function decodeNativeItemStorageDocId(docId: string): {
|
|
4
|
+
logicalItemId: string;
|
|
5
|
+
legacyScope: unknown;
|
|
6
|
+
} | null;
|
|
7
|
+
/**
|
|
8
|
+
* Convert legacy stored scope + metadata tags to flexible scope (migration / allowLegacy reads).
|
|
9
|
+
*/
|
|
10
|
+
export declare function convertLegacyScopeToFlexible(scope: unknown, metadata?: Record<string, unknown>): CatalogItemScope;
|
|
11
|
+
export type ScopeMappingFile = Record<string, {
|
|
12
|
+
domains?: string[];
|
|
13
|
+
agents?: string[];
|
|
14
|
+
}>;
|
|
15
|
+
export declare function applyAgentScopeMapping(scope: CatalogItemScope, accountId: string, agentId: string, mapping?: ScopeMappingFile): CatalogItemScope;
|
|
16
|
+
/** Stable fingerprint for migration payload equivalence checks. */
|
|
17
|
+
export declare function payloadFingerprint(data: Record<string, unknown>): string;
|
|
18
|
+
export declare function unionFlexibleScopes(scopes: CatalogItemScope[]): CatalogItemScope;
|
|
19
|
+
//# sourceMappingURL=scope-legacy-convert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-legacy-convert.d.ts","sourceRoot":"","sources":["../../../src/catalox/scope-legacy-convert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAS9D,iEAAiE;AACjE,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,GACZ;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA4CxD;AAiBD;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,OAAO,EACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,gBAAgB,CAgElB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AAEzF,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,gBAAgB,GACzB,gBAAgB,CASlB;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAExE;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,CAQhF"}
|