@narrative.io/data-collaboration-sdk-ts 2.102.0 → 2.105.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 (40) hide show
  1. package/build/access-tokens/types.d.ts +1 -0
  2. package/build/access-tokens/types.js +1 -0
  3. package/build/agents/index.d.ts +7 -0
  4. package/build/agents/index.js +8 -0
  5. package/build/apps/index.d.ts +7 -0
  6. package/build/apps/index.js +9 -0
  7. package/build/collaboration-policy/core/filter-builder.js +7 -1
  8. package/build/collaboration-policy/core/jsonschema/json-schema-types.d.ts +45 -0
  9. package/build/collaboration-policy/core/jsonschema/json-schema-types.js +1 -0
  10. package/build/collaboration-policy/core/jsonschema/policy-branches.d.ts +49 -0
  11. package/build/collaboration-policy/core/jsonschema/policy-branches.js +1 -0
  12. package/build/collaboration-policy/core/jsonschema/policy-evaluator.d.ts +22 -0
  13. package/build/collaboration-policy/core/jsonschema/policy-evaluator.js +250 -0
  14. package/build/collaboration-policy/core/jsonschema/sql-builder.d.ts +79 -0
  15. package/build/collaboration-policy/core/jsonschema/sql-builder.js +306 -0
  16. package/build/collaboration-policy/core/types.d.ts +81 -10
  17. package/build/collaboration-policy/index.d.ts +5 -4
  18. package/build/collaboration-policy/index.js +4 -3
  19. package/build/collaboration-policy/useCollaborationPolicy.d.ts +13 -4
  20. package/build/collaboration-policy/useCollaborationPolicy.js +23 -1
  21. package/build/collaboration-policy/utils/path-helpers.d.ts +7 -3
  22. package/build/collaboration-policy/utils/path-helpers.js +6 -22
  23. package/build/datasets/index.d.ts +11 -2
  24. package/build/datasets/index.js +12 -0
  25. package/build/datasets/types.d.ts +25 -0
  26. package/package.json +2 -1
  27. package/build/collaboration-policy/core/filter-utils.d.ts +0 -6
  28. package/build/collaboration-policy/core/filter-utils.js +0 -49
  29. package/build/collaboration-policy/core/policy-traverser.d.ts +0 -4
  30. package/build/collaboration-policy/core/policy-traverser.js +0 -99
  31. package/build/collaboration-policy/core/policy-validator.d.ts +0 -11
  32. package/build/collaboration-policy/core/policy-validator.js +0 -185
  33. package/build/collaboration-policy/core/sql-builder.d.ts +0 -37
  34. package/build/collaboration-policy/core/sql-builder.js +0 -346
  35. package/build/collaboration-policy/scripts/generate-policy-schema.d.ts +0 -2
  36. package/build/collaboration-policy/scripts/generate-policy-schema.js +0 -4
  37. package/build/collaboration-policy/types/collaboration-policy.d.ts +0 -1370
  38. package/build/collaboration-policy/types/collaboration-policy.js +0 -274
  39. package/build/collaboration-policy/types/index.d.ts +0 -2
  40. package/build/collaboration-policy/types/index.js +0 -1
@@ -0,0 +1,306 @@
1
+ import { buildSelectAlias, resolveAttributeExpression, } from "../../utils/sql-helpers";
2
+ import { filterToSql } from "../filter-builder";
3
+ import { evaluateJsonSchemaPolicies } from "./policy-evaluator";
4
+ /**
5
+ * Convert an ISO 8601 duration string (e.g. "P1D", "PT1H") into milliseconds.
6
+ * This is an approximation (months and years are treated as 30 and 365 days respectively).
7
+ *
8
+ * @param duration - The ISO 8601 duration string.
9
+ * @returns The approximate duration in milliseconds.
10
+ * @throws If the duration string does not match the ISO 8601 duration format.
11
+ */
12
+ function parseDurationToMilliseconds(duration) {
13
+ const regex = /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/;
14
+ const matches = duration.match(regex);
15
+ if (!matches) {
16
+ throw new Error(`Invalid ISO 8601 duration format: ${duration}`);
17
+ }
18
+ const [, years, months, weeks, days, hours, minutes, seconds] = matches.map((m) => Number.parseInt(m || "0", 10));
19
+ const milliseconds = years * 365 * 24 * 60 * 60 * 1000 +
20
+ months * 30 * 24 * 60 * 60 * 1000 +
21
+ weeks * 7 * 24 * 60 * 60 * 1000 +
22
+ days * 24 * 60 * 60 * 1000 +
23
+ hours * 60 * 60 * 1000 +
24
+ minutes * 60 * 1000 +
25
+ seconds * 1000;
26
+ return milliseconds;
27
+ }
28
+ /**
29
+ * Find the "smallest" (most frequent) ISO 8601 duration within a list.
30
+ *
31
+ * @param schedules - Array of ISO 8601 duration strings.
32
+ * @returns The smallest duration as an ISO 8601 string, or "P1M" if none are valid.
33
+ */
34
+ function getSmallestRefreshSchedule(schedules) {
35
+ const defaultMonthly = "P1M";
36
+ if (schedules.length === 0)
37
+ return defaultMonthly;
38
+ if (schedules.length === 1) {
39
+ try {
40
+ parseDurationToMilliseconds(schedules[0]);
41
+ return schedules[0];
42
+ }
43
+ catch {
44
+ return defaultMonthly;
45
+ }
46
+ }
47
+ let smallestValue = Number.MAX_SAFE_INTEGER;
48
+ let smallestSchedule = null;
49
+ for (const schedule of schedules) {
50
+ try {
51
+ const value = parseDurationToMilliseconds(schedule);
52
+ if (value < smallestValue) {
53
+ smallestValue = value;
54
+ smallestSchedule = schedule;
55
+ }
56
+ }
57
+ catch {
58
+ // skip invalid schedule
59
+ }
60
+ }
61
+ return smallestSchedule ?? defaultMonthly;
62
+ }
63
+ /**
64
+ * Build SQL fragments (SELECT + WHERE) from branch matches across all applied policies.
65
+ *
66
+ * SELECT: one entry per unique required attribute (root level, no subfield paths):
67
+ * company_data."<dataset>"."_rosetta_stone"."<attr>" AS "<attr>"
68
+ *
69
+ * WHERE: structured as root AND → per-policy structure → OR of branches.
70
+ * Each branch contains IS NOT NULL clauses (from additionalRequiredProperties)
71
+ * and filter SQL (from filters), ANDed together.
72
+ *
73
+ * @param branchMatches - Per-policy branch match results from evaluateJsonSchemaPolicies.
74
+ * @param attributeByName - Map of attribute name → Attribute for SQL expression building.
75
+ * @param datasetName - Dataset name used in SQL column paths.
76
+ * @returns PolicySqlFragments with select and where arrays.
77
+ */
78
+ function buildSqlFromBranchMatches(branchMatches, attributeByName, datasetName) {
79
+ // --- SELECT ---
80
+ // Collect all unique required attributes across all policies and branches
81
+ const globalRequiredAttributes = new Set();
82
+ for (const policyMatch of branchMatches) {
83
+ for (const branch of policyMatch.branches) {
84
+ for (const attrName of branch.requiredAttributes) {
85
+ globalRequiredAttributes.add(attrName);
86
+ }
87
+ }
88
+ }
89
+ const selectClauses = new Set();
90
+ for (const attrName of globalRequiredAttributes) {
91
+ const attribute = attributeByName.get(attrName);
92
+ if (!attribute)
93
+ continue;
94
+ const expression = resolveAttributeExpression(attribute, "", datasetName);
95
+ const alias = buildSelectAlias(attrName, "");
96
+ selectClauses.add(`${expression} AS ${alias}`);
97
+ }
98
+ // --- WHERE ---
99
+ const policyWhereGroups = [];
100
+ for (const policyMatch of branchMatches) {
101
+ const branchWheres = [];
102
+ for (const branch of policyMatch.branches) {
103
+ const branchClauses = [];
104
+ // 1. IS NOT NULL clauses derived from $ref/$defs resolution
105
+ for (const req of branch.additionalRequiredProperties) {
106
+ const attribute = attributeByName.get(req.attributeName);
107
+ if (!attribute)
108
+ continue;
109
+ for (const path of req.paths) {
110
+ const expression = resolveAttributeExpression(attribute, path, datasetName);
111
+ branchClauses.push(`(${expression} IS NOT NULL)`);
112
+ }
113
+ }
114
+ // 2. Filter SQL
115
+ for (const filter of branch.filters) {
116
+ const sql = filterToSql(filter, attributeByName, datasetName);
117
+ branchClauses.push(sql);
118
+ }
119
+ if (branchClauses.length === 0) {
120
+ continue; // branch has no WHERE contribution
121
+ }
122
+ if (branchClauses.length === 1) {
123
+ branchWheres.push(branchClauses[0]);
124
+ }
125
+ else {
126
+ branchWheres.push({
127
+ operation: "AND",
128
+ fragments: branchClauses,
129
+ });
130
+ }
131
+ }
132
+ if (branchWheres.length === 0) {
133
+ continue; // policy has no WHERE contribution
134
+ }
135
+ if (branchWheres.length === 1) {
136
+ policyWhereGroups.push(branchWheres[0]);
137
+ }
138
+ else {
139
+ // Multiple branches → OR them (anyOf semantics)
140
+ policyWhereGroups.push({
141
+ operation: "OR",
142
+ fragments: branchWheres,
143
+ });
144
+ }
145
+ }
146
+ let where;
147
+ if (policyWhereGroups.length === 0) {
148
+ where = [];
149
+ }
150
+ else {
151
+ // Root AND wrapping all policies
152
+ where = [
153
+ {
154
+ fragments: policyWhereGroups,
155
+ operation: "AND",
156
+ },
157
+ ];
158
+ }
159
+ return {
160
+ select: Array.from(selectClauses),
161
+ where,
162
+ };
163
+ }
164
+ /**
165
+ * Categorize JSON-Schema-based connector policies into matching and non-matching
166
+ * groups based on dataset compatibility.
167
+ *
168
+ * @param policies - JSON-Schema-based connector policies.
169
+ * @param dataset - Dataset whose schema is used for validation.
170
+ * @param attributes - Attributes associated with the dataset.
171
+ * @returns Object with matching policies, nonMatching policies, and skip details.
172
+ */
173
+ export function categorizePolicies(policies, dataset, attributes) {
174
+ const evaluations = evaluateJsonSchemaPolicies(dataset, attributes, policies);
175
+ const matching = [];
176
+ const nonMatching = [];
177
+ const skippedDetails = [];
178
+ for (const evalResult of evaluations) {
179
+ const { policy, isValid, errors } = evalResult;
180
+ if (isValid) {
181
+ matching.push(policy);
182
+ }
183
+ else {
184
+ nonMatching.push(policy);
185
+ skippedDetails.push({
186
+ name: policy.name,
187
+ reason: "Dataset schema not compatible with policy JSON Schema",
188
+ details: errors,
189
+ });
190
+ }
191
+ }
192
+ return { matching, nonMatching, skippedDetails };
193
+ }
194
+ /**
195
+ * Helper that evaluates JSON-Schema-based policies and returns branch-level
196
+ * matches plus scheduling information.
197
+ *
198
+ * This is intended to be consumed by a separate SQL builder layer or for
199
+ * inspection of branch-level match details.
200
+ *
201
+ * @param dataset - The dataset against which policies should be evaluated.
202
+ * @param policies - JSON-Schema-based connector policies.
203
+ * @param attributes - Attributes associated with the dataset.
204
+ * @returns JsonSchemaPolicyMatchResult with applied/skipped policies and branch matches.
205
+ * @throws If no policies are compatible with the dataset.
206
+ */
207
+ export function buildPolicySqlWithJsonSchema(dataset, policies, attributes) {
208
+ const evaluations = evaluateJsonSchemaPolicies(dataset, attributes, policies);
209
+ const appliedPolicies = [];
210
+ const skippedPolicies = [];
211
+ const warnings = [];
212
+ const branchMatches = [];
213
+ for (const evalResult of evaluations) {
214
+ const { policy, isValid, matches, errors } = evalResult;
215
+ if (!isValid) {
216
+ skippedPolicies.push({
217
+ name: policy.name,
218
+ reason: "Dataset schema not compatible with policy JSON Schema",
219
+ details: errors,
220
+ });
221
+ continue;
222
+ }
223
+ appliedPolicies.push(policy.name);
224
+ branchMatches.push({
225
+ policyName: policy.name,
226
+ branches: matches,
227
+ });
228
+ }
229
+ if (appliedPolicies.length === 0) {
230
+ const errorMessage = policies.length === 0
231
+ ? "No policies provided"
232
+ : `No policies are compatible with dataset "${dataset.display_name || dataset.name}".`;
233
+ const details = skippedPolicies.length > 0
234
+ ? [`Skipped policies: ${skippedPolicies.map((p) => p.name).join(", ")}`]
235
+ : [];
236
+ throw new Error(`${errorMessage}\n${details.join("\n")}`);
237
+ }
238
+ if (skippedPolicies.length > 0) {
239
+ warnings.push(`Skipped ${skippedPolicies.length} incompatible policies: ${skippedPolicies
240
+ .map((p) => p.name)
241
+ .join(", ")}`);
242
+ }
243
+ const refreshSchedules = evaluations
244
+ .filter((e) => e.isValid && e.policy.metadata.refresh_schedule?.max)
245
+ .map((e) => e.policy.metadata.refresh_schedule?.max)
246
+ .filter((max) => max != null);
247
+ const refresh_schedule = getSmallestRefreshSchedule(refreshSchedules);
248
+ return {
249
+ appliedPolicies,
250
+ skippedPolicies,
251
+ warnings,
252
+ refresh_schedule,
253
+ branchMatches,
254
+ };
255
+ }
256
+ /**
257
+ * Uses JSON Schema branch matching to determine valid policies, then builds
258
+ * SELECT and WHERE SQL fragments.
259
+ *
260
+ * @param dataset - Dataset for which to evaluate policies and produce SQL.
261
+ * @param policies - JSON-Schema-based connector policies.
262
+ * @param attributes - Attributes associated with the dataset.
263
+ * @returns PolicyMatchResult with select, where, applied/skipped policies, warnings, and refresh schedule.
264
+ */
265
+ export function buildPolicySqlWithValidation(dataset, policies, attributes) {
266
+ const matchResult = buildPolicySqlWithJsonSchema(dataset, policies, attributes);
267
+ const attributeByName = new Map();
268
+ for (const attr of attributes) {
269
+ attributeByName.set(attr.name, attr);
270
+ }
271
+ const sqlFragments = buildSqlFromBranchMatches(matchResult.branchMatches, attributeByName, dataset.name);
272
+ return {
273
+ ...sqlFragments,
274
+ appliedPolicies: matchResult.appliedPolicies,
275
+ skippedPolicies: matchResult.skippedPolicies,
276
+ warnings: matchResult.warnings,
277
+ refresh_schedule: matchResult.refresh_schedule,
278
+ };
279
+ }
280
+ /**
281
+ * Build SQL fragments without validation. Assumes you already filtered the policies
282
+ * however you want (e.g. via categorizePolicies). Builds SELECT and WHERE SQL
283
+ * fragments from all provided policies.
284
+ *
285
+ * @param dataset - Dataset for which to build SQL fragments.
286
+ * @param policies - JSON-Schema-based connector policies.
287
+ * @param attributes - Attributes associated with the dataset.
288
+ * @returns PolicySqlFragments with select and where arrays.
289
+ */
290
+ export function buildPolicySql(dataset, policies, attributes) {
291
+ const evaluations = evaluateJsonSchemaPolicies(dataset, attributes, policies);
292
+ const attributeByName = new Map();
293
+ for (const attr of attributes) {
294
+ attributeByName.set(attr.name, attr);
295
+ }
296
+ const branchMatches = [];
297
+ for (const evalResult of evaluations) {
298
+ if (evalResult.isValid) {
299
+ branchMatches.push({
300
+ policyName: evalResult.policy.name,
301
+ branches: evalResult.matches,
302
+ });
303
+ }
304
+ }
305
+ return buildSqlFromBranchMatches(branchMatches, attributeByName, dataset.name);
306
+ }
@@ -1,20 +1,91 @@
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];
1
+ /**
2
+ * PathValue describes a path to a subfield, expressed as either dot notation
3
+ * or a JSON Pointer. Used in filter attribute references to specify which
4
+ * subfield of an attribute to operate on.
5
+ */
6
+ export type PathValue = {
7
+ dot: string;
8
+ } | {
9
+ pointer: string;
10
+ };
11
+ /**
12
+ * An attribute reference within a filter expression.
13
+ */
14
+ export interface FilterAttributeRef {
15
+ type: "attribute";
16
+ attribute_name: string;
17
+ path?: PathValue;
18
+ }
19
+ /**
20
+ * A filter expression: a literal value or an attribute reference.
21
+ */
22
+ export type FilterExpression = string | number | boolean | FilterAttributeRef;
23
+ /**
24
+ * PolicyFilter is a single filter entry in a policy definition.
25
+ *
26
+ * This is the canonical filter type used across the codebase (JSON-Schema
27
+ * filters, SQL builder, etc.).
28
+ */
29
+ export type PolicyFilter = PolicyFilterAndOr | PolicyFilterNot | PolicyFilterIsNull | PolicyFilterIn | PolicyFilterCompare | PolicyFilterBetween;
30
+ interface PolicyFilterBase {
31
+ stage?: "generation";
32
+ name?: string;
33
+ required?: boolean;
34
+ }
35
+ interface PolicyFilterAndOr extends PolicyFilterBase {
36
+ op: "and" | "or";
37
+ args: FilterExpression[];
38
+ }
39
+ interface PolicyFilterNot extends PolicyFilterBase {
40
+ op: "not";
41
+ args: FilterExpression[];
42
+ }
43
+ interface PolicyFilterIsNull extends PolicyFilterBase {
44
+ op: "is_null" | "is_not_null";
45
+ left: FilterExpression;
46
+ }
47
+ interface PolicyFilterIn extends PolicyFilterBase {
48
+ op: "in" | "not in";
49
+ left: FilterExpression;
50
+ right: FilterExpression[];
51
+ }
52
+ interface PolicyFilterCompare extends PolicyFilterBase {
53
+ op: "=" | "<>" | ">" | ">=" | "<" | "<=" | "like" | "not like";
54
+ left: FilterExpression;
55
+ right: FilterExpression;
56
+ }
57
+ interface PolicyFilterBetween extends PolicyFilterBase {
58
+ op: "between";
59
+ operand: FilterExpression;
60
+ lower: FilterExpression;
61
+ upper: FilterExpression;
62
+ }
63
+ /**
64
+ * Map from attribute name -> set of normalized paths that must be selected
65
+ * for SQL generation.
66
+ */
9
67
  export type AttributePathIndex = Map<string, Set<string>>;
68
+ /**
69
+ * A logical group of SQL fragments combined by an operation.
70
+ */
10
71
  export interface PolicySqlGroup {
11
72
  fragments: Array<string | PolicySqlGroup>;
12
73
  operation: "AND" | "OR";
13
74
  }
75
+ /**
76
+ * Core SQL fragments required to represent a set of collaboration policies.
77
+ */
14
78
  export interface PolicySqlFragments {
15
79
  select: string[];
16
80
  where: Array<PolicySqlGroup | string>;
17
81
  }
82
+ /**
83
+ * Full result of applying collaboration policies to a dataset, including:
84
+ * - which policies were applied
85
+ * - which were skipped and why
86
+ * - warnings
87
+ * - the minimum refresh schedule across applied policies
88
+ */
18
89
  export interface PolicyMatchResult extends PolicySqlFragments {
19
90
  appliedPolicies: string[];
20
91
  skippedPolicies: Array<{
@@ -23,7 +94,7 @@ export interface PolicyMatchResult extends PolicySqlFragments {
23
94
  details: string[];
24
95
  }>;
25
96
  warnings: string[];
26
- /** The minimum refresh schedule in milliseconds across all evaluated policies */
27
- refresh_schedule: number;
97
+ /** The minimum refresh schedule as an ISO 8601 duration string across all evaluated policies */
98
+ refresh_schedule: string;
28
99
  }
29
100
  export {};
@@ -1,4 +1,5 @@
1
- export { buildPolicySql, buildPolicySqlWithValidation, categorizePolicies, } from "./core/sql-builder";
2
- export type { AttributePathIndex, ExtendedAttributeNode, PathValue, PolicyFilter, PolicyMatchResult, PolicySqlFragments, PolicySqlGroup, StructureNode, } from "./core/types";
3
- export type { CollaborationPolicyInput, CollaborationPolicyType, } from "./types";
4
- export { buildCollaborationPolicyJsonSchema, CollaborationPolicy, } from "./types";
1
+ export type { JsonSchemaConnectorPolicy } from "./core/jsonschema/json-schema-types";
2
+ export { evaluateJsonSchemaPolicies } from "./core/jsonschema/policy-evaluator";
3
+ export { buildPolicySql, buildPolicySqlWithJsonSchema, buildPolicySqlWithValidation, categorizePolicies, type JsonSchemaPolicyMatchResult, } from "./core/jsonschema/sql-builder";
4
+ export type { AttributePathIndex, FilterAttributeRef, FilterExpression, PathValue, PolicyFilter, PolicyMatchResult, PolicySqlFragments, PolicySqlGroup, } from "./core/types";
5
+ export { default as useCollaborationPolicy } from "./useCollaborationPolicy";
@@ -1,3 +1,4 @@
1
- // Export the main functions
2
- export { buildPolicySql, buildPolicySqlWithValidation, categorizePolicies, } from "./core/sql-builder";
3
- export { buildCollaborationPolicyJsonSchema, CollaborationPolicy, } from "./types";
1
+ export { evaluateJsonSchemaPolicies } from "./core/jsonschema/policy-evaluator";
2
+ export { buildPolicySql, buildPolicySqlWithJsonSchema, buildPolicySqlWithValidation, categorizePolicies, } from "./core/jsonschema/sql-builder";
3
+ // Hook-style entrypoint
4
+ export { default as useCollaborationPolicy } from "./useCollaborationPolicy";
@@ -1,8 +1,17 @@
1
1
  import type { Attribute } from "../attributes/types";
2
2
  import type { Dataset } from "../datasets/types";
3
- import type { PolicySqlFragments } from "./core/types";
4
- import type { CollaborationPolicyType } from "./types";
3
+ import type { JsonSchemaConnectorPolicy } from "./core/jsonschema/json-schema-types";
4
+ import type { PolicyMatchResult, PolicySqlFragments } from "./core/types";
5
+ /**
6
+ * Hook-style wrapper that exposes collaboration policy helpers.
7
+ *
8
+ * Public API surface:
9
+ * - categorizePolicies
10
+ * - buildPolicySql
11
+ * - buildPolicySqlWithValidation
12
+ */
5
13
  export default function useCollaborationPolicy(): {
6
- getEligibleConnectorsPolicies: (dataset: Dataset, policies: CollaborationPolicyType[], attributes: Attribute[]) => CollaborationPolicyType[];
7
- buildPolicySqlFragments: (dataset: Dataset, policies: CollaborationPolicyType[], attributes: Attribute[]) => PolicySqlFragments;
14
+ getEligibleConnectorsPolicies: (dataset: Dataset, policies: JsonSchemaConnectorPolicy[], attributes: Attribute[]) => JsonSchemaConnectorPolicy[];
15
+ buildPolicySqlFragments: (dataset: Dataset, policies: JsonSchemaConnectorPolicy[], attributes: Attribute[]) => PolicySqlFragments;
16
+ buildPolicySqlWithValidationJsonSchema: (dataset: Dataset, policies: JsonSchemaConnectorPolicy[], attributes: Attribute[]) => PolicyMatchResult;
8
17
  };
@@ -1,14 +1,36 @@
1
- import { buildPolicySql, categorizePolicies } from "./core/sql-builder";
1
+ import { buildPolicySql, buildPolicySqlWithValidation, categorizePolicies, } from "./core/jsonschema/sql-builder";
2
+ /**
3
+ * Hook-style wrapper that exposes collaboration policy helpers.
4
+ *
5
+ * Public API surface:
6
+ * - categorizePolicies
7
+ * - buildPolicySql
8
+ * - buildPolicySqlWithValidation
9
+ */
2
10
  export default function useCollaborationPolicy() {
11
+ /**
12
+ * Get policies that are compatible with a dataset using the JSON-Schema policy system.
13
+ */
3
14
  const getEligibleConnectorsPolicies = (dataset, policies, attributes) => {
4
15
  const { matching } = categorizePolicies(policies, dataset, attributes);
5
16
  return matching;
6
17
  };
18
+ /**
19
+ * Build SQL fragments using JSON-Schema policies, without per-policy validation.
20
+ */
7
21
  const buildPolicySqlFragments = (dataset, policies, attributes) => {
8
22
  return buildPolicySql(dataset, policies, attributes);
9
23
  };
24
+ /**
25
+ * Validate each JSON-Schema policy against the dataset and build SQL
26
+ * plus metadata (appliedPolicies, skippedPolicies, etc.).
27
+ */
28
+ const buildPolicySqlWithValidationJsonSchema = (dataset, policies, attributes) => {
29
+ return buildPolicySqlWithValidation(dataset, policies, attributes);
30
+ };
10
31
  return {
11
32
  getEligibleConnectorsPolicies,
12
33
  buildPolicySqlFragments,
34
+ buildPolicySqlWithValidationJsonSchema,
13
35
  };
14
36
  }
@@ -1,4 +1,8 @@
1
- import type { Attribute } from "../../attributes/types";
2
- import type { AttributePathIndex, PathValue } from "../core/types";
3
- export declare function markAttributePath(index: AttributePathIndex, attribute: Attribute, path: string): void;
1
+ import type { PathValue } from "../core/types";
2
+ /**
3
+ * Normalizes a PathValue (dot notation or JSON pointer) into a simple dot-notation string.
4
+ *
5
+ * @param path - A PathValue with either `{ dot: "..." }` or `{ pointer: "..." }` format.
6
+ * @returns Normalized dot-notation string, or empty string for undefined/null.
7
+ */
4
8
  export declare function normalizePathValue(path?: PathValue): string;
@@ -1,25 +1,9 @@
1
- export function markAttributePath(index, attribute, path) {
2
- const attributePaths = index.get(attribute.name) ?? new Set();
3
- // Use the provided path, even if it's an empty string (which represents the root attribute)
4
- const normalized = path !== undefined && path !== null
5
- ? path
6
- : determineDefaultPath(attribute);
7
- attributePaths.add(normalized);
8
- index.set(attribute.name, attributePaths);
9
- }
10
- function determineDefaultPath(attribute) {
11
- if (attribute.type === "object" && "properties" in attribute) {
12
- const properties = attribute.properties ?? {};
13
- if ("value" in properties) {
14
- return "value";
15
- }
16
- const [firstKey] = Object.keys(properties);
17
- if (firstKey) {
18
- return firstKey;
19
- }
20
- }
21
- return "";
22
- }
1
+ /**
2
+ * Normalizes a PathValue (dot notation or JSON pointer) into a simple dot-notation string.
3
+ *
4
+ * @param path - A PathValue with either `{ dot: "..." }` or `{ pointer: "..." }` format.
5
+ * @returns Normalized dot-notation string, or empty string for undefined/null.
6
+ */
23
7
  export function normalizePathValue(path) {
24
8
  if (!path) {
25
9
  return "";
@@ -2,9 +2,9 @@ import type { UnknownJob } from "src/jobs/types";
2
2
  import { BaseApi } from "../base-api";
3
3
  import type { ApiRecords } from "../types";
4
4
  import type { DatasetStatisticsConfiguration, StatisticsConfigurationResponse } from "./statistics-types";
5
- import type { AdvancedStatistics, AdvancedStatisticsMetadata, BasicStatistics, ColumnStatistics, ColumnSummary, Columns, Configuration, CreateDatasetRefreshScheduleRequest, CreateDatasetRequest, Dataset, DatasetStatus, DatasetTableSummary, DatasetTableSummaryAPIResponse, DatasetWriteMode, FilePerSnapshotResponse, Histogram, IngestDatasetFileRequest, RecalculationInfo, RetentionPolicy, RetentionScheduleConfig, RowClock, RowTTLPolicy, Schema, SchemaArrayItems, SchemaArrayItemsArray, SchemaArrayItemsObject, SchemaArrayItemsPrimitive, SchemaArrayProperty, SchemaFileConfig, SchemaFileConfigType, SchemaObjectProperty, SchemaPrimitiveProperty, SchemaProperties, SchemaPropertiesType, SchemaProperty, SnapshotAgeExpression, SnapshotRange, SnapshotTTLPolicy, TableClock, TableClockKind, TableTTLPolicy, TTLPolicy, TTLPolicyKind, UpdateDatasetRefreshScheduleRequest, UpdateDatasetRequest, UpsertRetentionPoliciesRequest, Value } from "./types";
5
+ import type { AdvancedStatistics, AdvancedStatisticsMetadata, BasicStatistics, ColumnStatistics, ColumnSummary, Columns, Configuration, CreateDatasetRefreshScheduleRequest, CreateDatasetRequest, Dataset, DatasetInterfaceError, DatasetInterfaceRef, DatasetInterfaceValidation, DatasetStatus, DatasetTableSummary, DatasetTableSummaryAPIResponse, DatasetWriteMode, FilePerSnapshotResponse, Histogram, IngestDatasetFileRequest, JsonSchemaValidationNode, RecalculationInfo, RetentionPolicy, RetentionScheduleConfig, RowClock, RowTTLPolicy, Schema, SchemaArrayItems, SchemaArrayItemsArray, SchemaArrayItemsObject, SchemaArrayItemsPrimitive, SchemaArrayProperty, SchemaFileConfig, SchemaFileConfigType, SchemaObjectProperty, SchemaPrimitiveProperty, SchemaProperties, SchemaPropertiesType, SchemaProperty, SnapshotAgeExpression, SnapshotRange, SnapshotTTLPolicy, TableClock, TableClockKind, TableTTLPolicy, TTLPolicy, TTLPolicyKind, UpdateDatasetRefreshScheduleRequest, UpdateDatasetRequest, UpsertRetentionPoliciesRequest, Value } from "./types";
6
6
  export type { CronRefresh, DatasetFieldConfig, DatasetFieldNamespace, DatasetStatisticsConfiguration, HistogramOptions, HistogramOverflow, ManualRefresh, NestedNodeConfig, NestedPropertyConfig, NonPrimitiveNodeConfig, OnUpdateRefresh, PrimitiveNodeConfig, RefreshTrigger, RosettaStoneFieldConfig, RosettaStoneFieldNamespace, StatConfig, StatisticsConfigurationResponse, StatName, StatOptions, } from "./statistics-types";
7
- export type { AdvancedStatistics, AdvancedStatisticsMetadata, BasicStatistics, ColumnStatistics, ColumnSummary, Columns, Configuration, CreateDatasetRefreshScheduleRequest, CreateDatasetRequest, Dataset, DatasetStatus, DatasetTableSummaryAPIResponse, DatasetWriteMode, FilePerSnapshotResponse, Histogram, RetentionPolicy, RetentionScheduleConfig, RowClock, RowTTLPolicy, Schema, SchemaArrayItems, SchemaArrayItemsArray, SchemaArrayItemsObject, SchemaArrayItemsPrimitive, SchemaArrayProperty, SchemaFileConfig, SchemaFileConfigType, SchemaObjectProperty, SchemaPrimitiveProperty, SchemaProperties, SchemaPropertiesType, SchemaProperty, SnapshotAgeExpression, SnapshotRange, SnapshotTTLPolicy, TableClock, TableClockKind, TableTTLPolicy, TTLPolicy, TTLPolicyKind, UpdateDatasetRefreshScheduleRequest, UpdateDatasetRequest, UpsertRetentionPoliciesRequest, Value, };
7
+ export type { AdvancedStatistics, AdvancedStatisticsMetadata, BasicStatistics, ColumnStatistics, ColumnSummary, Columns, Configuration, CreateDatasetRefreshScheduleRequest, CreateDatasetRequest, Dataset, DatasetInterfaceError, DatasetInterfaceRef, DatasetInterfaceValidation, DatasetStatus, DatasetTableSummaryAPIResponse, DatasetWriteMode, FilePerSnapshotResponse, Histogram, JsonSchemaValidationNode, RetentionPolicy, RetentionScheduleConfig, RowClock, RowTTLPolicy, Schema, SchemaArrayItems, SchemaArrayItemsArray, SchemaArrayItemsObject, SchemaArrayItemsPrimitive, SchemaArrayProperty, SchemaFileConfig, SchemaFileConfigType, SchemaObjectProperty, SchemaPrimitiveProperty, SchemaProperties, SchemaPropertiesType, SchemaProperty, SnapshotAgeExpression, SnapshotRange, SnapshotTTLPolicy, TableClock, TableClockKind, TableTTLPolicy, TTLPolicy, TTLPolicyKind, UpdateDatasetRefreshScheduleRequest, UpdateDatasetRequest, UpsertRetentionPoliciesRequest, Value, };
8
8
  /**
9
9
  * @module DatasetApi
10
10
  * @description This module provides methods for fetching datasets.
@@ -221,6 +221,15 @@ export declare class DatasetApi extends BaseApi {
221
221
  createDatasetRefreshSchedule(datasetId: number, refreshSchedule: CreateDatasetRefreshScheduleRequest): Promise<void>;
222
222
  updateDatasetRefreshSchedule(datasetId: number, refreshSchedule: UpdateDatasetRefreshScheduleRequest): Promise<void>;
223
223
  deleteDatasetRefreshSchedule(datasetId: number): Promise<void>;
224
+ /**
225
+ * Validate which connector interfaces are compatible with a dataset.
226
+ * Filtered by the user's installed apps and optionally by tags.
227
+ *
228
+ * @param {number} datasetId - The ID of the dataset to validate interfaces against.
229
+ * @param {string[]} [tags] - Optional tags to filter interfaces (e.g. ["audience_delivery"]).
230
+ * @returns {Promise<DatasetInterfaceValidation>} - Accepted interface IDs and per-interface validation errors.
231
+ */
232
+ getDatasetInterfaces(datasetId: number, tags?: string[]): Promise<DatasetInterfaceValidation>;
224
233
  putStatisticsConfiguration(datasetId: number, configuration: DatasetStatisticsConfiguration): Promise<StatisticsConfigurationResponse>;
225
234
  getStatisticsConfiguration(datasetId: number): Promise<StatisticsConfigurationResponse>;
226
235
  deleteStatisticsConfiguration(datasetId: number): Promise<StatisticsConfigurationResponse>;
@@ -295,6 +295,18 @@ export class DatasetApi extends BaseApi {
295
295
  async deleteDatasetRefreshSchedule(datasetId) {
296
296
  await this.delete(`${resourceName}/${datasetId}/refresh-schedule`);
297
297
  }
298
+ /**
299
+ * Validate which connector interfaces are compatible with a dataset.
300
+ * Filtered by the user's installed apps and optionally by tags.
301
+ *
302
+ * @param {number} datasetId - The ID of the dataset to validate interfaces against.
303
+ * @param {string[]} [tags] - Optional tags to filter interfaces (e.g. ["audience_delivery"]).
304
+ * @returns {Promise<DatasetInterfaceValidation>} - Accepted interface IDs and per-interface validation errors.
305
+ */
306
+ async getDatasetInterfaces(datasetId, tags) {
307
+ const queryString = this.constructQueryString(tags != null ? { tags } : undefined);
308
+ return await this.get(`${resourceName}/${datasetId}/interfaces${queryString}`);
309
+ }
298
310
  async putStatisticsConfiguration(datasetId, configuration) {
299
311
  return await this.put(`${resourceName}/${datasetId}/statistics-configuration`, configuration);
300
312
  }
@@ -294,6 +294,31 @@ export interface CreateDatasetRequest {
294
294
  export interface IngestDatasetFileRequest {
295
295
  source_file: string;
296
296
  }
297
+ export interface DatasetInterfaceRef {
298
+ app_id: number;
299
+ interface_id: string;
300
+ }
301
+ /**
302
+ * A JSON-Schema 2020-12 "Basic" output node, used recursively in validation errors.
303
+ * `errors` is keyed by JSON-Schema keyword (`"required"`, `"type"`, etc.); values
304
+ * are human-readable strings.
305
+ */
306
+ export interface JsonSchemaValidationNode {
307
+ valid: boolean;
308
+ evaluationPath: string;
309
+ schemaLocation: string;
310
+ instanceLocation: string;
311
+ errors?: Record<string, string>;
312
+ details?: JsonSchemaValidationNode[];
313
+ }
314
+ export interface DatasetInterfaceError extends DatasetInterfaceRef {
315
+ details: JsonSchemaValidationNode;
316
+ }
317
+ export interface DatasetInterfaceValidation {
318
+ dataset_id: number;
319
+ accepted: DatasetInterfaceRef[];
320
+ errors: DatasetInterfaceError[];
321
+ }
297
322
  export interface UpdateDatasetRefreshScheduleRequest {
298
323
  cron?: string;
299
324
  cron_zone_id?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narrative.io/data-collaboration-sdk-ts",
3
- "version": "2.102.0",
3
+ "version": "2.105.0",
4
4
  "main": "build/index.js",
5
5
  "repository": "github:narrative-io/data-collaboration-sdk-ts",
6
6
  "source": "src/index.ts",
@@ -32,6 +32,7 @@
32
32
  "@commitlint/cli": "21.0.1",
33
33
  "@commitlint/config-conventional": "21.0.1",
34
34
  "@types/jest": "30.0.0",
35
+ "@types/json-schema": "^7.0.15",
35
36
  "babel-jest": "30.4.1",
36
37
  "jest": "30.4.2",
37
38
  "lefthook": "2.1.6",
@@ -1,6 +0,0 @@
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
- };