@x12i/memorix-associator 1.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/README.md +90 -0
- package/dist/associated-properties/index.d.ts +16 -0
- package/dist/associated-properties/index.d.ts.map +1 -0
- package/dist/associated-properties/index.js +118 -0
- package/dist/associated-properties/index.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +88 -0
- package/dist/cli.js.map +1 -0
- package/dist/engine.d.ts +12 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +115 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/planning/build-plan.d.ts +10 -0
- package/dist/planning/build-plan.d.ts.map +1 -0
- package/dist/planning/build-plan.js +319 -0
- package/dist/planning/build-plan.js.map +1 -0
- package/dist/planning/fingerprint.d.ts +19 -0
- package/dist/planning/fingerprint.d.ts.map +1 -0
- package/dist/planning/fingerprint.js +47 -0
- package/dist/planning/fingerprint.js.map +1 -0
- package/dist/request/schema.d.ts +213 -0
- package/dist/request/schema.d.ts.map +1 -0
- package/dist/request/schema.js +2 -0
- package/dist/request/schema.js.map +1 -0
- package/dist/request/validate.d.ts +6 -0
- package/dist/request/validate.d.ts.map +1 -0
- package/dist/request/validate.js +64 -0
- package/dist/request/validate.js.map +1 -0
- package/dist/store/mongo.d.ts +4 -0
- package/dist/store/mongo.d.ts.map +1 -0
- package/dist/store/mongo.js +26 -0
- package/dist/store/mongo.js.map +1 -0
- package/dist/store/types.d.ts +30 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +64 -0
- package/dist/store/types.js.map +1 -0
- package/dist/verification/associated-shape.d.ts +7 -0
- package/dist/verification/associated-shape.d.ts.map +1 -0
- package/dist/verification/associated-shape.js +52 -0
- package/dist/verification/associated-shape.js.map +1 -0
- package/dist/writing/failures.d.ts +13 -0
- package/dist/writing/failures.d.ts.map +1 -0
- package/dist/writing/failures.js +18 -0
- package/dist/writing/failures.js.map +1 -0
- package/dist/writing/smart-merge.d.ts +19 -0
- package/dist/writing/smart-merge.d.ts.map +1 -0
- package/dist/writing/smart-merge.js +77 -0
- package/dist/writing/smart-merge.js.map +1 -0
- package/docs/running-associations.md +340 -0
- package/examples/minimal-association-request.json +46 -0
- package/package.json +57 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { isAssociatedPropertyName } from "@x12i/memorix-resolvers";
|
|
2
|
+
export class AssociationRequestValidationError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "AssociationRequestValidationError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
function assertNonEmpty(value, label) {
|
|
9
|
+
if (!value || value.trim() === "") {
|
|
10
|
+
throw new AssociationRequestValidationError(`${label} is required`);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function validateAssociatedProperty(name) {
|
|
15
|
+
if (!isAssociatedPropertyName(name)) {
|
|
16
|
+
throw new AssociationRequestValidationError(`associatedProperty must start with "associated": ${name}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function validateRule(rule, index) {
|
|
20
|
+
if (rule.type !== "association") {
|
|
21
|
+
throw new AssociationRequestValidationError(`rules[${index}].type must be "association"`);
|
|
22
|
+
}
|
|
23
|
+
assertNonEmpty(rule.ruleKey, `rules[${index}].ruleKey`);
|
|
24
|
+
validateAssociatedProperty(rule.associatedProperty);
|
|
25
|
+
if (!Array.isArray(rule.sourceJoinPaths) || rule.sourceJoinPaths.length === 0) {
|
|
26
|
+
throw new AssociationRequestValidationError(`rules[${index}].sourceJoinPaths must be non-empty`);
|
|
27
|
+
}
|
|
28
|
+
assertNonEmpty(rule.target.db, `rules[${index}].target.db`);
|
|
29
|
+
assertNonEmpty(rule.target.collection, `rules[${index}].target.collection`);
|
|
30
|
+
if (!Array.isArray(rule.target.joinPaths) || rule.target.joinPaths.length === 0) {
|
|
31
|
+
throw new AssociationRequestValidationError(`rules[${index}].target.joinPaths must be non-empty`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function validateNormalization(step, index) {
|
|
35
|
+
if (step.type !== "split-values") {
|
|
36
|
+
throw new AssociationRequestValidationError(`normalization[${index}].type must be "split-values"`);
|
|
37
|
+
}
|
|
38
|
+
assertNonEmpty(step.to, `normalization[${index}].to`);
|
|
39
|
+
}
|
|
40
|
+
export function validateAssociationRequest(request) {
|
|
41
|
+
assertNonEmpty(request.processId, "processId");
|
|
42
|
+
if (request.mode !== "dry-run" && request.mode !== "apply") {
|
|
43
|
+
throw new AssociationRequestValidationError('mode must be "dry-run" or "apply"');
|
|
44
|
+
}
|
|
45
|
+
assertNonEmpty(request.source.db, "source.db");
|
|
46
|
+
assertNonEmpty(request.source.collection, "source.collection");
|
|
47
|
+
assertNonEmpty(request.source.objectType, "source.objectType");
|
|
48
|
+
assertNonEmpty(request.source.contentType, "source.contentType");
|
|
49
|
+
if (!Array.isArray(request.rules) || request.rules.length === 0) {
|
|
50
|
+
throw new AssociationRequestValidationError("rules must be a non-empty array");
|
|
51
|
+
}
|
|
52
|
+
request.rules.forEach(validateRule);
|
|
53
|
+
request.normalization?.forEach(validateNormalization);
|
|
54
|
+
if (request.writePolicy.associatedArrayMode !== "append-unique") {
|
|
55
|
+
throw new AssociationRequestValidationError('writePolicy.associatedArrayMode must be "append-unique"');
|
|
56
|
+
}
|
|
57
|
+
if (request.mode === "apply" &&
|
|
58
|
+
request.safety.requireReviewedReportForApply &&
|
|
59
|
+
!request.safety.reviewedReportPath &&
|
|
60
|
+
!request.safety.expectedPlanFingerprint) {
|
|
61
|
+
throw new AssociationRequestValidationError("apply mode requires reviewedReportPath or expectedPlanFingerprint when requireReviewedReportForApply is true");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/request/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongo.d.ts","sourceRoot":"","sources":["../../src/store/mongo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,YAAY,CAAC;AAElE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAwBvE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function createMongoRecordStore(client) {
|
|
2
|
+
return {
|
|
3
|
+
async collectionExists(db, collection) {
|
|
4
|
+
const matches = await client.db(db).listCollections({ name: collection }).toArray();
|
|
5
|
+
return matches.length > 0;
|
|
6
|
+
},
|
|
7
|
+
async *scanRecords(db, collection, filter = {}, limit) {
|
|
8
|
+
let count = 0;
|
|
9
|
+
const cursor = client.db(db).collection(collection).find(filter);
|
|
10
|
+
for await (const doc of cursor) {
|
|
11
|
+
yield doc;
|
|
12
|
+
count += 1;
|
|
13
|
+
if (limit !== undefined && count >= limit)
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
async bulkWrite(db, collection, operations) {
|
|
18
|
+
if (operations.length === 0)
|
|
19
|
+
return { errors: 0 };
|
|
20
|
+
const result = await client.db(db).collection(collection).bulkWrite(operations, { ordered: false });
|
|
21
|
+
const errors = result.getWriteErrorCount?.() ?? 0;
|
|
22
|
+
return { errors };
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=mongo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongo.js","sourceRoot":"","sources":["../../src/store/mongo.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,sBAAsB,CAAC,MAAmB;IACxD,OAAO;QACL,KAAK,CAAC,gBAAgB,CAAC,EAAE,EAAE,UAAU;YACnC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;YACpF,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QAC5B,CAAC;QAED,KAAK,CAAC,CAAC,WAAW,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,EAAE,EAAE,KAAK;YACnD,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACjE,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;gBAC/B,MAAM,GAA8B,CAAC;gBACrC,KAAK,IAAI,CAAC,CAAC;gBACX,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,IAAI,KAAK;oBAAE,MAAM;YACnD,CAAC;QACH,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,EAAE,EAAE,UAAU,EAAE,UAAU;YACxC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YAClD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YACpG,MAAM,MAAM,GAAG,MAAM,CAAC,kBAAkB,EAAE,EAAE,IAAI,CAAC,CAAC;YAClD,OAAO,EAAE,MAAM,EAAE,CAAC;QACpB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type BulkWriteOperation = {
|
|
2
|
+
updateOne: {
|
|
3
|
+
filter: Record<string, unknown>;
|
|
4
|
+
update: {
|
|
5
|
+
$set?: Record<string, unknown>;
|
|
6
|
+
$unset?: Record<string, "">;
|
|
7
|
+
};
|
|
8
|
+
upsert?: boolean;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export interface RecordStore {
|
|
12
|
+
collectionExists(db: string, collection: string): Promise<boolean>;
|
|
13
|
+
scanRecords(db: string, collection: string, filter?: Record<string, unknown>, limit?: number): AsyncIterable<Record<string, unknown>>;
|
|
14
|
+
bulkWrite(db: string, collection: string, operations: BulkWriteOperation[]): Promise<{
|
|
15
|
+
errors: number;
|
|
16
|
+
}>;
|
|
17
|
+
backupCollection?(db: string, collection: string): Promise<string>;
|
|
18
|
+
}
|
|
19
|
+
export declare class InMemoryRecordStore implements RecordStore {
|
|
20
|
+
private readonly data;
|
|
21
|
+
seed(db: string, collection: string, records: Record<string, unknown>[]): void;
|
|
22
|
+
private bucket;
|
|
23
|
+
collectionExists(db: string, collection: string): Promise<boolean>;
|
|
24
|
+
scanRecords(db: string, collection: string, filter?: Record<string, unknown>, limit?: number): AsyncIterable<Record<string, unknown>>;
|
|
25
|
+
bulkWrite(db: string, collection: string, operations: BulkWriteOperation[]): Promise<{
|
|
26
|
+
errors: number;
|
|
27
|
+
}>;
|
|
28
|
+
getRecords(db: string, collection: string): Record<string, unknown>[];
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE;QACT,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,MAAM,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;SAAE,CAAC;QACxE,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;CACH,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,WAAW,CACT,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1C,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzG,gBAAgB,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACpE;AAED,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2D;IAEhF,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;IAK9E,OAAO,CAAC,MAAM;IAMR,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMjE,WAAW,CAChB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAanC,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IA4B9G,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;CAGtE"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export class InMemoryRecordStore {
|
|
2
|
+
data = new Map();
|
|
3
|
+
seed(db, collection, records) {
|
|
4
|
+
const key = `${db}/${collection}`;
|
|
5
|
+
this.data.set(key, new Map(records.map((record, index) => [String(record._id ?? index), { ...record }])));
|
|
6
|
+
}
|
|
7
|
+
bucket(db, collection) {
|
|
8
|
+
const key = `${db}/${collection}`;
|
|
9
|
+
if (!this.data.has(key))
|
|
10
|
+
this.data.set(key, new Map());
|
|
11
|
+
return this.data.get(key);
|
|
12
|
+
}
|
|
13
|
+
async collectionExists(db, collection) {
|
|
14
|
+
const key = `${db}/${collection}`;
|
|
15
|
+
const bucket = this.data.get(key);
|
|
16
|
+
return bucket !== undefined && bucket.size > 0;
|
|
17
|
+
}
|
|
18
|
+
async *scanRecords(db, collection, filter, limit) {
|
|
19
|
+
let count = 0;
|
|
20
|
+
for (const record of this.bucket(db, collection).values()) {
|
|
21
|
+
if (filter) {
|
|
22
|
+
const matches = Object.entries(filter).every(([key, value]) => record[key] === value);
|
|
23
|
+
if (!matches)
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
yield { ...record };
|
|
27
|
+
count += 1;
|
|
28
|
+
if (limit !== undefined && count >= limit)
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async bulkWrite(db, collection, operations) {
|
|
33
|
+
const bucket = this.bucket(db, collection);
|
|
34
|
+
let errors = 0;
|
|
35
|
+
for (const op of operations) {
|
|
36
|
+
try {
|
|
37
|
+
const id = String(op.updateOne.filter._id);
|
|
38
|
+
const existing = bucket.get(id);
|
|
39
|
+
if (!existing) {
|
|
40
|
+
errors += 1;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const next = { ...existing };
|
|
44
|
+
if (op.updateOne.update.$set) {
|
|
45
|
+
Object.assign(next, op.updateOne.update.$set);
|
|
46
|
+
}
|
|
47
|
+
if (op.updateOne.update.$unset) {
|
|
48
|
+
for (const key of Object.keys(op.updateOne.update.$unset)) {
|
|
49
|
+
delete next[key];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
bucket.set(id, next);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
errors += 1;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return { errors };
|
|
59
|
+
}
|
|
60
|
+
getRecords(db, collection) {
|
|
61
|
+
return [...this.bucket(db, collection).values()];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAoBA,MAAM,OAAO,mBAAmB;IACb,IAAI,GAAG,IAAI,GAAG,EAAgD,CAAC;IAEhF,IAAI,CAAC,EAAU,EAAE,UAAkB,EAAE,OAAkC;QACrE,MAAM,GAAG,GAAG,GAAG,EAAE,IAAI,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5G,CAAC;IAEO,MAAM,CAAC,EAAU,EAAE,UAAkB;QAC3C,MAAM,GAAG,GAAG,GAAG,EAAE,IAAI,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,EAAU,EAAE,UAAkB;QACnD,MAAM,GAAG,GAAG,GAAG,EAAE,IAAI,UAAU,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,CAAC,WAAW,CAChB,EAAU,EACV,UAAkB,EAClB,MAAgC,EAChC,KAAc;QAEd,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1D,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC;gBACtF,IAAI,CAAC,OAAO;oBAAE,SAAS;YACzB,CAAC;YACD,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;YACpB,KAAK,IAAI,CAAC,CAAC;YACX,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,IAAI,KAAK;gBAAE,MAAM;QACnD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAAU,EAAE,UAAkB,EAAE,UAAgC;QAC9E,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QAC3C,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC3C,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,MAAM,IAAI,CAAC,CAAC;oBACZ,SAAS;gBACX,CAAC;gBACD,MAAM,IAAI,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;gBAC7B,IAAI,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;oBAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAChD,CAAC;gBACD,IAAI,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;oBAC/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;wBAC1D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;oBACnB,CAAC;gBACH,CAAC;gBACD,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,CAAC,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,CAAC;IACpB,CAAC;IAED,UAAU,CAAC,EAAU,EAAE,UAAkB;QACvC,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACnD,CAAC;CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { VerificationReport } from "../request/schema.js";
|
|
2
|
+
export declare function verifyAssociatedShapeOnRecord(record: Record<string, unknown>): Array<{
|
|
3
|
+
property: string;
|
|
4
|
+
message: string;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function verifyAssociatedShapes(records: Array<Record<string, unknown>>): VerificationReport;
|
|
7
|
+
//# sourceMappingURL=associated-shape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"associated-shape.d.ts","sourceRoot":"","sources":["../../src/verification/associated-shape.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAmC9C;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACtC,kBAAkB,CAgBpB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { isAssociatedPropertyName } from "@x12i/memorix-resolvers";
|
|
2
|
+
import { isPlainObject } from "../planning/fingerprint.js";
|
|
3
|
+
export function verifyAssociatedShapeOnRecord(record) {
|
|
4
|
+
const issues = [];
|
|
5
|
+
for (const [key, value] of Object.entries(record)) {
|
|
6
|
+
if (!isAssociatedPropertyName(key))
|
|
7
|
+
continue;
|
|
8
|
+
if (value === undefined)
|
|
9
|
+
continue;
|
|
10
|
+
if (!Array.isArray(value)) {
|
|
11
|
+
issues.push({
|
|
12
|
+
property: key,
|
|
13
|
+
message: "associated property must be an array",
|
|
14
|
+
});
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (value.length === 0) {
|
|
18
|
+
issues.push({
|
|
19
|
+
property: key,
|
|
20
|
+
message: "empty associated arrays should be omitted",
|
|
21
|
+
});
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
for (const item of value) {
|
|
25
|
+
if (!isPlainObject(item)) {
|
|
26
|
+
issues.push({
|
|
27
|
+
property: key,
|
|
28
|
+
message: "associated array items must be objects (linked data payloads)",
|
|
29
|
+
});
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return issues;
|
|
35
|
+
}
|
|
36
|
+
export function verifyAssociatedShapes(records) {
|
|
37
|
+
const issues = [];
|
|
38
|
+
for (const record of records) {
|
|
39
|
+
const recordIssues = verifyAssociatedShapeOnRecord(record);
|
|
40
|
+
const recordId = String(record.recordId ?? record._id ?? "unknown");
|
|
41
|
+
for (const issue of recordIssues) {
|
|
42
|
+
issues.push({ recordId, ...issue });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
passed: issues.length === 0,
|
|
47
|
+
checkedRecords: records.length,
|
|
48
|
+
invalidAssociatedShapes: issues.length,
|
|
49
|
+
issues,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=associated-shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"associated-shape.js","sourceRoot":"","sources":["../../src/verification/associated-shape.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAG3D,MAAM,UAAU,6BAA6B,CAC3C,MAA+B;IAE/B,MAAM,MAAM,GAAiD,EAAE,CAAC;IAEhE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC;YAAE,SAAS;QAC7C,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAElC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,GAAG;gBACb,OAAO,EAAE,sCAAsC;aAChD,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,GAAG;gBACb,OAAO,EAAE,2CAA2C;aACrD,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,MAAM,CAAC,IAAI,CAAC;oBACV,QAAQ,EAAE,GAAG;oBACb,OAAO,EAAE,+DAA+D;iBACzE,CAAC,CAAC;gBACH,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,OAAuC;IAEvC,MAAM,MAAM,GAAiC,EAAE,CAAC;IAChD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,YAAY,GAAG,6BAA6B,CAAC,MAAM,CAAC,CAAC;QAC3D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC;QACpE,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC3B,cAAc,EAAE,OAAO,CAAC,MAAM;QAC9B,uBAAuB,EAAE,MAAM,CAAC,MAAM;QACtC,MAAM;KACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AssociationFailureRecord, FailureReason } from "../request/schema.js";
|
|
2
|
+
export declare function defaultFailureCollection(objectType: string, override?: string): string;
|
|
3
|
+
export declare function buildFailureRecord(input: {
|
|
4
|
+
sourceDoc: Record<string, unknown>;
|
|
5
|
+
sourceObjectType: string;
|
|
6
|
+
sourceContentType: string;
|
|
7
|
+
ruleKey: string;
|
|
8
|
+
reason: FailureReason;
|
|
9
|
+
message: string;
|
|
10
|
+
details: Record<string, unknown>;
|
|
11
|
+
failedAt: string;
|
|
12
|
+
}): AssociationFailureRecord;
|
|
13
|
+
//# sourceMappingURL=failures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failures.d.ts","sourceRoot":"","sources":["../../src/writing/failures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAEpF,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtF;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,wBAAwB,CAe3B"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function defaultFailureCollection(objectType, override) {
|
|
2
|
+
return override ?? `${objectType}-failed`;
|
|
3
|
+
}
|
|
4
|
+
export function buildFailureRecord(input) {
|
|
5
|
+
const recordId = String(input.sourceDoc.recordId ??
|
|
6
|
+
(input.sourceDoc._id !== undefined ? input.sourceDoc._id : ""));
|
|
7
|
+
return {
|
|
8
|
+
recordId: recordId || undefined,
|
|
9
|
+
sourceObjectType: input.sourceObjectType,
|
|
10
|
+
sourceContentType: input.sourceContentType,
|
|
11
|
+
ruleKey: input.ruleKey,
|
|
12
|
+
failedAt: input.failedAt,
|
|
13
|
+
reason: input.reason,
|
|
14
|
+
message: input.message,
|
|
15
|
+
details: input.details,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=failures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failures.js","sourceRoot":"","sources":["../../src/writing/failures.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,wBAAwB,CAAC,UAAkB,EAAE,QAAiB;IAC5E,OAAO,QAAQ,IAAI,GAAG,UAAU,SAAS,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KASlC;IACC,MAAM,QAAQ,GAAG,MAAM,CACrB,KAAK,CAAC,SAAS,CAAC,QAAQ;QACtB,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CACjE,CAAC;IACF,OAAO;QACL,QAAQ,EAAE,QAAQ,IAAI,SAAS;QAC/B,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;QACxC,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type NormalizeAssociatedResult = {
|
|
2
|
+
value: unknown[] | undefined;
|
|
3
|
+
changed: boolean;
|
|
4
|
+
removedEmpty: boolean;
|
|
5
|
+
deduped: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function normalizeAssociatedValue(value: unknown): NormalizeAssociatedResult;
|
|
8
|
+
export type MergeAssociatedResult = {
|
|
9
|
+
value: unknown[] | undefined;
|
|
10
|
+
appended: number;
|
|
11
|
+
changed: boolean;
|
|
12
|
+
normalized: NormalizeAssociatedResult;
|
|
13
|
+
};
|
|
14
|
+
export declare function mergeAssociatedAppendUnique(existing: unknown, incomingItems: unknown[]): MergeAssociatedResult;
|
|
15
|
+
export declare function buildUpdateOperation(originalAssociated: Map<string, unknown>, workingAssociated: Map<string, unknown>): {
|
|
16
|
+
$set?: Record<string, unknown>;
|
|
17
|
+
$unset?: Record<string, "">;
|
|
18
|
+
} | null;
|
|
19
|
+
//# sourceMappingURL=smart-merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smart-merge.d.ts","sourceRoot":"","sources":["../../src/writing/smart-merge.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,CA+BlF;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,yBAAyB,CAAC;CACvC,CAAC;AAEF,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,OAAO,EACjB,aAAa,EAAE,OAAO,EAAE,GACvB,qBAAqB,CAwBvB;AAED,wBAAgB,oBAAoB,CAClC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GACtC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;CAAE,GAAG,IAAI,CAoBxE"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { canonicalKey } from "../planning/fingerprint.js";
|
|
2
|
+
export function normalizeAssociatedValue(value) {
|
|
3
|
+
if (value === undefined) {
|
|
4
|
+
return { value: undefined, changed: false, removedEmpty: false, deduped: 0 };
|
|
5
|
+
}
|
|
6
|
+
const beforeKey = canonicalKey(value);
|
|
7
|
+
const arr = Array.isArray(value) ? value : [value];
|
|
8
|
+
const seen = new Set();
|
|
9
|
+
const normalized = [];
|
|
10
|
+
let deduped = 0;
|
|
11
|
+
for (const item of arr) {
|
|
12
|
+
const key = canonicalKey(item);
|
|
13
|
+
if (seen.has(key)) {
|
|
14
|
+
deduped += 1;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
seen.add(key);
|
|
18
|
+
normalized.push(item);
|
|
19
|
+
}
|
|
20
|
+
if (normalized.length === 0) {
|
|
21
|
+
return { value: undefined, changed: true, removedEmpty: true, deduped };
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
value: normalized,
|
|
25
|
+
changed: beforeKey !== canonicalKey(normalized),
|
|
26
|
+
removedEmpty: false,
|
|
27
|
+
deduped,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function mergeAssociatedAppendUnique(existing, incomingItems) {
|
|
31
|
+
const normalized = normalizeAssociatedValue(existing);
|
|
32
|
+
const merged = normalized.value ? [...normalized.value] : [];
|
|
33
|
+
const seen = new Set(merged.map(canonicalKey));
|
|
34
|
+
let appended = 0;
|
|
35
|
+
for (const item of incomingItems) {
|
|
36
|
+
const key = canonicalKey(item);
|
|
37
|
+
if (seen.has(key))
|
|
38
|
+
continue;
|
|
39
|
+
seen.add(key);
|
|
40
|
+
merged.push(item);
|
|
41
|
+
appended += 1;
|
|
42
|
+
}
|
|
43
|
+
if (merged.length === 0) {
|
|
44
|
+
return { value: undefined, appended, changed: normalized.changed, normalized };
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
value: merged,
|
|
48
|
+
appended,
|
|
49
|
+
changed: normalized.changed || appended > 0,
|
|
50
|
+
normalized,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function buildUpdateOperation(originalAssociated, workingAssociated) {
|
|
54
|
+
const $set = {};
|
|
55
|
+
const $unset = {};
|
|
56
|
+
const keys = new Set([...originalAssociated.keys(), ...workingAssociated.keys()]);
|
|
57
|
+
for (const key of keys) {
|
|
58
|
+
const original = originalAssociated.get(key);
|
|
59
|
+
const next = workingAssociated.get(key);
|
|
60
|
+
if (next === undefined) {
|
|
61
|
+
if (original !== undefined)
|
|
62
|
+
$unset[key] = "";
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (canonicalKey(original) !== canonicalKey(next))
|
|
66
|
+
$set[key] = next;
|
|
67
|
+
}
|
|
68
|
+
const update = {};
|
|
69
|
+
if (Object.keys($set).length > 0)
|
|
70
|
+
update.$set = $set;
|
|
71
|
+
if (Object.keys($unset).length > 0)
|
|
72
|
+
update.$unset = $unset;
|
|
73
|
+
if (Object.keys(update).length === 0)
|
|
74
|
+
return null;
|
|
75
|
+
return update;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=smart-merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smart-merge.js","sourceRoot":"","sources":["../../src/writing/smart-merge.ts"],"names":[],"mappings":"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"}
|