@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
@@ -0,0 +1,173 @@
1
+ import { buildSelectAlias, resolveAttributeExpression, } from "../utils/sql-helpers";
2
+ import { traversePolicy } from "./policy-traverser";
3
+ import { validatePolicyAgainstDataset } from "./policy-validator";
4
+ function buildAttributeIndexes(attributes) {
5
+ const byId = new Map();
6
+ const byName = new Map();
7
+ for (const attribute of attributes) {
8
+ byId.set(attribute.id, attribute);
9
+ byName.set(attribute.name, attribute);
10
+ }
11
+ return { byId, byName };
12
+ }
13
+ function getSmallestRefreshSchedule(schedules) {
14
+ if (schedules.length === 0) {
15
+ return "@monthly";
16
+ }
17
+ // Define the hierarchy for refresh schedules (from smallest to largest)
18
+ const scheduleHierarchy = {
19
+ "@once": 0,
20
+ "@hourly": 1,
21
+ "@daily": 2,
22
+ "@weekly": 3,
23
+ "@monthly": 4,
24
+ };
25
+ // Find the schedule with the smallest value (most frequent)
26
+ let smallestSchedule = "@monthly";
27
+ let smallestValue = scheduleHierarchy["@monthly"];
28
+ for (const schedule of schedules) {
29
+ if (schedule in scheduleHierarchy) {
30
+ const value = scheduleHierarchy[schedule];
31
+ if (value < smallestValue) {
32
+ smallestValue = value;
33
+ smallestSchedule = schedule;
34
+ }
35
+ }
36
+ else {
37
+ // If it's a custom cron expression, treat it as the most frequent (smallest)
38
+ // since we can't easily compare cron expressions
39
+ return schedule;
40
+ }
41
+ }
42
+ return smallestSchedule;
43
+ }
44
+ function buildPolicySqlUsingIndexes(policies, attributeByName, datasetName) {
45
+ const globalRequiredAttributePaths = new Map();
46
+ const policyWhereClauses = [];
47
+ for (const policy of policies) {
48
+ const policyRequiredAttributePaths = new Map();
49
+ const policyWhereClausesSet = new Set();
50
+ traversePolicy(policy, attributeByName, policyRequiredAttributePaths, policyWhereClausesSet, datasetName);
51
+ policyWhereClauses.push(Array.from(policyWhereClausesSet));
52
+ for (const [attributeName, paths] of policyRequiredAttributePaths) {
53
+ const globalPaths = globalRequiredAttributePaths.get(attributeName) ?? new Set();
54
+ for (const path of paths) {
55
+ globalPaths.add(path);
56
+ }
57
+ globalRequiredAttributePaths.set(attributeName, globalPaths);
58
+ }
59
+ }
60
+ const selectClauses = new Set();
61
+ for (const [attributeName, _paths] of globalRequiredAttributePaths) {
62
+ const attribute = attributeByName.get(attributeName);
63
+ if (!attribute) {
64
+ continue;
65
+ }
66
+ // Always select the root attribute (without path)
67
+ const expression = resolveAttributeExpression(attribute, "", // Empty path for root attribute
68
+ datasetName);
69
+ const alias = buildSelectAlias(attributeName, "");
70
+ selectClauses.add(`${expression} AS ${alias}`);
71
+ }
72
+ return {
73
+ select: Array.from(selectClauses),
74
+ where: policyWhereClauses,
75
+ };
76
+ }
77
+ export function buildPolicySqlWithValidation(dataset, policies, attributes) {
78
+ const attributeIndexes = buildAttributeIndexes(attributes);
79
+ const validPolicies = [];
80
+ const skippedPolicies = [];
81
+ const warnings = [];
82
+ // Validate each policy against the dataset
83
+ for (const policy of policies) {
84
+ const validation = validatePolicyAgainstDataset(policy, dataset, attributes);
85
+ if (validation.isValid) {
86
+ validPolicies.push(policy);
87
+ }
88
+ else {
89
+ skippedPolicies.push({
90
+ name: policy.display_name || policy.name,
91
+ reason: "Missing required attributes or mappings",
92
+ details: validation.errors,
93
+ });
94
+ }
95
+ }
96
+ // Check if we have at least one valid policy
97
+ if (validPolicies.length === 0) {
98
+ const errorMessage = policies.length === 0
99
+ ? "No policies provided"
100
+ : `No policies are compatible with dataset "${dataset.display_name || dataset.name}".`;
101
+ const details = skippedPolicies.length > 0
102
+ ? [`Skipped policies: ${skippedPolicies.map((p) => p.name).join(", ")}`]
103
+ : [];
104
+ throw new Error(`${errorMessage}\n${details.join("\n")}`);
105
+ }
106
+ if (skippedPolicies.length > 0) {
107
+ warnings.push(`Skipped ${skippedPolicies.length} incompatible policies: ${skippedPolicies.map((p) => p.name).join(", ")}`);
108
+ }
109
+ // Build SQL for valid policies
110
+ const sqlFragments = buildPolicySqlUsingIndexes(validPolicies, attributeIndexes.byName, dataset.name);
111
+ // Calculate the smallest refresh schedule from applied policies
112
+ const refreshSchedules = validPolicies
113
+ .map((p) => p.policy.metadata?.refresh_schedule?.max)
114
+ .filter((schedule) => schedule !== undefined);
115
+ const refreshSchedule = getSmallestRefreshSchedule(refreshSchedules);
116
+ return {
117
+ ...sqlFragments,
118
+ appliedPolicies: validPolicies.map((p) => p.name),
119
+ skippedPolicies,
120
+ warnings,
121
+ refresh_schedule: refreshSchedule,
122
+ };
123
+ }
124
+ /**
125
+ * Categorizes collaboration policies into matching and non-matching groups based on dataset compatibility.
126
+ *
127
+ * This function validates each policy against the provided dataset and attributes to determine
128
+ * whether the policy can be successfully applied. Policies are categorized as matching if they
129
+ * have all required attributes available, or non-matching if they're missing required attributes
130
+ * or mappings.
131
+ *
132
+ * @param policies - Array of collaboration policies to categorize
133
+ * @param dataset - The dataset to validate policies against
134
+ * @param attributes - Available attributes for validation
135
+ * @returns Object containing matching policies, non-matching policies, and detailed skip information
136
+ *
137
+ * @example
138
+ * ```typescript
139
+ * const { matching, nonMatching, skippedDetails } = categorizePolicies(
140
+ * allPolicies,
141
+ * myDataset,
142
+ * availableAttributes
143
+ * );
144
+ *
145
+ * console.log(`${matching.length} policies can be applied`);
146
+ * console.log(`${nonMatching.length} policies were skipped`);
147
+ * skippedDetails.forEach(skip => console.log(`Skipped ${skip.name}: ${skip.reason}`));
148
+ * ```
149
+ */
150
+ export function categorizePolicies(policies, dataset, attributes) {
151
+ const matching = [];
152
+ const nonMatching = [];
153
+ const skippedDetails = [];
154
+ for (const policy of policies) {
155
+ const validation = validatePolicyAgainstDataset(policy, dataset, attributes);
156
+ if (validation.isValid) {
157
+ matching.push(policy);
158
+ }
159
+ else {
160
+ nonMatching.push(policy);
161
+ skippedDetails.push({
162
+ name: policy.display_name || policy.name,
163
+ reason: "Missing required attributes or mappings",
164
+ details: validation.errors,
165
+ });
166
+ }
167
+ }
168
+ return { matching, nonMatching, skippedDetails };
169
+ }
170
+ export function buildPolicySql(dataset, policies, attributes) {
171
+ const attributeIndexes = buildAttributeIndexes(attributes);
172
+ return buildPolicySqlUsingIndexes(policies, attributeIndexes.byName, dataset.name);
173
+ }
@@ -0,0 +1,24 @@
1
+ import type { CollaborationPolicyType } from "../types";
2
+ type PolicyDefinition = CollaborationPolicyType["policy"]["definition"];
3
+ export type StructureNode = PolicyDefinition["structure"];
4
+ export type ExtendedAttributeNode = Extract<StructureNode, {
5
+ field: unknown;
6
+ }>;
7
+ export type PolicyFilter = NonNullable<PolicyDefinition["filters"]>[number];
8
+ export type PathValue = NonNullable<ExtendedAttributeNode["field"]["additional_required_properties"]>[number];
9
+ export type AttributePathIndex = Map<string, Set<string>>;
10
+ export interface PolicySqlFragments {
11
+ select: string[];
12
+ where: string[][];
13
+ }
14
+ export interface PolicyMatchResult extends PolicySqlFragments {
15
+ appliedPolicies: string[];
16
+ skippedPolicies: Array<{
17
+ name: string;
18
+ reason: string;
19
+ details: string[];
20
+ }>;
21
+ warnings: string[];
22
+ refresh_schedule: string;
23
+ }
24
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export { buildPolicySql, buildPolicySqlWithValidation, categorizePolicies, } from "./core/sql-builder";
2
+ export type { AttributePathIndex, ExtendedAttributeNode, PathValue, PolicyFilter, PolicyMatchResult, PolicySqlFragments, StructureNode, } from "./core/types";
3
+ export type { CollaborationPolicyInput, CollaborationPolicyType, } from "./types";
4
+ export { buildCollaborationPolicyJsonSchema, CollaborationPolicy, } from "./types";
@@ -0,0 +1,3 @@
1
+ // Export the main functions
2
+ export { buildPolicySql, buildPolicySqlWithValidation, categorizePolicies, } from "./core/sql-builder";
3
+ export { buildCollaborationPolicyJsonSchema, CollaborationPolicy, } from "./types";
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ export {};
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bun
2
+ import { buildCollaborationPolicyJsonSchema } from "../types";
3
+ const schema = buildCollaborationPolicyJsonSchema();
4
+ console.log(JSON.stringify(schema, null, 2));