@narrative.io/data-collaboration-sdk-ts 2.64.1 → 2.66.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 (34) hide show
  1. package/README.md +137 -1
  2. package/build/collaboration-policy/core/filter-builder.d.ts +3 -0
  3. package/build/collaboration-policy/core/filter-builder.js +90 -0
  4. package/build/collaboration-policy/core/filter-utils.d.ts +6 -0
  5. package/build/collaboration-policy/core/filter-utils.js +49 -0
  6. package/build/collaboration-policy/core/policy-traverser.d.ts +4 -0
  7. package/build/collaboration-policy/core/policy-traverser.js +100 -0
  8. package/build/collaboration-policy/core/policy-validator.d.ts +11 -0
  9. package/build/collaboration-policy/core/policy-validator.js +148 -0
  10. package/build/collaboration-policy/core/sql-builder.d.ts +37 -0
  11. package/build/collaboration-policy/core/sql-builder.js +173 -0
  12. package/build/collaboration-policy/core/types.d.ts +24 -0
  13. package/build/collaboration-policy/core/types.js +1 -0
  14. package/build/collaboration-policy/index.d.ts +4 -0
  15. package/build/collaboration-policy/index.js +3 -0
  16. package/build/collaboration-policy/scripts/generate-policy-schema.d.ts +2 -0
  17. package/build/collaboration-policy/scripts/generate-policy-schema.js +4 -0
  18. package/build/collaboration-policy/types/collaboration-policy.d.ts +697 -0
  19. package/build/collaboration-policy/types/collaboration-policy.js +262 -0
  20. package/build/collaboration-policy/types/index.d.ts +2 -0
  21. package/build/collaboration-policy/types/index.js +1 -0
  22. package/build/collaboration-policy/useCollaborationPolicy.d.ts +8 -0
  23. package/build/collaboration-policy/useCollaborationPolicy.js +14 -0
  24. package/build/collaboration-policy/utils/path-helpers.d.ts +4 -0
  25. package/build/collaboration-policy/utils/path-helpers.js +39 -0
  26. package/build/collaboration-policy/utils/sql-helpers.d.ts +5 -0
  27. package/build/collaboration-policy/utils/sql-helpers.js +65 -0
  28. package/build/index.d.ts +1 -0
  29. package/build/index.js +1 -0
  30. package/build/jobs/types.d.ts +7 -0
  31. package/build/nql/AstParser.js +5 -4
  32. package/build/nql/NqlBuilder.js +1 -1
  33. package/build/nql/types.d.ts +13 -0
  34. package/package.json +16 -12
package/README.md CHANGED
@@ -313,4 +313,140 @@ try {
313
313
 
314
314
  ## API Reference
315
315
 
316
- For detailed API documentation, please visit the [Narrative.io API Documentation](https://api.narrative.dev).
316
+ For detailed API documentation, please visit the [Narrative.io API Documentation](https://api.narrative.dev).
317
+
318
+ ## Local Development: Use This SDK Locally in a Consumer App (No Publish Needed)
319
+
320
+ You can link this SDK into another repo (e.g., a Nuxt 3 app) and iterate without publishing to npm each time. Two supported flows:
321
+
322
+ - **Linked mode (fastest)** — symlink your local SDK into the app
323
+ - **Tarball mode (exact publish parity)** — install from a local `npm pack` tarball
324
+
325
+ Both approaches use the SDK’s **built output** in `build/`, exactly like the published package.
326
+
327
+ ### Prerequisites
328
+
329
+ - Node & npm
330
+ - This repo builds TypeScript to `build/`
331
+ - This repo includes `scripts/ensureBuild.js` to verify artifacts exist
332
+
333
+ ```bash
334
+ # In the SDK repo (this one)
335
+ npm i
336
+ npm run build # one-time; emits build/**
337
+ node scripts/ensureBuild.js
338
+ ```
339
+
340
+ ---
341
+
342
+ ### Option A — Linked Mode (Fastest Dev Feedback)
343
+
344
+ 1. **Keep the SDK building on save**
345
+ In the SDK repo (leave this running):
346
+ ```bash
347
+ npm run dev # tsc -w → continuously updates build/**
348
+ ```
349
+
350
+ 2. **Register the SDK globally**
351
+ In the SDK repo (new terminal):
352
+ ```bash
353
+ npm run link:global # runs ensureBuild + npm link
354
+ ```
355
+
356
+ 3. **Link into your consumer app**
357
+ In your app repo (e.g., Nuxt project):
358
+ ```bash
359
+ # optional safety if a link exists
360
+ npm unlink @narrative.io/data-collaboration-sdk-ts --no-save || true
361
+
362
+ # link the globally-registered SDK into this app’s node_modules
363
+ npm link @narrative.io/data-collaboration-sdk-ts
364
+ ```
365
+
366
+ 4. **Verify the app resolves to your local SDK**
367
+ ```bash
368
+ node -e "console.log(require.resolve('@narrative.io/data-collaboration-sdk-ts/package.json'))"
369
+ ```
370
+ You should see a path inside your SDK repo (not a versioned folder under the app’s `node_modules`).
371
+
372
+ 5. **Run your app**
373
+ ```bash
374
+ npm run dev
375
+ ```
376
+ Edit files under `SDK/src/**` → the watcher writes to `build/**` → your app hot-reloads.
377
+
378
+ **Revert to the registry version (in the app):**
379
+ ```bash
380
+ npm unlink @narrative.io/data-collaboration-sdk-ts --no-save
381
+ npm i
382
+ ```
383
+
384
+ ---
385
+
386
+ ### Option B — Tarball Mode (Exact Publish Parity)
387
+
388
+ 1. **Pack the SDK (same artifact you would publish)**
389
+ ```bash
390
+ # in the SDK repo
391
+ npm run build
392
+ npm pack # creates ./<name>-<version>.tgz
393
+ ```
394
+
395
+ 2. **Install the tarball in the app**
396
+ ```bash
397
+ # in the app repo
398
+ npm i /ABS/PATH/TO/<name>-<version>.tgz
399
+ npm run dev
400
+ ```
401
+
402
+ **Revert to a registry version (in the app):**
403
+ ```bash
404
+ npm i @narrative.io/data-collaboration-sdk-ts@<semver>
405
+ ```
406
+
407
+ ---
408
+
409
+ ### Troubleshooting (Nuxt 3 / Vite / SSR)
410
+
411
+ - **“Cannot use import statement outside a module” (SSR):** Inline the SDK so Nitro bundles it.
412
+ ```ts
413
+ // nuxt.config.ts
414
+ export default defineNuxtConfig({
415
+ nitro: { externals: { inline: ['@narrative.io/data-collaboration-sdk-ts'] } }
416
+ })
417
+ ```
418
+
419
+ - **Build artifacts not updating:** Ensure `npm run dev` is running in the SDK repo (TypeScript watch), and that the app is linked (or reinstalled from a fresh tarball).
420
+
421
+ - **Accidental imports from `src/*`:** Consumers should import from the package root only. This SDK publishes `build/**` and defines `main`/`types` (and may include an `exports` map) to prevent `src/*` imports.
422
+
423
+ - **Type/version drift (e.g., Zod):** If your app and the SDK use different major versions of a library whose types appear in public APIs, align versions or declare that library as a `peerDependency` in the SDK.
424
+
425
+ ---
426
+
427
+ ### Quick Reference
428
+
429
+ ```bash
430
+ # SDK (this repo)
431
+ npm i
432
+ npm run build
433
+ npm run dev
434
+ npm run link:global
435
+
436
+ # App (consumer repo)
437
+ npm unlink @narrative.io/data-collaboration-sdk-ts --no-save || true
438
+ npm link @narrative.io/data-collaboration-sdk-ts
439
+ npm run dev
440
+ ```
441
+
442
+ Alternative (publish-parity):
443
+
444
+ ```bash
445
+ # SDK
446
+ npm run build
447
+ npm pack
448
+
449
+ # App
450
+ npm i /ABS/PATH/TO/*.tgz
451
+ npm run dev
452
+ ```
@@ -0,0 +1,3 @@
1
+ import type { Attribute } from "../../attributes/types";
2
+ import type { PolicyFilter } from "./types";
3
+ export declare function filterToSql(filter: PolicyFilter, attributeByName: Map<string, Attribute>, datasetName: string): string;
@@ -0,0 +1,90 @@
1
+ import { normalizePathValue } from "../utils/path-helpers";
2
+ import { resolveAttributeExpression } from "../utils/sql-helpers";
3
+ import { isAttributeReference } from "./filter-utils";
4
+ export function filterToSql(filter, attributeByName, datasetName) {
5
+ const op = filter.op.toLowerCase();
6
+ switch (op) {
7
+ case "and":
8
+ case "or": {
9
+ const args = filter.args;
10
+ if (!Array.isArray(args) || args.length === 0) {
11
+ throw new Error(`Logical filter "${filter.op}" requires at least one argument.`);
12
+ }
13
+ const pieces = args.map((arg) => expressionToSql(arg, attributeByName, datasetName));
14
+ const joiner = op === "and" ? " AND " : " OR ";
15
+ return `(${pieces.join(joiner)})`;
16
+ }
17
+ case "not": {
18
+ const args = filter.args;
19
+ if (!Array.isArray(args) || args.length !== 1) {
20
+ throw new Error("NOT filter must have exactly one argument.");
21
+ }
22
+ return `(NOT ${expressionToSql(args[0], attributeByName, datasetName)})`;
23
+ }
24
+ case "is_null":
25
+ case "is_not_null": {
26
+ const left = filter.left;
27
+ const leftSql = expressionToSql(left, attributeByName, datasetName);
28
+ return `(${leftSql} ${op === "is_null" ? "IS NULL" : "IS NOT NULL"})`;
29
+ }
30
+ case "in":
31
+ case "not in": {
32
+ const left = filter.left;
33
+ const right = filter.right;
34
+ if (!Array.isArray(right) || right.length === 0) {
35
+ throw new Error(`Filter "${filter.op}" requires a non-empty array of values.`);
36
+ }
37
+ const leftSql = expressionToSql(left, attributeByName, datasetName);
38
+ const rightSql = right
39
+ .map((value) => expressionToSql(value, attributeByName, datasetName))
40
+ .join(", ");
41
+ const operator = op === "in" ? "IN" : "NOT IN";
42
+ return `(${leftSql} ${operator} (${rightSql}))`;
43
+ }
44
+ case "between": {
45
+ const operand = filter.operand;
46
+ const lower = filter.lower;
47
+ const upper = filter.upper;
48
+ const operandSql = expressionToSql(operand, attributeByName, datasetName);
49
+ const lowerSql = expressionToSql(lower, attributeByName, datasetName);
50
+ const upperSql = expressionToSql(upper, attributeByName, datasetName);
51
+ return `(${operandSql} BETWEEN ${lowerSql} AND ${upperSql})`;
52
+ }
53
+ default: {
54
+ const left = filter.left;
55
+ const right = filter.right;
56
+ const leftSql = expressionToSql(left, attributeByName, datasetName);
57
+ const rightSql = expressionToSql(right, attributeByName, datasetName);
58
+ return `(${leftSql} ${filter.op} ${rightSql})`;
59
+ }
60
+ }
61
+ }
62
+ function expressionToSql(expression, attributeByName, datasetName) {
63
+ if (expression === null || expression === undefined) {
64
+ throw new Error("Filter expressions cannot be null or undefined.");
65
+ }
66
+ if (typeof expression === "number") {
67
+ return expression.toString();
68
+ }
69
+ if (typeof expression === "boolean") {
70
+ return expression ? "TRUE" : "FALSE";
71
+ }
72
+ if (typeof expression === "string") {
73
+ return `'${expression.replace(/'/g, "''")}'`;
74
+ }
75
+ if (typeof expression === "object") {
76
+ if (isAttributeReference(expression)) {
77
+ const attributeName = expression.attribute_name;
78
+ const attribute = attributeByName.get(attributeName);
79
+ if (!attribute) {
80
+ throw new Error(`Attribute "${attributeName}" referenced in filter but not provided.`);
81
+ }
82
+ const path = normalizePathValue(expression.path);
83
+ return resolveAttributeExpression(attribute, path, datasetName);
84
+ }
85
+ if ("op" in expression) {
86
+ return filterToSql(expression, attributeByName, datasetName);
87
+ }
88
+ }
89
+ throw new Error(`Unsupported filter expression: ${JSON.stringify(expression)}`);
90
+ }
@@ -0,0 +1,6 @@
1
+ import type { PathValue, PolicyFilter } from "./types";
2
+ export declare function visitFilterAttributes(filter: PolicyFilter, visitor: (attributeName: string, normalizedPath: string) => void): void;
3
+ export declare function isAttributeReference(node: unknown): node is {
4
+ attribute_name: string;
5
+ path?: PathValue;
6
+ };
@@ -0,0 +1,49 @@
1
+ import { normalizePathValue } from "../utils/path-helpers";
2
+ export function visitFilterAttributes(filter, visitor) {
3
+ traverseFilter(filter, visitor);
4
+ }
5
+ function traverseFilter(node, visitor) {
6
+ if (!node || typeof node !== "object") {
7
+ return;
8
+ }
9
+ if (isAttributeReference(node)) {
10
+ const normalizedPath = normalizePathValue(node.path);
11
+ visitor(node.attribute_name, normalizedPath);
12
+ return;
13
+ }
14
+ if ("left" in node) {
15
+ traverseFilter(node.left, visitor);
16
+ }
17
+ if ("right" in node) {
18
+ const right = node.right;
19
+ if (Array.isArray(right)) {
20
+ for (const item of right) {
21
+ traverseFilter(item, visitor);
22
+ }
23
+ }
24
+ else {
25
+ traverseFilter(right, visitor);
26
+ }
27
+ }
28
+ if ("args" in node && Array.isArray(node.args)) {
29
+ for (const arg of node.args) {
30
+ traverseFilter(arg, visitor);
31
+ }
32
+ }
33
+ if ("operand" in node) {
34
+ traverseFilter(node.operand, visitor);
35
+ }
36
+ if ("lower" in node) {
37
+ traverseFilter(node.lower, visitor);
38
+ }
39
+ if ("upper" in node) {
40
+ traverseFilter(node.upper, visitor);
41
+ }
42
+ }
43
+ export function isAttributeReference(node) {
44
+ return (!!node &&
45
+ typeof node === "object" &&
46
+ "type" in node &&
47
+ node.type === "attribute" &&
48
+ "attribute_name" in node);
49
+ }
@@ -0,0 +1,4 @@
1
+ import type { Attribute } from "../../attributes/types";
2
+ import type { CollaborationPolicyType } from "../types";
3
+ import type { AttributePathIndex } from "./types";
4
+ export declare function traversePolicy(policy: CollaborationPolicyType, attributeByName: Map<string, Attribute>, requiredAttributePaths: AttributePathIndex, whereClauses: Set<string>, datasetName: string): void;
@@ -0,0 +1,100 @@
1
+ import { markAttributePath, normalizePathValue } from "../utils/path-helpers";
2
+ import { filterToSql } from "./filter-builder";
3
+ import { visitFilterAttributes } from "./filter-utils";
4
+ export function traversePolicy(policy, attributeByName, requiredAttributePaths, whereClauses, datasetName) {
5
+ const definition = policy.policy.definition;
6
+ // Only traverse satisfied parts of the structure
7
+ traverseSatisfiedStructure(definition.structure, attributeByName, (node) => {
8
+ const field = node.field;
9
+ const attributeName = field.attribute_name;
10
+ const attribute = attributeByName.get(attributeName);
11
+ if (!attribute) {
12
+ throw new Error(`Attribute "${attributeName}" referenced in policy but not provided.`);
13
+ }
14
+ // Always mark the root attribute (without path) for SELECT clause
15
+ markAttributePath(requiredAttributePaths, attribute, normalizePathValue(undefined));
16
+ if (field.additional_required_properties) {
17
+ for (const path of field.additional_required_properties) {
18
+ // Add IS NOT NULL filter for each additional required property
19
+ const pathStr = normalizePathValue(path);
20
+ const expression = `company_data."${datasetName}"."_rosetta_stone"."${attribute.name}"."${pathStr}"`;
21
+ whereClauses.add(`(${expression} IS NOT NULL)`);
22
+ }
23
+ }
24
+ if (node.filters) {
25
+ for (const filter of node.filters) {
26
+ visitFilterAttributes(filter, (attributeName, path) => {
27
+ const attribute = attributeByName.get(attributeName);
28
+ if (!attribute) {
29
+ throw new Error(`Attribute "${attributeName}" referenced in filter but not provided.`);
30
+ }
31
+ markAttributePath(requiredAttributePaths, attribute, path);
32
+ });
33
+ const sql = filterToSql(filter, attributeByName, datasetName);
34
+ whereClauses.add(sql);
35
+ }
36
+ }
37
+ });
38
+ if (definition.filters) {
39
+ for (const filter of definition.filters) {
40
+ visitFilterAttributes(filter, (attributeName, path) => {
41
+ const attribute = attributeByName.get(attributeName);
42
+ if (!attribute) {
43
+ throw new Error(`Attribute "${attributeName}" referenced in filter but not provided.`);
44
+ }
45
+ markAttributePath(requiredAttributePaths, attribute, path);
46
+ });
47
+ const sql = filterToSql(filter, attributeByName, datasetName);
48
+ whereClauses.add(sql);
49
+ }
50
+ }
51
+ }
52
+ function traverseSatisfiedStructure(node, attributeByName, visitor) {
53
+ if (!node || typeof node !== "object") {
54
+ return;
55
+ }
56
+ if ("field" in node) {
57
+ visitor(node);
58
+ return;
59
+ }
60
+ if ("anyOf" in node && Array.isArray(node.anyOf)) {
61
+ // For anyOf, only traverse the FIRST satisfied branch
62
+ for (const child of node.anyOf) {
63
+ if (isBranchSatisfied(child, attributeByName)) {
64
+ traverseSatisfiedStructure(child, attributeByName, visitor);
65
+ return; // Only process the first satisfied branch
66
+ }
67
+ }
68
+ return;
69
+ }
70
+ if ("allOf" in node && Array.isArray(node.allOf)) {
71
+ // For allOf, traverse all branches (they must all be satisfied)
72
+ for (const child of node.allOf) {
73
+ traverseSatisfiedStructure(child, attributeByName, visitor);
74
+ }
75
+ }
76
+ }
77
+ function isBranchSatisfied(node, attributeByName) {
78
+ if (!node || typeof node !== "object") {
79
+ return true;
80
+ }
81
+ if ("field" in node) {
82
+ const field = node
83
+ .field;
84
+ if (field.type === "attribute") {
85
+ const attributeName = field.attribute_name;
86
+ // Only check if attribute exists, not mappings since we use company_data format
87
+ return attributeByName.has(attributeName);
88
+ }
89
+ return true;
90
+ }
91
+ if ("anyOf" in node && Array.isArray(node.anyOf)) {
92
+ // For anyOf, at least one child must be satisfied
93
+ return node.anyOf.some((child) => isBranchSatisfied(child, attributeByName));
94
+ }
95
+ if ("allOf" in node && Array.isArray(node.allOf)) {
96
+ // For allOf, all children must be satisfied
97
+ return node.allOf.every((child) => isBranchSatisfied(child, attributeByName));
98
+ }
99
+ return true;
100
+ }
@@ -0,0 +1,11 @@
1
+ import type { Attribute } from "../../attributes/types";
2
+ import type { Dataset } from "../../datasets/types";
3
+ import type { CollaborationPolicyType } from "../types";
4
+ interface PolicyValidationResult {
5
+ isValid: boolean;
6
+ missingAttributes: string[];
7
+ missingMappings: string[];
8
+ errors: string[];
9
+ }
10
+ export declare function validatePolicyAgainstDataset(policy: CollaborationPolicyType, _dataset: Dataset, attributes: Attribute[]): PolicyValidationResult;
11
+ export {};
@@ -0,0 +1,148 @@
1
+ import { visitFilterAttributes } from "./filter-utils";
2
+ export function validatePolicyAgainstDataset(policy, _dataset, attributes) {
3
+ const result = {
4
+ isValid: true,
5
+ missingAttributes: [],
6
+ missingMappings: [],
7
+ errors: [],
8
+ };
9
+ const attributeByName = new Map();
10
+ for (const attribute of attributes) {
11
+ attributeByName.set(attribute.name, attribute);
12
+ }
13
+ // Validate the policy structure considering logical operators
14
+ const validation = validatePolicyStructure(policy.policy.definition.structure, attributeByName);
15
+ if (!validation.isValid) {
16
+ result.isValid = false;
17
+ result.errors.push(...validation.errors);
18
+ // Extract unique missing attributes for backwards compatibility
19
+ const allMissingAttributes = new Set();
20
+ for (const error of validation.errors) {
21
+ if (error.includes("Missing attributes:")) {
22
+ const attrs = error
23
+ .split("Missing attributes:")[1]
24
+ ?.split(",")
25
+ .map((s) => s.trim()) || [];
26
+ for (const attr of attrs) {
27
+ allMissingAttributes.add(attr);
28
+ }
29
+ }
30
+ }
31
+ result.missingAttributes = Array.from(allMissingAttributes);
32
+ }
33
+ const missingFilterAttributes = new Set();
34
+ const filters = policy.policy.definition.filters ?? [];
35
+ for (const filter of filters) {
36
+ collectMissingAttributesFromFilter(filter, attributeByName, missingFilterAttributes);
37
+ }
38
+ visitStructureFilters(policy.policy.definition.structure, (filter) => collectMissingAttributesFromFilter(filter, attributeByName, missingFilterAttributes));
39
+ if (missingFilterAttributes.size > 0) {
40
+ result.isValid = false;
41
+ const missingList = Array.from(missingFilterAttributes).sort();
42
+ result.errors.push(`Filters reference missing attributes: ${missingList.join(", ")}`);
43
+ for (const attr of missingList) {
44
+ if (!result.missingAttributes.includes(attr)) {
45
+ result.missingAttributes.push(attr);
46
+ }
47
+ }
48
+ }
49
+ return result;
50
+ }
51
+ function validatePolicyStructure(structure, attributeByName) {
52
+ if (!structure || typeof structure !== "object") {
53
+ return { isValid: true, errors: [] };
54
+ }
55
+ const struct = structure;
56
+ // Handle anyOf - at least one must be valid
57
+ if (struct.anyOf && Array.isArray(struct.anyOf)) {
58
+ for (const option of struct.anyOf) {
59
+ const optionValidation = validatePolicyStructure(option, attributeByName);
60
+ if (optionValidation.isValid) {
61
+ // If any option is valid, the whole anyOf is valid
62
+ return { isValid: true, errors: [] };
63
+ }
64
+ }
65
+ // If no options are valid, collect all errors
66
+ const allErrors = [];
67
+ for (const option of struct.anyOf) {
68
+ const optionValidation = validatePolicyStructure(option, attributeByName);
69
+ allErrors.push(...optionValidation.errors);
70
+ }
71
+ return {
72
+ isValid: false,
73
+ errors: [
74
+ `None of the anyOf options are satisfied. Issues: ${allErrors.join("; ")}`,
75
+ ],
76
+ };
77
+ }
78
+ // Handle allOf - all must be valid
79
+ if (struct.allOf && Array.isArray(struct.allOf)) {
80
+ const allErrors = [];
81
+ for (const requirement of struct.allOf) {
82
+ const reqValidation = validatePolicyStructure(requirement, attributeByName);
83
+ if (!reqValidation.isValid) {
84
+ allErrors.push(...reqValidation.errors);
85
+ }
86
+ }
87
+ return allErrors.length > 0
88
+ ? { isValid: false, errors: allErrors }
89
+ : { isValid: true, errors: [] };
90
+ }
91
+ // Handle single field with attribute
92
+ if (struct.field &&
93
+ struct.field.type ===
94
+ "attribute") {
95
+ const attributeName = struct.field
96
+ .attribute_name;
97
+ const errors = [];
98
+ // Check if attribute exists - we don't need to check mappings anymore
99
+ // since we use company_data.<attribute_name> format
100
+ if (!attributeByName.has(attributeName)) {
101
+ errors.push(`Missing attributes: ${attributeName}`);
102
+ }
103
+ return errors.length > 0
104
+ ? { isValid: false, errors }
105
+ : { isValid: true, errors: [] };
106
+ }
107
+ // For other structures, recursively validate
108
+ for (const value of Object.values(struct)) {
109
+ if (typeof value === "object") {
110
+ const childValidation = validatePolicyStructure(value, attributeByName);
111
+ if (!childValidation.isValid) {
112
+ return childValidation;
113
+ }
114
+ }
115
+ }
116
+ return { isValid: true, errors: [] };
117
+ }
118
+ function visitStructureFilters(node, visitor) {
119
+ if (!node || typeof node !== "object") {
120
+ return;
121
+ }
122
+ if ("field" in node) {
123
+ const filters = node.filters;
124
+ if (filters) {
125
+ for (const filter of filters) {
126
+ visitor(filter);
127
+ }
128
+ }
129
+ return;
130
+ }
131
+ if ("anyOf" in node && Array.isArray(node.anyOf)) {
132
+ for (const child of node.anyOf) {
133
+ visitStructureFilters(child, visitor);
134
+ }
135
+ }
136
+ if ("allOf" in node && Array.isArray(node.allOf)) {
137
+ for (const child of node.allOf) {
138
+ visitStructureFilters(child, visitor);
139
+ }
140
+ }
141
+ }
142
+ function collectMissingAttributesFromFilter(filter, attributesByName, missingAttributes) {
143
+ visitFilterAttributes(filter, (attributeName) => {
144
+ if (!attributesByName.has(attributeName)) {
145
+ missingAttributes.add(attributeName);
146
+ }
147
+ });
148
+ }
@@ -0,0 +1,37 @@
1
+ import type { Attribute } from "../../attributes/types";
2
+ import type { Dataset } from "../../datasets/types";
3
+ import type { CollaborationPolicyType } from "../types";
4
+ import type { PolicyMatchResult, PolicySqlFragments } from "./types";
5
+ export declare function buildPolicySqlWithValidation(dataset: Dataset, policies: CollaborationPolicyType[], attributes: Attribute[]): PolicyMatchResult;
6
+ /**
7
+ * Categorizes collaboration policies into matching and non-matching groups based on dataset compatibility.
8
+ *
9
+ * This function validates each policy against the provided dataset and attributes to determine
10
+ * whether the policy can be successfully applied. Policies are categorized as matching if they
11
+ * have all required attributes available, or non-matching if they're missing required attributes
12
+ * or mappings.
13
+ *
14
+ * @param policies - Array of collaboration policies to categorize
15
+ * @param dataset - The dataset to validate policies against
16
+ * @param attributes - Available attributes for validation
17
+ * @returns Object containing matching policies, non-matching policies, and detailed skip information
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const { matching, nonMatching, skippedDetails } = categorizePolicies(
22
+ * allPolicies,
23
+ * myDataset,
24
+ * availableAttributes
25
+ * );
26
+ *
27
+ * console.log(`${matching.length} policies can be applied`);
28
+ * console.log(`${nonMatching.length} policies were skipped`);
29
+ * skippedDetails.forEach(skip => console.log(`Skipped ${skip.name}: ${skip.reason}`));
30
+ * ```
31
+ */
32
+ export declare function categorizePolicies(policies: CollaborationPolicyType[], dataset: Dataset, attributes: Attribute[]): {
33
+ matching: CollaborationPolicyType[];
34
+ nonMatching: CollaborationPolicyType[];
35
+ skippedDetails: PolicyMatchResult["skippedPolicies"];
36
+ };
37
+ export declare function buildPolicySql(dataset: Dataset, policies: CollaborationPolicyType[], attributes: Attribute[]): PolicySqlFragments;