@things-factory/ops-master 10.1.33 → 10.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +1 -0
- package/dist-client/index.js.map +1 -1
- package/dist-client/master-change-approval.d.ts +14 -0
- package/dist-client/master-change-approval.js +36 -0
- package/dist-client/master-change-approval.js.map +1 -0
- package/dist-client/pages/master/material-definition.d.ts +4 -11
- package/dist-client/pages/master/material-definition.js +5 -53
- package/dist-client/pages/master/material-definition.js.map +1 -1
- package/dist-client/pages/master/material-revision-fields.d.ts +2 -0
- package/dist-client/pages/master/material-revision-fields.js +43 -0
- package/dist-client/pages/master/material-revision-fields.js.map +1 -0
- package/dist-client/pages/master/recipe-definition.d.ts +4 -135
- package/dist-client/pages/master/recipe-definition.js +7 -397
- package/dist-client/pages/master/recipe-definition.js.map +1 -1
- package/dist-client/pages/master/revision-comparison.d.ts +48 -0
- package/dist-client/pages/master/revision-comparison.js +553 -0
- package/dist-client/pages/master/revision-comparison.js.map +1 -0
- package/dist-client/pages/master/revision-view.d.ts +19 -0
- package/dist-client/pages/master/revision-view.js +133 -0
- package/dist-client/pages/master/revision-view.js.map +1 -0
- package/dist-client/pages/master/revision-workspace.d.ts +50 -0
- package/dist-client/pages/master/revision-workspace.js +752 -0
- package/dist-client/pages/master/revision-workspace.js.map +1 -0
- package/dist-client/pages/master/test-specification.d.ts +4 -92
- package/dist-client/pages/master/test-specification.js +5 -440
- package/dist-client/pages/master/test-specification.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/asset/asset-master-resolver.js +38 -34
- package/dist-server/service/asset/asset-master-resolver.js.map +1 -1
- package/dist-server/service/asset/index.d.ts +2 -2
- package/dist-server/service/equipment/equipment-master-resolver.js +54 -49
- package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -1
- package/dist-server/service/equipment/index.d.ts +2 -2
- package/dist-server/service/index.d.ts +18 -2
- package/dist-server/service/index.js +18 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/location/index.d.ts +1 -1
- package/dist-server/service/location/location-master-resolver.js +34 -31
- package/dist-server/service/location/location-master-resolver.js.map +1 -1
- package/dist-server/service/master-axis.js +1 -1
- package/dist-server/service/master-axis.js.map +1 -1
- package/dist-server/service/master-change-policy.d.ts +23 -0
- package/dist-server/service/master-change-policy.js +134 -0
- package/dist-server/service/master-change-policy.js.map +1 -0
- package/dist-server/service/master-conformance-resolver.js +11 -1
- package/dist-server/service/master-conformance-resolver.js.map +1 -1
- package/dist-server/service/master-crud.js +9 -0
- package/dist-server/service/master-crud.js.map +1 -1
- package/dist-server/service/master-list.js +6 -0
- package/dist-server/service/master-list.js.map +1 -1
- package/dist-server/service/material/index.d.ts +3 -2
- package/dist-server/service/material/index.js +4 -2
- package/dist-server/service/material/index.js.map +1 -1
- package/dist-server/service/material/material-axis-status.d.ts +8 -0
- package/dist-server/service/material/material-axis-status.js +19 -0
- package/dist-server/service/material/material-axis-status.js.map +1 -0
- package/dist-server/service/material/material-definition.d.ts +3 -0
- package/dist-server/service/material/material-definition.js +13 -0
- package/dist-server/service/material/material-definition.js.map +1 -1
- package/dist-server/service/material/material-effective-list.d.ts +39 -0
- package/dist-server/service/material/material-effective-list.js +54 -0
- package/dist-server/service/material/material-effective-list.js.map +1 -0
- package/dist-server/service/material/material-master-resolver.js +59 -61
- package/dist-server/service/material/material-master-resolver.js.map +1 -1
- package/dist-server/service/material/material-query.js +9 -2
- package/dist-server/service/material/material-query.js.map +1 -1
- package/dist-server/service/material/material-revision-content.d.ts +10 -0
- package/dist-server/service/material/material-revision-content.js +92 -0
- package/dist-server/service/material/material-revision-content.js.map +1 -0
- package/dist-server/service/material/material-revision-resolver.d.ts +54 -0
- package/dist-server/service/material/material-revision-resolver.js +76 -0
- package/dist-server/service/material/material-revision-resolver.js.map +1 -0
- package/dist-server/service/material/material-revision-service.d.ts +144 -0
- package/dist-server/service/material/material-revision-service.js +534 -0
- package/dist-server/service/material/material-revision-service.js.map +1 -0
- package/dist-server/service/material/material-revision.d.ts +84 -0
- package/dist-server/service/material/material-revision.js +339 -0
- package/dist-server/service/material/material-revision.js.map +1 -0
- package/dist-server/service/operation/index.d.ts +1 -1
- package/dist-server/service/operation/operation-definition.js +1 -0
- package/dist-server/service/operation/operation-definition.js.map +1 -1
- package/dist-server/service/operation/operation-master-resolver.js +30 -30
- package/dist-server/service/operation/operation-master-resolver.js.map +1 -1
- package/dist-server/service/personnel/index.d.ts +2 -2
- package/dist-server/service/personnel/personnel-master-resolver.js +38 -34
- package/dist-server/service/personnel/personnel-master-resolver.js.map +1 -1
- package/dist-server/service/quality/nonconformance-cause-resolver.js +17 -14
- package/dist-server/service/quality/nonconformance-cause-resolver.js.map +1 -1
- package/dist-server/service/recipe/index.d.ts +3 -2
- package/dist-server/service/recipe/index.js +4 -2
- package/dist-server/service/recipe/index.js.map +1 -1
- package/dist-server/service/recipe/recipe-definition.d.ts +4 -0
- package/dist-server/service/recipe/recipe-definition.js +14 -1
- package/dist-server/service/recipe/recipe-definition.js.map +1 -1
- package/dist-server/service/recipe/recipe-effective-list.d.ts +49 -0
- package/dist-server/service/recipe/recipe-effective-list.js +96 -0
- package/dist-server/service/recipe/recipe-effective-list.js.map +1 -0
- package/dist-server/service/recipe/recipe-master-resolver.js +32 -28
- package/dist-server/service/recipe/recipe-master-resolver.js.map +1 -1
- package/dist-server/service/recipe/recipe-query.js +9 -2
- package/dist-server/service/recipe/recipe-query.js.map +1 -1
- package/dist-server/service/recipe/recipe-revision-content.d.ts +32 -0
- package/dist-server/service/recipe/recipe-revision-content.js +65 -0
- package/dist-server/service/recipe/recipe-revision-content.js.map +1 -0
- package/dist-server/service/recipe/recipe-revision-line.d.ts +30 -0
- package/dist-server/service/recipe/recipe-revision-line.js +95 -0
- package/dist-server/service/recipe/recipe-revision-line.js.map +1 -0
- package/dist-server/service/recipe/recipe-revision-resolver.d.ts +59 -0
- package/dist-server/service/recipe/recipe-revision-resolver.js +76 -0
- package/dist-server/service/recipe/recipe-revision-resolver.js.map +1 -0
- package/dist-server/service/recipe/recipe-revision-service.d.ts +147 -0
- package/dist-server/service/recipe/recipe-revision-service.js +591 -0
- package/dist-server/service/recipe/recipe-revision-service.js.map +1 -0
- package/dist-server/service/recipe/recipe-revision.d.ts +100 -0
- package/dist-server/service/recipe/recipe-revision.js +373 -0
- package/dist-server/service/recipe/recipe-revision.js.map +1 -0
- package/dist-server/service/recipe/route-definition.js +1 -0
- package/dist-server/service/recipe/route-definition.js.map +1 -1
- package/dist-server/service/resource/class-members.js +9 -0
- package/dist-server/service/resource/class-members.js.map +1 -1
- package/dist-server/service/status/master-status.js +2 -1
- package/dist-server/service/status/master-status.js.map +1 -1
- package/dist-server/service/test-spec/index.d.ts +3 -2
- package/dist-server/service/test-spec/index.js +4 -2
- package/dist-server/service/test-spec/index.js.map +1 -1
- package/dist-server/service/test-spec/test-spec-effective-list.d.ts +34 -0
- package/dist-server/service/test-spec/test-spec-effective-list.js +42 -0
- package/dist-server/service/test-spec/test-spec-effective-list.js.map +1 -0
- package/dist-server/service/test-spec/test-spec-master-resolver.d.ts +4 -14
- package/dist-server/service/test-spec/test-spec-master-resolver.js +12 -83
- package/dist-server/service/test-spec/test-spec-master-resolver.js.map +1 -1
- package/dist-server/service/test-spec/test-spec-query.js +7 -1
- package/dist-server/service/test-spec/test-spec-query.js.map +1 -1
- package/dist-server/service/test-spec/test-spec-revision-resolver.d.ts +53 -0
- package/dist-server/service/test-spec/test-spec-revision-resolver.js +76 -0
- package/dist-server/service/test-spec/test-spec-revision-resolver.js.map +1 -0
- package/dist-server/service/test-spec/test-spec-revision-service.d.ts +138 -0
- package/dist-server/service/test-spec/test-spec-revision-service.js +538 -0
- package/dist-server/service/test-spec/test-spec-revision-service.js.map +1 -0
- package/dist-server/service/test-spec/test-spec-revision.d.ts +84 -0
- package/dist-server/service/test-spec/test-spec-revision.js +334 -0
- package/dist-server/service/test-spec/test-spec-revision.js.map +1 -0
- package/dist-server/service/test-spec/test-specification.d.ts +6 -0
- package/dist-server/service/test-spec/test-specification.js +15 -2
- package/dist-server/service/test-spec/test-specification.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -6
- package/translations/en.json +148 -0
- package/translations/ko.json +148 -0
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MaterialDefinitionRefusal = void 0;
|
|
4
|
+
exports.materialDefinitionSubmissionHash = materialDefinitionSubmissionHash;
|
|
5
|
+
exports.materialDefinitionApprovalInput = materialDefinitionApprovalInput;
|
|
6
|
+
exports.assertMaterialDefinitionApprovalInput = assertMaterialDefinitionApprovalInput;
|
|
7
|
+
exports.createMaterialDefinitionDraft = createMaterialDefinitionDraft;
|
|
8
|
+
exports.editMaterialDefinitionDraft = editMaterialDefinitionDraft;
|
|
9
|
+
exports.freezeMaterialDefinitionDraft = freezeMaterialDefinitionDraft;
|
|
10
|
+
exports.bindMaterialDefinitionApproval = bindMaterialDefinitionApproval;
|
|
11
|
+
exports.publishMaterialDefinitionRevision = publishMaterialDefinitionRevision;
|
|
12
|
+
exports.publishUnregulatedMaterialDefinition = publishUnregulatedMaterialDefinition;
|
|
13
|
+
exports.resolveMaterialDefinition = resolveMaterialDefinition;
|
|
14
|
+
exports.materialDefinitionRevisionForBasis = materialDefinitionRevisionForBasis;
|
|
15
|
+
exports.captureMaterialDefinitionBasis = captureMaterialDefinitionBasis;
|
|
16
|
+
exports.materialDefinitionRevisionOverview = materialDefinitionRevisionOverview;
|
|
17
|
+
exports.effectiveMaterialDefinition = effectiveMaterialDefinition;
|
|
18
|
+
const node_crypto_1 = require("node:crypto");
|
|
19
|
+
const typeorm_1 = require("typeorm");
|
|
20
|
+
const shell_1 = require("@things-factory/shell");
|
|
21
|
+
const master_change_policy_1 = require("../master-change-policy");
|
|
22
|
+
const material_revision_content_1 = require("./material-revision-content");
|
|
23
|
+
const material_definition_1 = require("./material-definition");
|
|
24
|
+
const material_revision_1 = require("./material-revision");
|
|
25
|
+
class MaterialDefinitionRefusal extends shell_1.Refusal {
|
|
26
|
+
constructor(code, detail) {
|
|
27
|
+
super(code, {}, detail ?? MATERIAL_DEFINITION_REFUSALS[code] ?? 'The material definition change could not be applied.');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.MaterialDefinitionRefusal = MaterialDefinitionRefusal;
|
|
31
|
+
const MATERIAL_DEFINITION_REFUSALS = {
|
|
32
|
+
TRANSACTION_REQUIRED: 'This change requires a transaction.',
|
|
33
|
+
INVALID_INSTANT: 'Enter a valid date and time including its time zone.',
|
|
34
|
+
INVALID_CONTENT: 'The proposed material content is invalid.',
|
|
35
|
+
MATERIAL_NOT_FOUND: 'The material does not exist in this domain.',
|
|
36
|
+
REVISION_NOT_FOUND: 'The revision does not exist in this domain.',
|
|
37
|
+
INVALID_BASE_REVISION: 'The proposed change is based on a different revision.',
|
|
38
|
+
DRAFT_NOT_OWNED: 'Only the draft author can edit or submit it.',
|
|
39
|
+
REVISION_FROZEN: 'This submitted revision cannot be edited. Create another draft.',
|
|
40
|
+
DRAFT_CONFLICT: 'The draft changed. Reload it before saving.',
|
|
41
|
+
PLAN_CONFLICT: 'The approved schedule changed. Review and submit a new draft.',
|
|
42
|
+
INVALID_SUBMISSION: 'The submitted content does not match the frozen revision.',
|
|
43
|
+
AUTHOR_CANNOT_APPROVE: 'The author cannot approve their own change.',
|
|
44
|
+
RETROACTIVE_IMPACT: 'This change would alter material definitions already used by recorded transactions.',
|
|
45
|
+
FUTURE_PLAN_CONFLICT: 'This change conflicts with an approved future schedule.',
|
|
46
|
+
NO_EFFECTIVE_REVISION: 'There is no published revision effective at the business time.',
|
|
47
|
+
AMBIGUOUS_EFFECTIVITY: 'More than one revision is effective at the business time.',
|
|
48
|
+
REVISION_INTEGRITY_FAILURE: 'The stored revision does not match its signed content.',
|
|
49
|
+
BASIS_CONFLICT: 'This transaction already has a different material or business time.',
|
|
50
|
+
APPROVAL_LINE_REQUIRED: 'Configure an approval line before submitting this change.',
|
|
51
|
+
REVISION_REQUIRED: 'Change the material through a revision draft.'
|
|
52
|
+
};
|
|
53
|
+
function refuse(code, detail) {
|
|
54
|
+
throw new MaterialDefinitionRefusal(code, detail);
|
|
55
|
+
}
|
|
56
|
+
function transaction(manager) {
|
|
57
|
+
if (!manager.queryRunner?.isTransactionActive)
|
|
58
|
+
refuse('TRANSACTION_REQUIRED');
|
|
59
|
+
}
|
|
60
|
+
/** Current reads are required after waiting for the subject lock, including MySQL REPEATABLE READ. */
|
|
61
|
+
function currentLock(manager) {
|
|
62
|
+
const driver = manager.connection.options.type;
|
|
63
|
+
return manager.queryRunner?.isTransactionActive && !['sqlite', 'better-sqlite3', 'sqljs'].includes(driver)
|
|
64
|
+
? { lock: { mode: 'pessimistic_write' } }
|
|
65
|
+
: {};
|
|
66
|
+
}
|
|
67
|
+
function instant(value) {
|
|
68
|
+
if (!(value instanceof Date) && (typeof value !== 'string' || !/(Z|[+-]\d{2}:\d{2})$/.test(value)))
|
|
69
|
+
refuse('INVALID_INSTANT');
|
|
70
|
+
const result = new Date(value);
|
|
71
|
+
if (!Number.isFinite(result.getTime()))
|
|
72
|
+
refuse('INVALID_INSTANT');
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
function text(value, field) {
|
|
76
|
+
if (typeof value !== 'string' || !value.trim())
|
|
77
|
+
refuse('INVALID_CONTENT', field);
|
|
78
|
+
return value.trim();
|
|
79
|
+
}
|
|
80
|
+
/** Sorted object keys, ordered arrays, ISO instants. This is the v1 submission encoding. */
|
|
81
|
+
function canonical(value) {
|
|
82
|
+
if (value === null || typeof value !== 'object')
|
|
83
|
+
return JSON.stringify(value);
|
|
84
|
+
if (Array.isArray(value))
|
|
85
|
+
return '[' + value.map(canonical).join(',') + ']';
|
|
86
|
+
return ('{' +
|
|
87
|
+
Object.keys(value)
|
|
88
|
+
.sort()
|
|
89
|
+
.map(key => JSON.stringify(key) + ':' + canonical(value[key]))
|
|
90
|
+
.join(',') +
|
|
91
|
+
'}');
|
|
92
|
+
}
|
|
93
|
+
function materialDefinitionSubmissionHash(revision) {
|
|
94
|
+
return (0, node_crypto_1.createHash)('sha256')
|
|
95
|
+
.update(canonical({
|
|
96
|
+
schemaVersion: 1,
|
|
97
|
+
revisionNumber: revision.revisionNumber,
|
|
98
|
+
policyId: revision.policyId,
|
|
99
|
+
domainId: revision.domainId,
|
|
100
|
+
materialId: revision.materialId,
|
|
101
|
+
baseRevisionId: revision.baseRevisionId,
|
|
102
|
+
baseGeneration: revision.baseGeneration,
|
|
103
|
+
description: revision.description,
|
|
104
|
+
materialClassIds: revision.materialClassIds,
|
|
105
|
+
properties: revision.properties,
|
|
106
|
+
testSpecificationIds: revision.testSpecificationIds,
|
|
107
|
+
effectiveFrom: revision.effectiveFrom.toISOString(),
|
|
108
|
+
reason: revision.reason
|
|
109
|
+
}))
|
|
110
|
+
.digest('hex');
|
|
111
|
+
}
|
|
112
|
+
/** Display values are a checked projection of the frozen revision, never a second editable source. */
|
|
113
|
+
async function materialDefinitionApprovalInput(manager, domainId, revisionId) {
|
|
114
|
+
const draft = await revision(manager, domainId, revisionId);
|
|
115
|
+
if (!draft.frozenAt || draft.contentHash !== materialDefinitionSubmissionHash(draft))
|
|
116
|
+
refuse('INVALID_SUBMISSION');
|
|
117
|
+
const material = await subject(manager, domainId, draft.materialId);
|
|
118
|
+
const base = draft.baseRevisionId ? await revision(manager, domainId, draft.baseRevisionId) : null;
|
|
119
|
+
const plan = base ? await manager.findOneBy(material_revision_1.MaterialDefinitionPlan, {
|
|
120
|
+
domain: { id: domainId }, material: { id: draft.materialId }, generation: draft.baseGeneration
|
|
121
|
+
}) : null;
|
|
122
|
+
const segment = plan && base ? await manager.findOneBy(material_revision_1.MaterialDefinitionSegment, {
|
|
123
|
+
domain: { id: domainId }, plan: { id: plan.id }, revision: { id: base.id }
|
|
124
|
+
}) : null;
|
|
125
|
+
const values = (row) => ({
|
|
126
|
+
revisionNumber: row.revisionNumber,
|
|
127
|
+
detail: {
|
|
128
|
+
revisionId: row.id, createdAt: row.createdAt.toISOString(), frozenAt: row.frozenAt?.toISOString(),
|
|
129
|
+
effectiveFrom: row.effectiveFrom.toISOString(), reason: row.reason,
|
|
130
|
+
...(row.id === base?.id && segment ? { validFrom: segment.validFrom.toISOString(), validTo: segment.validTo?.toISOString() ?? null } : {})
|
|
131
|
+
},
|
|
132
|
+
description: row.description,
|
|
133
|
+
materialClassIds: row.materialClassIds,
|
|
134
|
+
properties: row.properties,
|
|
135
|
+
testSpecificationIds: row.testSpecificationIds
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
headline: material.name,
|
|
139
|
+
reason: draft.reason,
|
|
140
|
+
effectiveFrom: draft.effectiveFrom.toISOString(),
|
|
141
|
+
before: base ? values(base) : null,
|
|
142
|
+
after: values(draft),
|
|
143
|
+
revision: {
|
|
144
|
+
revisionId: draft.id,
|
|
145
|
+
hash: draft.contentHash,
|
|
146
|
+
materialId: draft.materialId,
|
|
147
|
+
baseGeneration: draft.baseGeneration
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
async function assertMaterialDefinitionApprovalInput(manager, domainId, input) {
|
|
152
|
+
const reference = input?.revision;
|
|
153
|
+
if (typeof reference?.revisionId !== 'string')
|
|
154
|
+
refuse('INVALID_SUBMISSION');
|
|
155
|
+
const expected = await materialDefinitionApprovalInput(manager, domainId, reference.revisionId);
|
|
156
|
+
for (const key of Object.keys(expected)) {
|
|
157
|
+
if (canonical(input[key]) !== canonical(expected[key]))
|
|
158
|
+
refuse('INVALID_SUBMISSION');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function content(input) {
|
|
162
|
+
return { ...(0, material_revision_content_1.materialDraftContent)(input), effectiveFrom: instant(input.effectiveFrom), reason: text(input.reason, 'reason') };
|
|
163
|
+
}
|
|
164
|
+
async function subject(manager, domainId, materialId) {
|
|
165
|
+
const material = await manager.findOne(material_definition_1.MaterialDefinition, {
|
|
166
|
+
where: { id: materialId, domain: { id: domainId } },
|
|
167
|
+
...currentLock(manager)
|
|
168
|
+
});
|
|
169
|
+
if (!material)
|
|
170
|
+
refuse('MATERIAL_NOT_FOUND');
|
|
171
|
+
return material;
|
|
172
|
+
}
|
|
173
|
+
async function revision(manager, domainId, revisionId, locked = true) {
|
|
174
|
+
const found = await manager.findOne(material_revision_1.MaterialDefinitionRevision, {
|
|
175
|
+
where: { id: revisionId, domain: { id: domainId } },
|
|
176
|
+
...(locked ? currentLock(manager) : {})
|
|
177
|
+
});
|
|
178
|
+
if (!found)
|
|
179
|
+
refuse('REVISION_NOT_FOUND');
|
|
180
|
+
return found;
|
|
181
|
+
}
|
|
182
|
+
/** A write lock valid on every supported SQL backend, including SQLite (no SELECT FOR UPDATE). */
|
|
183
|
+
async function lockSubject(manager, domainId, materialId) {
|
|
184
|
+
transaction(manager);
|
|
185
|
+
const locked = await manager
|
|
186
|
+
.createQueryBuilder()
|
|
187
|
+
.update(material_definition_1.MaterialDefinition)
|
|
188
|
+
.set({ revisionLock: () => 'revision_lock + 1' })
|
|
189
|
+
.where({ id: materialId, domain: { id: domainId } })
|
|
190
|
+
.execute();
|
|
191
|
+
if (locked.affected !== 1)
|
|
192
|
+
refuse('MATERIAL_NOT_FOUND');
|
|
193
|
+
return subject(manager, domainId, materialId);
|
|
194
|
+
}
|
|
195
|
+
async function createMaterialDefinitionDraft(manager, domainId, creatorId, asked) {
|
|
196
|
+
transaction(manager);
|
|
197
|
+
const accepted = content(asked.content);
|
|
198
|
+
await (0, material_revision_content_1.validateMaterialReferences)(manager, domainId, accepted);
|
|
199
|
+
const policy = await (0, master_change_policy_1.masterChangePolicy)(manager, domainId, 'materialDefinition');
|
|
200
|
+
let material;
|
|
201
|
+
if (asked.materialId) {
|
|
202
|
+
material = await lockSubject(manager, domainId, asked.materialId);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
material = await manager.save(material_definition_1.MaterialDefinition, manager.create(material_definition_1.MaterialDefinition, {
|
|
206
|
+
domain: { id: domainId },
|
|
207
|
+
name: text(asked.name, 'name'),
|
|
208
|
+
planGeneration: 0,
|
|
209
|
+
revisionLock: 0,
|
|
210
|
+
creator: { id: creatorId },
|
|
211
|
+
updater: { id: creatorId }
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
const previous = await manager.findOne(material_revision_1.MaterialDefinitionRevision, {
|
|
215
|
+
where: { domain: { id: domainId }, material: { id: material.id } }, order: { revisionNumber: 'DESC' },
|
|
216
|
+
...currentLock(manager)
|
|
217
|
+
});
|
|
218
|
+
const revisionNumber = (previous?.revisionNumber ?? 0) + 1;
|
|
219
|
+
let baseRevisionId = null;
|
|
220
|
+
if (material.planGeneration > 0) {
|
|
221
|
+
baseRevisionId = (await resolveMaterialDefinition(manager, domainId, material.id, accepted.effectiveFrom)).revision.id;
|
|
222
|
+
}
|
|
223
|
+
if (asked.baseRevisionId && asked.baseRevisionId !== baseRevisionId)
|
|
224
|
+
refuse('INVALID_BASE_REVISION');
|
|
225
|
+
const saved = await manager.save(material_revision_1.MaterialDefinitionRevision, manager.create(material_revision_1.MaterialDefinitionRevision, {
|
|
226
|
+
...accepted,
|
|
227
|
+
policy: { id: policy.id },
|
|
228
|
+
domain: { id: domainId },
|
|
229
|
+
material: { id: material.id },
|
|
230
|
+
baseRevision: baseRevisionId ? { id: baseRevisionId } : null,
|
|
231
|
+
baseGeneration: material.planGeneration,
|
|
232
|
+
revisionNumber,
|
|
233
|
+
editGeneration: 0,
|
|
234
|
+
creator: { id: creatorId },
|
|
235
|
+
createdAt: new Date(),
|
|
236
|
+
frozenAt: null,
|
|
237
|
+
contentHash: null,
|
|
238
|
+
activityInstanceId: null
|
|
239
|
+
}));
|
|
240
|
+
return revision(manager, domainId, saved.id);
|
|
241
|
+
}
|
|
242
|
+
async function editMaterialDefinitionDraft(manager, domainId, actorId, revisionId, expectedEditGeneration, input) {
|
|
243
|
+
transaction(manager);
|
|
244
|
+
const accepted = content(input);
|
|
245
|
+
await (0, material_revision_content_1.validateMaterialReferences)(manager, domainId, accepted);
|
|
246
|
+
const initial = await revision(manager, domainId, revisionId, false);
|
|
247
|
+
const material = await lockSubject(manager, domainId, initial.materialId);
|
|
248
|
+
const draft = await revision(manager, domainId, revisionId);
|
|
249
|
+
if (draft.creatorId !== actorId)
|
|
250
|
+
refuse('DRAFT_NOT_OWNED');
|
|
251
|
+
if (draft.frozenAt)
|
|
252
|
+
refuse('REVISION_FROZEN');
|
|
253
|
+
if (draft.baseGeneration !== material.planGeneration)
|
|
254
|
+
refuse('PLAN_CONFLICT');
|
|
255
|
+
// Moving the effective date across a scheduled boundary changes the before-image.
|
|
256
|
+
const baseRevisionId = material.planGeneration > 0
|
|
257
|
+
? (await resolveMaterialDefinition(manager, domainId, material.id, accepted.effectiveFrom)).revision.id
|
|
258
|
+
: null;
|
|
259
|
+
const updated = await manager
|
|
260
|
+
.createQueryBuilder()
|
|
261
|
+
.update(material_revision_1.MaterialDefinitionRevision)
|
|
262
|
+
.set({
|
|
263
|
+
...accepted,
|
|
264
|
+
baseRevision: baseRevisionId ? { id: baseRevisionId } : null,
|
|
265
|
+
editGeneration: expectedEditGeneration + 1
|
|
266
|
+
})
|
|
267
|
+
.where({
|
|
268
|
+
id: revisionId,
|
|
269
|
+
domain: { id: domainId },
|
|
270
|
+
creator: { id: actorId },
|
|
271
|
+
editGeneration: expectedEditGeneration
|
|
272
|
+
})
|
|
273
|
+
.andWhere('frozen_at IS NULL')
|
|
274
|
+
.execute();
|
|
275
|
+
if (updated.affected !== 1)
|
|
276
|
+
refuse('DRAFT_CONFLICT');
|
|
277
|
+
return revision(manager, domainId, revisionId);
|
|
278
|
+
}
|
|
279
|
+
/** Product calls this in the same transaction as issuing its worklist approval. */
|
|
280
|
+
async function freezeMaterialDefinitionDraft(manager, domainId, actorId, revisionId, expectedEditGeneration) {
|
|
281
|
+
const initial = await revision(manager, domainId, revisionId, false);
|
|
282
|
+
const material = await lockSubject(manager, domainId, initial.materialId);
|
|
283
|
+
const draft = await revision(manager, domainId, revisionId);
|
|
284
|
+
if (draft.creatorId !== actorId)
|
|
285
|
+
refuse('DRAFT_NOT_OWNED');
|
|
286
|
+
if (draft.frozenAt)
|
|
287
|
+
refuse('REVISION_FROZEN');
|
|
288
|
+
if (draft.editGeneration !== expectedEditGeneration)
|
|
289
|
+
refuse('DRAFT_CONFLICT');
|
|
290
|
+
if (draft.baseGeneration !== material.planGeneration)
|
|
291
|
+
refuse('PLAN_CONFLICT');
|
|
292
|
+
const policy = await (0, master_change_policy_1.masterChangePolicy)(manager, domainId, 'materialDefinition');
|
|
293
|
+
if (draft.policyId !== policy.id)
|
|
294
|
+
refuse('PLAN_CONFLICT');
|
|
295
|
+
(0, material_revision_content_1.materialDraftContent)(draft, true);
|
|
296
|
+
await (0, material_revision_content_1.validateMaterialReferences)(manager, domainId, draft);
|
|
297
|
+
const contentHash = materialDefinitionSubmissionHash(draft);
|
|
298
|
+
await manager.update(material_revision_1.MaterialDefinitionRevision, { id: revisionId, domain: { id: domainId } }, { frozenAt: new Date(), contentHash });
|
|
299
|
+
return revision(manager, domainId, revisionId);
|
|
300
|
+
}
|
|
301
|
+
async function bindMaterialDefinitionApproval(manager, domainId, revisionId, activityInstanceId) {
|
|
302
|
+
transaction(manager);
|
|
303
|
+
const initial = await revision(manager, domainId, revisionId, false);
|
|
304
|
+
await lockSubject(manager, domainId, initial.materialId);
|
|
305
|
+
const draft = await revision(manager, domainId, revisionId);
|
|
306
|
+
if (!draft.frozenAt || draft.activityInstanceId)
|
|
307
|
+
refuse('INVALID_SUBMISSION');
|
|
308
|
+
await manager.update(material_revision_1.MaterialDefinitionRevision, { id: revisionId, domain: { id: domainId } }, { activityInstanceId: text(activityInstanceId, 'activityInstanceId') });
|
|
309
|
+
}
|
|
310
|
+
/** Internal completion callback only. Never expose this as an approved=true mutation. */
|
|
311
|
+
async function publishMaterialDefinitionRevision(manager, domainId, revisionId, decision) {
|
|
312
|
+
return publishRevision(manager, domainId, revisionId, {
|
|
313
|
+
kind: 'approved',
|
|
314
|
+
activityInstanceId: decision.activityInstanceId,
|
|
315
|
+
actorId: decision.approverId,
|
|
316
|
+
submissionHash: decision.submissionHash
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
async function publishUnregulatedMaterialDefinition(manager, domainId, actorId, revisionId, editGeneration) {
|
|
320
|
+
transaction(manager);
|
|
321
|
+
const initial = await revision(manager, domainId, revisionId, false);
|
|
322
|
+
await lockSubject(manager, domainId, initial.materialId);
|
|
323
|
+
const policy = await (0, master_change_policy_1.masterChangePolicy)(manager, domainId, 'materialDefinition');
|
|
324
|
+
if (policy.approvalRequired)
|
|
325
|
+
throw new shell_1.Refusal('MASTER_APPROVAL_REQUIRED', {}, 'This change requires product approval.');
|
|
326
|
+
const prior = await manager.findOneBy(material_revision_1.MaterialDefinitionPlan, {
|
|
327
|
+
domain: { id: domainId },
|
|
328
|
+
publishedRevision: { id: revisionId },
|
|
329
|
+
releaseKind: 'policy'
|
|
330
|
+
});
|
|
331
|
+
if (prior) {
|
|
332
|
+
if (prior.publisherId !== actorId)
|
|
333
|
+
refuse('DRAFT_NOT_OWNED');
|
|
334
|
+
return prior;
|
|
335
|
+
}
|
|
336
|
+
const frozen = await freezeMaterialDefinitionDraft(manager, domainId, actorId, revisionId, editGeneration);
|
|
337
|
+
return publishRevision(manager, domainId, revisionId, { kind: 'policy', actorId, submissionHash: frozen.contentHash });
|
|
338
|
+
}
|
|
339
|
+
async function publishRevision(manager, domainId, revisionId, decision) {
|
|
340
|
+
const initial = await revision(manager, domainId, revisionId, false);
|
|
341
|
+
const material = await lockSubject(manager, domainId, initial.materialId);
|
|
342
|
+
const draft = await revision(manager, domainId, revisionId);
|
|
343
|
+
if (!draft.frozenAt ||
|
|
344
|
+
(decision.kind === 'approved'
|
|
345
|
+
? draft.activityInstanceId !== decision.activityInstanceId
|
|
346
|
+
: !!draft.activityInstanceId) ||
|
|
347
|
+
draft.contentHash !== decision.submissionHash ||
|
|
348
|
+
materialDefinitionSubmissionHash(draft) !== decision.submissionHash)
|
|
349
|
+
refuse('INVALID_SUBMISSION');
|
|
350
|
+
const policy = await (0, master_change_policy_1.masterChangePolicy)(manager, domainId, 'materialDefinition');
|
|
351
|
+
if (draft.policyId !== policy.id)
|
|
352
|
+
refuse('PLAN_CONFLICT');
|
|
353
|
+
if (policy.approvalRequired !== (decision.kind === 'approved'))
|
|
354
|
+
throw new shell_1.Refusal('MASTER_APPROVAL_POLICY_MISMATCH', {}, 'Use the publication path required by the domain policy.');
|
|
355
|
+
if (decision.kind === 'approved' && draft.creatorId === decision.actorId)
|
|
356
|
+
refuse('AUTHOR_CANNOT_APPROVE');
|
|
357
|
+
text(decision.actorId, 'publisher');
|
|
358
|
+
const previousDecision = await manager.findOne(material_revision_1.MaterialDefinitionPlan, {
|
|
359
|
+
where: { domain: { id: domainId }, publishedRevision: { id: revisionId } },
|
|
360
|
+
...currentLock(manager)
|
|
361
|
+
});
|
|
362
|
+
if (previousDecision)
|
|
363
|
+
return previousDecision;
|
|
364
|
+
if (draft.baseGeneration !== material.planGeneration)
|
|
365
|
+
refuse('PLAN_CONFLICT');
|
|
366
|
+
// Only replacement from effectiveFrom onward is supported in this slice, not arbitrary rescheduling.
|
|
367
|
+
await (0, material_revision_content_1.validateMaterialReferences)(manager, domainId, draft);
|
|
368
|
+
if (await manager.findOne(material_revision_1.MaterialDefinitionBasis, {
|
|
369
|
+
where: { domain: { id: domainId }, material: { id: material.id }, businessTime: (0, typeorm_1.MoreThanOrEqual)(draft.effectiveFrom) },
|
|
370
|
+
...currentLock(manager)
|
|
371
|
+
}))
|
|
372
|
+
refuse('RETROACTIVE_IMPACT');
|
|
373
|
+
const oldPlan = await manager.findOne(material_revision_1.MaterialDefinitionPlan, {
|
|
374
|
+
where: { domain: { id: domainId }, material: { id: material.id }, generation: material.planGeneration },
|
|
375
|
+
...currentLock(manager)
|
|
376
|
+
});
|
|
377
|
+
if (material.planGeneration > 0 && !oldPlan)
|
|
378
|
+
refuse('PLAN_CONFLICT');
|
|
379
|
+
const oldSegments = oldPlan
|
|
380
|
+
? await manager.find(material_revision_1.MaterialDefinitionSegment, {
|
|
381
|
+
where: { domain: { id: domainId }, material: { id: material.id }, plan: { id: oldPlan.id } },
|
|
382
|
+
order: { validFrom: 'ASC' },
|
|
383
|
+
...currentLock(manager)
|
|
384
|
+
})
|
|
385
|
+
: [];
|
|
386
|
+
// Do not silently erase an already approved future appointment.
|
|
387
|
+
if (oldSegments.some(segment => segment.validFrom >= draft.effectiveFrom))
|
|
388
|
+
refuse('FUTURE_PLAN_CONFLICT');
|
|
389
|
+
const recordedAt = new Date();
|
|
390
|
+
const plan = await manager.save(material_revision_1.MaterialDefinitionPlan, manager.create(material_revision_1.MaterialDefinitionPlan, {
|
|
391
|
+
domain: { id: domainId },
|
|
392
|
+
material: { id: material.id },
|
|
393
|
+
generation: material.planGeneration + 1,
|
|
394
|
+
recordedAt,
|
|
395
|
+
activityInstanceId: decision.activityInstanceId ?? null,
|
|
396
|
+
submissionHash: decision.submissionHash,
|
|
397
|
+
approver: decision.kind === 'approved' ? { id: decision.actorId } : null,
|
|
398
|
+
publisher: { id: decision.actorId },
|
|
399
|
+
releaseKind: decision.kind,
|
|
400
|
+
publishedRevision: { id: revisionId },
|
|
401
|
+
policy: { id: policy.id }
|
|
402
|
+
}));
|
|
403
|
+
for (const old of oldSegments) {
|
|
404
|
+
await manager.save(material_revision_1.MaterialDefinitionSegment, manager.create(material_revision_1.MaterialDefinitionSegment, {
|
|
405
|
+
domain: { id: domainId },
|
|
406
|
+
material: { id: material.id },
|
|
407
|
+
plan: { id: plan.id },
|
|
408
|
+
revision: { id: old.revisionId },
|
|
409
|
+
validFrom: old.validFrom,
|
|
410
|
+
validTo: !old.validTo || old.validTo > draft.effectiveFrom ? draft.effectiveFrom : old.validTo
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
413
|
+
await manager.save(material_revision_1.MaterialDefinitionSegment, manager.create(material_revision_1.MaterialDefinitionSegment, {
|
|
414
|
+
domain: { id: domainId },
|
|
415
|
+
material: { id: material.id },
|
|
416
|
+
plan: { id: plan.id },
|
|
417
|
+
revision: { id: revisionId },
|
|
418
|
+
validFrom: draft.effectiveFrom,
|
|
419
|
+
validTo: null
|
|
420
|
+
}));
|
|
421
|
+
const advanced = await manager.update(material_definition_1.MaterialDefinition, { id: material.id, domain: { id: domainId }, planGeneration: material.planGeneration }, { planGeneration: plan.generation });
|
|
422
|
+
if (advanced.affected !== 1)
|
|
423
|
+
refuse('PLAN_CONFLICT');
|
|
424
|
+
return plan;
|
|
425
|
+
}
|
|
426
|
+
async function resolveMaterialDefinition(manager, domainId, materialId, businessTime, knownAt = new Date()) {
|
|
427
|
+
const at = instant(businessTime);
|
|
428
|
+
const known = instant(knownAt);
|
|
429
|
+
const plan = await manager.findOne(material_revision_1.MaterialDefinitionPlan, {
|
|
430
|
+
where: { domain: { id: domainId }, material: { id: materialId }, recordedAt: (0, typeorm_1.LessThanOrEqual)(known) },
|
|
431
|
+
order: { generation: 'DESC' },
|
|
432
|
+
...currentLock(manager)
|
|
433
|
+
});
|
|
434
|
+
if (!plan)
|
|
435
|
+
refuse('NO_EFFECTIVE_REVISION');
|
|
436
|
+
const query = manager
|
|
437
|
+
.createQueryBuilder(material_revision_1.MaterialDefinitionSegment, 'segment')
|
|
438
|
+
.where({ domain: { id: domainId }, material: { id: materialId }, plan: { id: plan.id } })
|
|
439
|
+
.andWhere('segment.validFrom <= :at', { at })
|
|
440
|
+
.andWhere('(segment.validTo IS NULL OR segment.validTo > :at)', { at });
|
|
441
|
+
if (currentLock(manager).lock)
|
|
442
|
+
query.setLock('pessimistic_write');
|
|
443
|
+
const segments = await query.getMany();
|
|
444
|
+
if (segments.length !== 1)
|
|
445
|
+
refuse(segments.length ? 'AMBIGUOUS_EFFECTIVITY' : 'NO_EFFECTIVE_REVISION');
|
|
446
|
+
const selected = await revision(manager, domainId, segments[0].revisionId);
|
|
447
|
+
if (!selected.frozenAt || selected.contentHash !== materialDefinitionSubmissionHash(selected))
|
|
448
|
+
refuse('REVISION_INTEGRITY_FAILURE');
|
|
449
|
+
return { revision: selected, plan, segment: segments[0] };
|
|
450
|
+
}
|
|
451
|
+
/** Replay reads verify the captured digest too, not only the currently effective schedule. */
|
|
452
|
+
async function materialDefinitionRevisionForBasis(manager, domainId, basis) {
|
|
453
|
+
if (basis.domainId !== domainId)
|
|
454
|
+
refuse('BASIS_CONFLICT');
|
|
455
|
+
const selected = await revision(manager, domainId, basis.revisionId);
|
|
456
|
+
if (selected.materialId !== basis.materialId || !selected.frozenAt || selected.contentHash !== basis.contentHash || materialDefinitionSubmissionHash(selected) !== basis.contentHash) {
|
|
457
|
+
refuse('REVISION_INTEGRITY_FAILURE');
|
|
458
|
+
}
|
|
459
|
+
const plan = await manager.findOneBy(material_revision_1.MaterialDefinitionPlan, { id: basis.planId, domain: { id: domainId }, material: { id: basis.materialId } });
|
|
460
|
+
const segments = await manager.findBy(material_revision_1.MaterialDefinitionSegment, { domain: { id: domainId }, material: { id: basis.materialId }, plan: { id: basis.planId } });
|
|
461
|
+
const matching = segments.filter(s => s.validFrom <= basis.businessTime && (!s.validTo || s.validTo > basis.businessTime));
|
|
462
|
+
if (!plan || plan.recordedAt > basis.resolvedAt || matching.length !== 1 || matching[0].revisionId !== basis.revisionId)
|
|
463
|
+
refuse('BASIS_CONFLICT');
|
|
464
|
+
return selected;
|
|
465
|
+
}
|
|
466
|
+
/** Must run inside the transaction that writes the business record. */
|
|
467
|
+
async function captureMaterialDefinitionBasis(manager, domainId, materialId, recordKey, businessTime) {
|
|
468
|
+
const at = instant(businessTime);
|
|
469
|
+
await lockSubject(manager, domainId, materialId);
|
|
470
|
+
const existing = await manager.findOne(material_revision_1.MaterialDefinitionBasis, {
|
|
471
|
+
where: { domain: { id: domainId }, recordKey },
|
|
472
|
+
...currentLock(manager)
|
|
473
|
+
});
|
|
474
|
+
if (existing) {
|
|
475
|
+
if (existing.materialId !== materialId || existing.businessTime.getTime() !== at.getTime())
|
|
476
|
+
refuse('BASIS_CONFLICT');
|
|
477
|
+
await materialDefinitionRevisionForBasis(manager, domainId, existing);
|
|
478
|
+
return existing;
|
|
479
|
+
}
|
|
480
|
+
const resolvedAt = new Date();
|
|
481
|
+
const selected = await resolveMaterialDefinition(manager, domainId, materialId, at, resolvedAt);
|
|
482
|
+
const saved = await manager.save(material_revision_1.MaterialDefinitionBasis, manager.create(material_revision_1.MaterialDefinitionBasis, {
|
|
483
|
+
domain: { id: domainId },
|
|
484
|
+
material: { id: materialId },
|
|
485
|
+
recordKey: text(recordKey, 'recordKey'),
|
|
486
|
+
businessTime: at,
|
|
487
|
+
resolvedAt,
|
|
488
|
+
revision: { id: selected.revision.id },
|
|
489
|
+
contentHash: selected.revision.contentHash,
|
|
490
|
+
plan: { id: selected.plan.id }
|
|
491
|
+
}));
|
|
492
|
+
return manager.findOneByOrFail(material_revision_1.MaterialDefinitionBasis, { id: saved.id, domain: { id: domainId } });
|
|
493
|
+
}
|
|
494
|
+
async function materialDefinitionRevisionOverview(manager, domainId, materialId, at = new Date()) {
|
|
495
|
+
const material = await subject(manager, domainId, materialId);
|
|
496
|
+
const revisions = await manager.find(material_revision_1.MaterialDefinitionRevision, {
|
|
497
|
+
where: { domain: { id: domainId }, material: { id: materialId } },
|
|
498
|
+
order: { createdAt: 'ASC', id: 'ASC' }
|
|
499
|
+
});
|
|
500
|
+
const plan = await manager.findOne(material_revision_1.MaterialDefinitionPlan, {
|
|
501
|
+
where: { domain: { id: domainId }, material: { id: materialId }, generation: material.planGeneration }
|
|
502
|
+
});
|
|
503
|
+
const schedule = plan
|
|
504
|
+
? await manager.find(material_revision_1.MaterialDefinitionSegment, {
|
|
505
|
+
where: { domain: { id: domainId }, material: { id: materialId }, plan: { id: plan.id } },
|
|
506
|
+
order: { validFrom: 'ASC' }
|
|
507
|
+
})
|
|
508
|
+
: [];
|
|
509
|
+
return {
|
|
510
|
+
materialId,
|
|
511
|
+
name: material.name,
|
|
512
|
+
generation: material.planGeneration,
|
|
513
|
+
at: at.toISOString(),
|
|
514
|
+
schedule: schedule.map(segment => ({
|
|
515
|
+
...segment,
|
|
516
|
+
state: segment.validFrom > at ? 'scheduled' : segment.validTo && segment.validTo <= at ? 'expired' : 'current'
|
|
517
|
+
})),
|
|
518
|
+
revisions: revisions.map(item => ({ ...item, state: item.frozenAt ? 'frozen' : 'draft' }))
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
/** Read-time projection, never a clock-driven rewrite of the identity row. */
|
|
522
|
+
async function effectiveMaterialDefinition(manager, domainId, material, at = new Date()) {
|
|
523
|
+
const selected = await resolveMaterialDefinition(manager, domainId, material.id, at);
|
|
524
|
+
return Object.assign(new material_definition_1.MaterialDefinition(), material, {
|
|
525
|
+
revisionNumber: selected.revision.revisionNumber,
|
|
526
|
+
description: selected.revision.description,
|
|
527
|
+
materialClassIds: selected.revision.materialClassIds,
|
|
528
|
+
properties: selected.revision.properties,
|
|
529
|
+
testSpecificationIds: selected.revision.testSpecificationIds,
|
|
530
|
+
effectiveStart: selected.segment.validFrom,
|
|
531
|
+
effectiveEnd: selected.segment.validTo ?? undefined
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
//# sourceMappingURL=material-revision-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"material-revision-service.js","sourceRoot":"","sources":["../../../server/service/material/material-revision-service.ts"],"names":[],"mappings":";;;AAiFA,4EAoBC;AAGD,0EAoCC;AAED,sFAWC;AA0CD,sEAuDC;AAED,kEAwCC;AAGD,sEAyBC;AAED,wEAgBC;AAGD,8EAYC;AAED,oFAwBC;AAyGD,8DA2BC;AAGD,gFAWC;AAGD,wEAkCC;AAED,gFA+BC;AAGD,kEAgBC;AAtmBD,6CAAwC;AACxC,qCAAyE;AACzE,iDAA+C;AAC/C,kEAA4D;AAC5D,2EAA8F;AAC9F,+DAA0D;AAC1D,2DAK4B;AAE5B,MAAa,yBAA0B,SAAQ,eAAO;IACpD,YAAY,IAAY,EAAE,MAAe;QACvC,KAAK,CACH,IAAI,EACJ,EAAE,EACF,MAAM,IAAI,4BAA4B,CAAC,IAAI,CAAC,IAAI,sDAAsD,CACvG,CAAA;IACH,CAAC;CACF;AARD,8DAQC;AACD,MAAM,4BAA4B,GAA2B;IAC3D,oBAAoB,EAAE,qCAAqC;IAC3D,eAAe,EAAE,sDAAsD;IACvE,eAAe,EAAE,2CAA2C;IAC5D,kBAAkB,EAAE,6CAA6C;IACjE,kBAAkB,EAAE,6CAA6C;IACjE,qBAAqB,EAAE,uDAAuD;IAC9E,eAAe,EAAE,8CAA8C;IAC/D,eAAe,EAAE,iEAAiE;IAClF,cAAc,EAAE,6CAA6C;IAC7D,aAAa,EAAE,+DAA+D;IAC9E,kBAAkB,EAAE,2DAA2D;IAC/E,qBAAqB,EAAE,6CAA6C;IACpE,kBAAkB,EAAE,qFAAqF;IACzG,oBAAoB,EAAE,yDAAyD;IAC/E,qBAAqB,EAAE,gEAAgE;IACvF,qBAAqB,EAAE,2DAA2D;IAClF,0BAA0B,EAAE,wDAAwD;IACpF,cAAc,EAAE,qEAAqE;IACrF,sBAAsB,EAAE,2DAA2D;IACnF,iBAAiB,EAAE,+CAA+C;CACnE,CAAA;AACD,SAAS,MAAM,CAAC,IAAY,EAAE,MAAe;IAC3C,MAAM,IAAI,yBAAyB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AACnD,CAAC;AACD,SAAS,WAAW,CAAC,OAAsB;IACzC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,mBAAmB;QAAE,MAAM,CAAC,sBAAsB,CAAC,CAAA;AAC/E,CAAC;AACD,sGAAsG;AACtG,SAAS,WAAW,CAAC,OAAsB;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAA;IAC9C,OAAO,OAAO,CAAC,WAAW,EAAE,mBAAmB,IAAI,CAAC,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;QACxG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,mBAA4B,EAAE,EAAE;QAClD,CAAC,CAAC,EAAE,CAAA;AACR,CAAC;AACD,SAAS,OAAO,CAAC,KAAoB;IACnC,IAAI,CAAC,CAAC,KAAK,YAAY,IAAI,CAAC,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChG,MAAM,CAAC,iBAAiB,CAAC,CAAA;IAC3B,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAA;IAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAAE,MAAM,CAAC,iBAAiB,CAAC,CAAA;IACjE,OAAO,MAAM,CAAA;AACf,CAAC;AACD,SAAS,IAAI,CAAC,KAAc,EAAE,KAAa;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAA;IAChF,OAAO,KAAK,CAAC,IAAI,EAAE,CAAA;AACrB,CAAC;AACD,4FAA4F;AAC5F,SAAS,SAAS,CAAC,KAAU;IAC3B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC7E,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;IAC3E,OAAO,CACL,GAAG;QACH,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;aACf,IAAI,EAAE;aACN,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;aAC7D,IAAI,CAAC,GAAG,CAAC;QACZ,GAAG,CACJ,CAAA;AACH,CAAC;AACD,SAAgB,gCAAgC,CAAC,QAAoC;IACnF,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC;SACxB,MAAM,CACL,SAAS,CAAC;QACR,aAAa,EAAE,CAAC;QAChB,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;QAC3C,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,oBAAoB,EAAE,QAAQ,CAAC,oBAAoB;QACnD,aAAa,EAAE,QAAQ,CAAC,aAAa,CAAC,WAAW,EAAE;QACnD,MAAM,EAAE,QAAQ,CAAC,MAAM;KACxB,CAAC,CACH;SACA,MAAM,CAAC,KAAK,CAAC,CAAA;AAClB,CAAC;AAED,sGAAsG;AAC/F,KAAK,UAAU,+BAA+B,CAAC,OAAsB,EAAE,QAAgB,EAAE,UAAkB;IAChH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;IAC3D,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,WAAW,KAAK,gCAAgC,CAAC,KAAK,CAAC;QAAE,MAAM,CAAC,oBAAoB,CAAC,CAAA;IAClH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;IACnE,MAAM,IAAI,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAClG,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC,0CAAsB,EAAE;QAClE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC,cAAc;KAC/F,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACT,MAAM,OAAO,GAAG,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC,6CAAyB,EAAE;QAChF,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;KAC3E,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACT,MAAM,MAAM,GAAG,CAAC,GAA+B,EAAE,EAAE,CAAC,CAAC;QACnD,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,MAAM,EAAE;YACN,UAAU,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE;YACjG,aAAa,EAAE,GAAG,CAAC,aAAa,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM;YAClE,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,IAAI,EAAE,EAAE,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3I;QACD,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;QACtC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,oBAAoB,EAAE,GAAG,CAAC,oBAAoB;KAC/C,CAAC,CAAA;IACF,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,IAAI;QACvB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC,WAAW,EAAE;QAChD,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAClC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;QACpB,QAAQ,EAAE;YACR,UAAU,EAAE,KAAK,CAAC,EAAE;YACpB,IAAI,EAAE,KAAK,CAAC,WAAW;YACvB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,cAAc,EAAE,KAAK,CAAC,cAAc;SACrC;KACF,CAAA;AACH,CAAC;AAEM,KAAK,UAAU,qCAAqC,CACzD,OAAsB,EACtB,QAAgB,EAChB,KAA8B;IAE9B,MAAM,SAAS,GAAG,KAAK,EAAE,QAAmC,CAAA;IAC5D,IAAI,OAAO,SAAS,EAAE,UAAU,KAAK,QAAQ;QAAE,MAAM,CAAC,oBAAoB,CAAC,CAAA;IAC3E,MAAM,QAAQ,GAAG,MAAM,+BAA+B,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,UAAU,CAAC,CAAA;IAC/F,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,IAAI,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAAE,MAAM,CAAC,oBAAoB,CAAC,CAAA;IACtF,CAAC;AACH,CAAC;AAUD,SAAS,OAAO,CAAC,KAAqC;IACpD,OAAO,EAAE,GAAG,IAAA,gDAAoB,EAAC,KAAK,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAA;AAC9H,CAAC;AACD,KAAK,UAAU,OAAO,CAAC,OAAsB,EAAE,QAAgB,EAAE,UAAkB;IACjF,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,wCAAkB,EAAE;QACzD,KAAK,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE;QACnD,GAAG,WAAW,CAAC,OAAO,CAAC;KACxB,CAAC,CAAA;IACF,IAAI,CAAC,QAAQ;QAAE,MAAM,CAAC,oBAAoB,CAAC,CAAA;IAC3C,OAAO,QAAQ,CAAA;AACjB,CAAC;AACD,KAAK,UAAU,QAAQ,CAAC,OAAsB,EAAE,QAAgB,EAAE,UAAkB,EAAE,MAAM,GAAG,IAAI;IACjG,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,8CAA0B,EAAE;QAC9D,KAAK,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE;QACnD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACxC,CAAC,CAAA;IACF,IAAI,CAAC,KAAK;QAAE,MAAM,CAAC,oBAAoB,CAAC,CAAA;IACxC,OAAO,KAAK,CAAA;AACd,CAAC;AACD,kGAAkG;AAClG,KAAK,UAAU,WAAW,CAAC,OAAsB,EAAE,QAAgB,EAAE,UAAkB;IACrF,WAAW,CAAC,OAAO,CAAC,CAAA;IACpB,MAAM,MAAM,GAAG,MAAM,OAAO;SACzB,kBAAkB,EAAE;SACpB,MAAM,CAAC,wCAAkB,CAAC;SAC1B,GAAG,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAAC;SAChD,KAAK,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC;SACnD,OAAO,EAAE,CAAA;IACZ,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC;QAAE,MAAM,CAAC,oBAAoB,CAAC,CAAA;IACvD,OAAO,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;AAC/C,CAAC;AAEM,KAAK,UAAU,6BAA6B,CACjD,OAAsB,EACtB,QAAgB,EAChB,SAAiB,EACjB,KAA+G;IAE/G,WAAW,CAAC,OAAO,CAAC,CAAA;IACpB,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACvC,MAAM,IAAA,sDAA0B,EAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC7D,MAAM,MAAM,GAAG,MAAM,IAAA,yCAAkB,EAAC,OAAO,EAAE,QAAQ,EAAE,oBAAoB,CAAC,CAAA;IAChF,IAAI,QAA4B,CAAA;IAChC,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,QAAQ,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;IACnE,CAAC;SAAM,CAAC;QACN,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAC3B,wCAAkB,EAClB,OAAO,CAAC,MAAM,CAAC,wCAAkB,EAAE;YACjC,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE;YACxB,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC;YAC9B,cAAc,EAAE,CAAC;YACjB,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YAC1B,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;SAC3B,CAAC,CACH,CAAA;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,8CAA0B,EAAE;QACjE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE;QACrG,GAAG,WAAW,CAAC,OAAO,CAAC;KACxB,CAAC,CAAA;IACF,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,cAAc,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;IAC1D,IAAI,cAAc,GAAkB,IAAI,CAAA;IACxC,IAAI,QAAQ,CAAC,cAAc,GAAG,CAAC,EAAE,CAAC;QAChC,cAAc,GAAG,CAAC,MAAM,yBAAyB,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAA;IACxH,CAAC;IACD,IAAI,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC,cAAc,KAAK,cAAc;QAAE,MAAM,CAAC,uBAAuB,CAAC,CAAA;IACpG,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,CAC9B,8CAA0B,EAC1B,OAAO,CAAC,MAAM,CAAC,8CAA0B,EAAE;QACzC,GAAG,QAAQ;QACX,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;QACzB,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE;QACxB,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE;QAC7B,YAAY,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,IAAI;QAC5D,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,cAAc;QACd,cAAc,EAAE,CAAC;QACjB,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;QAC1B,SAAS,EAAE,IAAI,IAAI,EAAE;QACrB,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,IAAI;QACjB,kBAAkB,EAAE,IAAI;KACzB,CAAC,CACH,CAAA;IACD,OAAO,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,CAAA;AAC9C,CAAC;AAEM,KAAK,UAAU,2BAA2B,CAC/C,OAAsB,EACtB,QAAgB,EAChB,OAAe,EACf,UAAkB,EAClB,sBAA8B,EAC9B,KAAqC;IAErC,WAAW,CAAC,OAAO,CAAC,CAAA;IACpB,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;IAC/B,MAAM,IAAA,sDAA0B,EAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC7D,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,CAAA;IACpE,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACzE,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;IAC3D,IAAI,KAAK,CAAC,SAAS,KAAK,OAAO;QAAE,MAAM,CAAC,iBAAiB,CAAC,CAAA;IAC1D,IAAI,KAAK,CAAC,QAAQ;QAAE,MAAM,CAAC,iBAAiB,CAAC,CAAA;IAC7C,IAAI,KAAK,CAAC,cAAc,KAAK,QAAQ,CAAC,cAAc;QAAE,MAAM,CAAC,eAAe,CAAC,CAAA;IAC7E,kFAAkF;IAClF,MAAM,cAAc,GAClB,QAAQ,CAAC,cAAc,GAAG,CAAC;QACzB,CAAC,CAAC,CAAC,MAAM,yBAAyB,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE;QACvG,CAAC,CAAC,IAAI,CAAA;IACV,MAAM,OAAO,GAAG,MAAM,OAAO;SAC1B,kBAAkB,EAAE;SACpB,MAAM,CAAC,8CAA0B,CAAC;SAClC,GAAG,CAAC;QACH,GAAG,QAAQ;QACX,YAAY,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,IAAI;QAC5D,cAAc,EAAE,sBAAsB,GAAG,CAAC;KAC3C,CAAC;SACD,KAAK,CAAC;QACL,EAAE,EAAE,UAAU;QACd,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE;QACxB,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACxB,cAAc,EAAE,sBAAsB;KACvC,CAAC;SACD,QAAQ,CAAC,mBAAmB,CAAC;SAC7B,OAAO,EAAE,CAAA;IACZ,IAAI,OAAO,CAAC,QAAQ,KAAK,CAAC;QAAE,MAAM,CAAC,gBAAgB,CAAC,CAAA;IACpD,OAAO,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;AAChD,CAAC;AAED,mFAAmF;AAC5E,KAAK,UAAU,6BAA6B,CACjD,OAAsB,EACtB,QAAgB,EAChB,OAAe,EACf,UAAkB,EAClB,sBAA8B;IAE9B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,CAAA;IACpE,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACzE,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;IAC3D,IAAI,KAAK,CAAC,SAAS,KAAK,OAAO;QAAE,MAAM,CAAC,iBAAiB,CAAC,CAAA;IAC1D,IAAI,KAAK,CAAC,QAAQ;QAAE,MAAM,CAAC,iBAAiB,CAAC,CAAA;IAC7C,IAAI,KAAK,CAAC,cAAc,KAAK,sBAAsB;QAAE,MAAM,CAAC,gBAAgB,CAAC,CAAA;IAC7E,IAAI,KAAK,CAAC,cAAc,KAAK,QAAQ,CAAC,cAAc;QAAE,MAAM,CAAC,eAAe,CAAC,CAAA;IAC7E,MAAM,MAAM,GAAG,MAAM,IAAA,yCAAkB,EAAC,OAAO,EAAE,QAAQ,EAAE,oBAAoB,CAAC,CAAA;IAChF,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,eAAe,CAAC,CAAA;IACzD,IAAA,gDAAoB,EAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IACjC,MAAM,IAAA,sDAA0B,EAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAA;IAC1D,MAAM,WAAW,GAAG,gCAAgC,CAAC,KAAK,CAAC,CAAA;IAC3D,MAAM,OAAO,CAAC,MAAM,CAClB,8CAA0B,EAC1B,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAC5C,EAAE,QAAQ,EAAE,IAAI,IAAI,EAAE,EAAE,WAAW,EAAE,CACtC,CAAA;IACD,OAAO,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;AAChD,CAAC;AAEM,KAAK,UAAU,8BAA8B,CAClD,OAAsB,EACtB,QAAgB,EAChB,UAAkB,EAClB,kBAA0B;IAE1B,WAAW,CAAC,OAAO,CAAC,CAAA;IACpB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,CAAA;IACpE,MAAM,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACxD,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;IAC3D,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,kBAAkB;QAAE,MAAM,CAAC,oBAAoB,CAAC,CAAA;IAC7E,MAAM,OAAO,CAAC,MAAM,CAClB,8CAA0B,EAC1B,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAC5C,EAAE,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,EAAE,CACvE,CAAA;AACH,CAAC;AAED,yFAAyF;AAClF,KAAK,UAAU,iCAAiC,CACrD,OAAsB,EACtB,QAAgB,EAChB,UAAkB,EAClB,QAAoF;IAEpF,OAAO,eAAe,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE;QACpD,IAAI,EAAE,UAAU;QAChB,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;QAC/C,OAAO,EAAE,QAAQ,CAAC,UAAU;QAC5B,cAAc,EAAE,QAAQ,CAAC,cAAc;KACxC,CAAC,CAAA;AACJ,CAAC;AAEM,KAAK,UAAU,oCAAoC,CACxD,OAAsB,EACtB,QAAgB,EAChB,OAAe,EACf,UAAkB,EAClB,cAAsB;IAEtB,WAAW,CAAC,OAAO,CAAC,CAAA;IACpB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,CAAA;IACpE,MAAM,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACxD,MAAM,MAAM,GAAG,MAAM,IAAA,yCAAkB,EAAC,OAAO,EAAE,QAAQ,EAAE,oBAAoB,CAAC,CAAA;IAChF,IAAI,MAAM,CAAC,gBAAgB;QACzB,MAAM,IAAI,eAAO,CAAC,0BAA0B,EAAE,EAAE,EAAE,wCAAwC,CAAC,CAAA;IAC7F,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,0CAAsB,EAAE;QAC5D,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE;QACxB,iBAAiB,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE;QACrC,WAAW,EAAE,QAAQ;KACtB,CAAC,CAAA;IACF,IAAI,KAAK,EAAE,CAAC;QACV,IAAI,KAAK,CAAC,WAAW,KAAK,OAAO;YAAE,MAAM,CAAC,iBAAiB,CAAC,CAAA;QAC5D,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,6BAA6B,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,CAAC,CAAA;IAC1G,OAAO,eAAe,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAA;AACxH,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,OAAsB,EACtB,QAAgB,EAChB,UAAkB,EAClB,QAA+G;IAE/G,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,CAAA;IACpE,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACzE,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;IAC3D,IACE,CAAC,KAAK,CAAC,QAAQ;QACf,CAAC,QAAQ,CAAC,IAAI,KAAK,UAAU;YAC3B,CAAC,CAAC,KAAK,CAAC,kBAAkB,KAAK,QAAQ,CAAC,kBAAkB;YAC1D,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC;QAC/B,KAAK,CAAC,WAAW,KAAK,QAAQ,CAAC,cAAc;QAC7C,gCAAgC,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAC,cAAc;QAEnE,MAAM,CAAC,oBAAoB,CAAC,CAAA;IAC9B,MAAM,MAAM,GAAG,MAAM,IAAA,yCAAkB,EAAC,OAAO,EAAE,QAAQ,EAAE,oBAAoB,CAAC,CAAA;IAChF,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,eAAe,CAAC,CAAA;IACzD,IAAI,MAAM,CAAC,gBAAgB,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,UAAU,CAAC;QAC5D,MAAM,IAAI,eAAO,CAAC,iCAAiC,EAAE,EAAE,EAAE,yDAAyD,CAAC,CAAA;IACrH,IAAI,QAAQ,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC,OAAO;QAAE,MAAM,CAAC,uBAAuB,CAAC,CAAA;IACzG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;IACnC,MAAM,gBAAgB,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,0CAAsB,EAAE;QACrE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE;QAC1E,GAAG,WAAW,CAAC,OAAO,CAAC;KACxB,CAAC,CAAA;IACF,IAAI,gBAAgB;QAAE,OAAO,gBAAgB,CAAA;IAC7C,IAAI,KAAK,CAAC,cAAc,KAAK,QAAQ,CAAC,cAAc;QAAE,MAAM,CAAC,eAAe,CAAC,CAAA;IAC7E,qGAAqG;IACrG,MAAM,IAAA,sDAA0B,EAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAA;IAC1D,IACE,MAAM,OAAO,CAAC,OAAO,CAAC,2CAAuB,EAAE;QAC7C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,IAAA,yBAAe,EAAC,KAAK,CAAC,aAAa,CAAC,EAAE;QACtH,GAAG,WAAW,CAAC,OAAO,CAAC;KACxB,CAAC;QAEF,MAAM,CAAC,oBAAoB,CAAC,CAAA;IAC9B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,0CAAsB,EAAE;QAC5D,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,cAAc,EAAE;QACvG,GAAG,WAAW,CAAC,OAAO,CAAC;KACxB,CAAC,CAAA;IACF,IAAI,QAAQ,CAAC,cAAc,GAAG,CAAC,IAAI,CAAC,OAAO;QAAE,MAAM,CAAC,eAAe,CAAC,CAAA;IACpE,MAAM,WAAW,GAAG,OAAO;QACzB,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,6CAAyB,EAAE;YAC5C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE;YAC5F,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;YAC3B,GAAG,WAAW,CAAC,OAAO,CAAC;SACxB,CAAC;QACJ,CAAC,CAAC,EAAE,CAAA;IACN,gEAAgE;IAChE,IAAI,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC,aAAa,CAAC;QAAE,MAAM,CAAC,sBAAsB,CAAC,CAAA;IACzG,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAA;IAC7B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAC7B,0CAAsB,EACtB,OAAO,CAAC,MAAM,CAAC,0CAAsB,EAAE;QACrC,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE;QACxB,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE;QAC7B,UAAU,EAAE,QAAQ,CAAC,cAAc,GAAG,CAAC;QACvC,UAAU;QACV,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB,IAAI,IAAI;QACvD,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,QAAQ,EAAE,QAAQ,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI;QACxE,SAAS,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,OAAO,EAAE;QACnC,WAAW,EAAE,QAAQ,CAAC,IAAI;QAC1B,iBAAiB,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE;QACrC,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;KAC1B,CAAC,CACH,CAAA;IACD,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,OAAO,CAAC,IAAI,CAChB,6CAAyB,EACzB,OAAO,CAAC,MAAM,CAAC,6CAAyB,EAAE;YACxC,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE;YACxB,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE;YAC7B,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;YACrB,QAAQ,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,UAAU,EAAE;YAChC,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,OAAO,EAAE,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO;SAC/F,CAAC,CACH,CAAA;IACH,CAAC;IACD,MAAM,OAAO,CAAC,IAAI,CAChB,6CAAyB,EACzB,OAAO,CAAC,MAAM,CAAC,6CAAyB,EAAE;QACxC,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE;QACxB,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE;QAC7B,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;QACrB,QAAQ,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE;QAC5B,SAAS,EAAE,KAAK,CAAC,aAAa;QAC9B,OAAO,EAAE,IAAI;KACd,CAAC,CACH,CAAA;IACD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,CACnC,wCAAkB,EAClB,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,cAAc,EAAE,QAAQ,CAAC,cAAc,EAAE,EACtF,EAAE,cAAc,EAAE,IAAI,CAAC,UAAU,EAAE,CACpC,CAAA;IACD,IAAI,QAAQ,CAAC,QAAQ,KAAK,CAAC;QAAE,MAAM,CAAC,eAAe,CAAC,CAAA;IACpD,OAAO,IAAI,CAAA;AACb,CAAC;AAEM,KAAK,UAAU,yBAAyB,CAC7C,OAAsB,EACtB,QAAgB,EAChB,UAAkB,EAClB,YAA2B,EAC3B,UAAyB,IAAI,IAAI,EAAE;IAEnC,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAChC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,0CAAsB,EAAE;QACzD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,IAAA,yBAAe,EAAC,KAAK,CAAC,EAAE;QACrG,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;QAC7B,GAAG,WAAW,CAAC,OAAO,CAAC;KACxB,CAAC,CAAA;IACF,IAAI,CAAC,IAAI;QAAE,MAAM,CAAC,uBAAuB,CAAC,CAAA;IAC1C,MAAM,KAAK,GAAG,OAAO;SAClB,kBAAkB,CAAC,6CAAyB,EAAE,SAAS,CAAC;SACxD,KAAK,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;SACxF,QAAQ,CAAC,0BAA0B,EAAE,EAAE,EAAE,EAAE,CAAC;SAC5C,QAAQ,CAAC,oDAAoD,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;IACzE,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI;QAAE,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACjE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,CAAA;IACtC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAA;IACtG,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;IAC1E,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,WAAW,KAAK,gCAAgC,CAAC,QAAQ,CAAC;QAC3F,MAAM,CAAC,4BAA4B,CAAC,CAAA;IACtC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAA;AAC3D,CAAC;AAED,8FAA8F;AACvF,KAAK,UAAU,kCAAkC,CAAC,OAAsB,EAAE,QAAgB,EAAE,KAA8B;IAC/H,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAAE,MAAM,CAAC,gBAAgB,CAAC,CAAA;IACzD,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;IACpE,IAAI,QAAQ,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW,IAAI,gCAAgC,CAAC,QAAQ,CAAC,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC;QACrL,MAAM,CAAC,4BAA4B,CAAC,CAAA;IACtC,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,0CAAsB,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IAChJ,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,6CAAyB,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAC9J,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,CAAA;IAC1H,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU;QAAE,MAAM,CAAC,gBAAgB,CAAC,CAAA;IACjJ,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,uEAAuE;AAChE,KAAK,UAAU,8BAA8B,CAClD,OAAsB,EACtB,QAAgB,EAChB,UAAkB,EAClB,SAAiB,EACjB,YAA2B;IAE3B,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAChC,MAAM,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;IAChD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,2CAAuB,EAAE;QAC9D,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE;QAC9C,GAAG,WAAW,CAAC,OAAO,CAAC;KACxB,CAAC,CAAA;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,QAAQ,CAAC,UAAU,KAAK,UAAU,IAAI,QAAQ,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE;YAAE,MAAM,CAAC,gBAAgB,CAAC,CAAA;QACpH,MAAM,kCAAkC,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;QACrE,OAAO,QAAQ,CAAA;IACjB,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAA;IAC7B,MAAM,QAAQ,GAAG,MAAM,yBAAyB,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,CAAC,CAAA;IAC/F,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,CAC9B,2CAAuB,EACvB,OAAO,CAAC,MAAM,CAAC,2CAAuB,EAAE;QACtC,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE;QACxB,QAAQ,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE;QAC5B,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC;QACvC,YAAY,EAAE,EAAE;QAChB,UAAU;QACV,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE;QACtC,WAAW,EAAE,QAAQ,CAAC,QAAQ,CAAC,WAAW;QAC1C,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE;KAC/B,CAAC,CACH,CAAA;IACD,OAAO,OAAO,CAAC,eAAe,CAAC,2CAAuB,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAA;AACrG,CAAC;AAEM,KAAK,UAAU,kCAAkC,CACtD,OAAsB,EACtB,QAAgB,EAChB,UAAkB,EAClB,EAAE,GAAG,IAAI,IAAI,EAAE;IAEf,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;IAC7D,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,8CAA0B,EAAE;QAC/D,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE;QACjE,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE;KACvC,CAAC,CAAA;IACF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,0CAAsB,EAAE;QACzD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,cAAc,EAAE;KACvG,CAAC,CAAA;IACF,MAAM,QAAQ,GAAG,IAAI;QACnB,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,6CAAyB,EAAE;YAC5C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE;YACxF,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;SAC5B,CAAC;QACJ,CAAC,CAAC,EAAE,CAAA;IACN,OAAO;QACL,UAAU;QACV,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,UAAU,EAAE,QAAQ,CAAC,cAAc;QACnC,EAAE,EAAE,EAAE,CAAC,WAAW,EAAE;QACpB,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACjC,GAAG,OAAO;YACV,KAAK,EAAE,OAAO,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;SAC/G,CAAC,CAAC;QACH,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;KAC3F,CAAA;AACH,CAAC;AAED,8EAA8E;AACvE,KAAK,UAAU,2BAA2B,CAC/C,OAAsB,EACtB,QAAgB,EAChB,QAA4B,EAC5B,EAAE,GAAG,IAAI,IAAI,EAAE;IAEf,MAAM,QAAQ,GAAG,MAAM,yBAAyB,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;IACpF,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,wCAAkB,EAAE,EAAE,QAAQ,EAAE;QACvD,cAAc,EAAE,QAAQ,CAAC,QAAQ,CAAC,cAAc;QAChD,WAAW,EAAE,QAAQ,CAAC,QAAQ,CAAC,WAAW;QAC1C,gBAAgB,EAAE,QAAQ,CAAC,QAAQ,CAAC,gBAAgB;QACpD,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,UAAU;QACxC,oBAAoB,EAAE,QAAQ,CAAC,QAAQ,CAAC,oBAAoB;QAC5D,cAAc,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS;QAC1C,YAAY,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,IAAI,SAAS;KACpD,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { createHash } from 'node:crypto'\nimport { EntityManager, LessThanOrEqual, MoreThanOrEqual } from 'typeorm'\nimport { Refusal } from '@things-factory/shell'\nimport { masterChangePolicy } from '../master-change-policy'\nimport { materialDraftContent, validateMaterialReferences } from './material-revision-content'\nimport { MaterialDefinition } from './material-definition'\nimport {\n MaterialDefinitionBasis,\n MaterialDefinitionPlan,\n MaterialDefinitionRevision,\n MaterialDefinitionSegment\n} from './material-revision'\n\nexport class MaterialDefinitionRefusal extends Refusal {\n constructor(code: string, detail?: string) {\n super(\n code,\n {},\n detail ?? MATERIAL_DEFINITION_REFUSALS[code] ?? 'The material definition change could not be applied.'\n )\n }\n}\nconst MATERIAL_DEFINITION_REFUSALS: Record<string, string> = {\n TRANSACTION_REQUIRED: 'This change requires a transaction.',\n INVALID_INSTANT: 'Enter a valid date and time including its time zone.',\n INVALID_CONTENT: 'The proposed material content is invalid.',\n MATERIAL_NOT_FOUND: 'The material does not exist in this domain.',\n REVISION_NOT_FOUND: 'The revision does not exist in this domain.',\n INVALID_BASE_REVISION: 'The proposed change is based on a different revision.',\n DRAFT_NOT_OWNED: 'Only the draft author can edit or submit it.',\n REVISION_FROZEN: 'This submitted revision cannot be edited. Create another draft.',\n DRAFT_CONFLICT: 'The draft changed. Reload it before saving.',\n PLAN_CONFLICT: 'The approved schedule changed. Review and submit a new draft.',\n INVALID_SUBMISSION: 'The submitted content does not match the frozen revision.',\n AUTHOR_CANNOT_APPROVE: 'The author cannot approve their own change.',\n RETROACTIVE_IMPACT: 'This change would alter material definitions already used by recorded transactions.',\n FUTURE_PLAN_CONFLICT: 'This change conflicts with an approved future schedule.',\n NO_EFFECTIVE_REVISION: 'There is no published revision effective at the business time.',\n AMBIGUOUS_EFFECTIVITY: 'More than one revision is effective at the business time.',\n REVISION_INTEGRITY_FAILURE: 'The stored revision does not match its signed content.',\n BASIS_CONFLICT: 'This transaction already has a different material or business time.',\n APPROVAL_LINE_REQUIRED: 'Configure an approval line before submitting this change.',\n REVISION_REQUIRED: 'Change the material through a revision draft.'\n}\nfunction refuse(code: string, detail?: string): never {\n throw new MaterialDefinitionRefusal(code, detail)\n}\nfunction transaction(manager: EntityManager) {\n if (!manager.queryRunner?.isTransactionActive) refuse('TRANSACTION_REQUIRED')\n}\n/** Current reads are required after waiting for the subject lock, including MySQL REPEATABLE READ. */\nfunction currentLock(manager: EntityManager) {\n const driver = manager.connection.options.type\n return manager.queryRunner?.isTransactionActive && !['sqlite', 'better-sqlite3', 'sqljs'].includes(driver)\n ? { lock: { mode: 'pessimistic_write' as const } }\n : {}\n}\nfunction instant(value: Date | string): Date {\n if (!(value instanceof Date) && (typeof value !== 'string' || !/(Z|[+-]\\d{2}:\\d{2})$/.test(value)))\n refuse('INVALID_INSTANT')\n const result = new Date(value)\n if (!Number.isFinite(result.getTime())) refuse('INVALID_INSTANT')\n return result\n}\nfunction text(value: unknown, field: string): string {\n if (typeof value !== 'string' || !value.trim()) refuse('INVALID_CONTENT', field)\n return value.trim()\n}\n/** Sorted object keys, ordered arrays, ISO instants. This is the v1 submission encoding. */\nfunction canonical(value: any): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value)\n if (Array.isArray(value)) return '[' + value.map(canonical).join(',') + ']'\n return (\n '{' +\n Object.keys(value)\n .sort()\n .map(key => JSON.stringify(key) + ':' + canonical(value[key]))\n .join(',') +\n '}'\n )\n}\nexport function materialDefinitionSubmissionHash(revision: MaterialDefinitionRevision): string {\n return createHash('sha256')\n .update(\n canonical({\n schemaVersion: 1,\n revisionNumber: revision.revisionNumber,\n policyId: revision.policyId,\n domainId: revision.domainId,\n materialId: revision.materialId,\n baseRevisionId: revision.baseRevisionId,\n baseGeneration: revision.baseGeneration,\n description: revision.description,\n materialClassIds: revision.materialClassIds,\n properties: revision.properties,\n testSpecificationIds: revision.testSpecificationIds,\n effectiveFrom: revision.effectiveFrom.toISOString(),\n reason: revision.reason\n })\n )\n .digest('hex')\n}\n\n/** Display values are a checked projection of the frozen revision, never a second editable source. */\nexport async function materialDefinitionApprovalInput(manager: EntityManager, domainId: string, revisionId: string) {\n const draft = await revision(manager, domainId, revisionId)\n if (!draft.frozenAt || draft.contentHash !== materialDefinitionSubmissionHash(draft)) refuse('INVALID_SUBMISSION')\n const material = await subject(manager, domainId, draft.materialId)\n const base = draft.baseRevisionId ? await revision(manager, domainId, draft.baseRevisionId) : null\n const plan = base ? await manager.findOneBy(MaterialDefinitionPlan, {\n domain: { id: domainId }, material: { id: draft.materialId }, generation: draft.baseGeneration\n }) : null\n const segment = plan && base ? await manager.findOneBy(MaterialDefinitionSegment, {\n domain: { id: domainId }, plan: { id: plan.id }, revision: { id: base.id }\n }) : null\n const values = (row: MaterialDefinitionRevision) => ({\n revisionNumber: row.revisionNumber,\n detail: {\n revisionId: row.id, createdAt: row.createdAt.toISOString(), frozenAt: row.frozenAt?.toISOString(),\n effectiveFrom: row.effectiveFrom.toISOString(), reason: row.reason,\n ...(row.id === base?.id && segment ? { validFrom: segment.validFrom.toISOString(), validTo: segment.validTo?.toISOString() ?? null } : {})\n },\n description: row.description,\n materialClassIds: row.materialClassIds,\n properties: row.properties,\n testSpecificationIds: row.testSpecificationIds\n })\n return {\n headline: material.name,\n reason: draft.reason,\n effectiveFrom: draft.effectiveFrom.toISOString(),\n before: base ? values(base) : null,\n after: values(draft),\n revision: {\n revisionId: draft.id,\n hash: draft.contentHash,\n materialId: draft.materialId,\n baseGeneration: draft.baseGeneration\n }\n }\n}\n\nexport async function assertMaterialDefinitionApprovalInput(\n manager: EntityManager,\n domainId: string,\n input: Record<string, unknown>\n) {\n const reference = input?.revision as { revisionId?: string }\n if (typeof reference?.revisionId !== 'string') refuse('INVALID_SUBMISSION')\n const expected = await materialDefinitionApprovalInput(manager, domainId, reference.revisionId)\n for (const key of Object.keys(expected)) {\n if (canonical(input[key]) !== canonical(expected[key])) refuse('INVALID_SUBMISSION')\n }\n}\n\nexport interface MaterialDefinitionDraftContent {\n description?: string\n materialClassIds?: string[]\n properties?: Record<string, unknown>[]\n testSpecificationIds?: string[]\n effectiveFrom: Date | string\n reason: string\n}\nfunction content(input: MaterialDefinitionDraftContent) {\n return { ...materialDraftContent(input), effectiveFrom: instant(input.effectiveFrom), reason: text(input.reason, 'reason') }\n}\nasync function subject(manager: EntityManager, domainId: string, materialId: string) {\n const material = await manager.findOne(MaterialDefinition, {\n where: { id: materialId, domain: { id: domainId } },\n ...currentLock(manager)\n })\n if (!material) refuse('MATERIAL_NOT_FOUND')\n return material\n}\nasync function revision(manager: EntityManager, domainId: string, revisionId: string, locked = true) {\n const found = await manager.findOne(MaterialDefinitionRevision, {\n where: { id: revisionId, domain: { id: domainId } },\n ...(locked ? currentLock(manager) : {})\n })\n if (!found) refuse('REVISION_NOT_FOUND')\n return found\n}\n/** A write lock valid on every supported SQL backend, including SQLite (no SELECT FOR UPDATE). */\nasync function lockSubject(manager: EntityManager, domainId: string, materialId: string) {\n transaction(manager)\n const locked = await manager\n .createQueryBuilder()\n .update(MaterialDefinition)\n .set({ revisionLock: () => 'revision_lock + 1' })\n .where({ id: materialId, domain: { id: domainId } })\n .execute()\n if (locked.affected !== 1) refuse('MATERIAL_NOT_FOUND')\n return subject(manager, domainId, materialId)\n}\n\nexport async function createMaterialDefinitionDraft(\n manager: EntityManager,\n domainId: string,\n creatorId: string,\n asked: { materialId?: string; name?: string; baseRevisionId?: string; content: MaterialDefinitionDraftContent }\n) {\n transaction(manager)\n const accepted = content(asked.content)\n await validateMaterialReferences(manager, domainId, accepted)\n const policy = await masterChangePolicy(manager, domainId, 'materialDefinition')\n let material: MaterialDefinition\n if (asked.materialId) {\n material = await lockSubject(manager, domainId, asked.materialId)\n } else {\n material = await manager.save(\n MaterialDefinition,\n manager.create(MaterialDefinition, {\n domain: { id: domainId },\n name: text(asked.name, 'name'),\n planGeneration: 0,\n revisionLock: 0,\n creator: { id: creatorId },\n updater: { id: creatorId }\n })\n )\n }\n const previous = await manager.findOne(MaterialDefinitionRevision, {\n where: { domain: { id: domainId }, material: { id: material.id } }, order: { revisionNumber: 'DESC' },\n ...currentLock(manager)\n })\n const revisionNumber = (previous?.revisionNumber ?? 0) + 1\n let baseRevisionId: string | null = null\n if (material.planGeneration > 0) {\n baseRevisionId = (await resolveMaterialDefinition(manager, domainId, material.id, accepted.effectiveFrom)).revision.id\n }\n if (asked.baseRevisionId && asked.baseRevisionId !== baseRevisionId) refuse('INVALID_BASE_REVISION')\n const saved = await manager.save(\n MaterialDefinitionRevision,\n manager.create(MaterialDefinitionRevision, {\n ...accepted,\n policy: { id: policy.id },\n domain: { id: domainId },\n material: { id: material.id },\n baseRevision: baseRevisionId ? { id: baseRevisionId } : null,\n baseGeneration: material.planGeneration,\n revisionNumber,\n editGeneration: 0,\n creator: { id: creatorId },\n createdAt: new Date(),\n frozenAt: null,\n contentHash: null,\n activityInstanceId: null\n })\n )\n return revision(manager, domainId, saved.id)\n}\n\nexport async function editMaterialDefinitionDraft(\n manager: EntityManager,\n domainId: string,\n actorId: string,\n revisionId: string,\n expectedEditGeneration: number,\n input: MaterialDefinitionDraftContent\n) {\n transaction(manager)\n const accepted = content(input)\n await validateMaterialReferences(manager, domainId, accepted)\n const initial = await revision(manager, domainId, revisionId, false)\n const material = await lockSubject(manager, domainId, initial.materialId)\n const draft = await revision(manager, domainId, revisionId)\n if (draft.creatorId !== actorId) refuse('DRAFT_NOT_OWNED')\n if (draft.frozenAt) refuse('REVISION_FROZEN')\n if (draft.baseGeneration !== material.planGeneration) refuse('PLAN_CONFLICT')\n // Moving the effective date across a scheduled boundary changes the before-image.\n const baseRevisionId =\n material.planGeneration > 0\n ? (await resolveMaterialDefinition(manager, domainId, material.id, accepted.effectiveFrom)).revision.id\n : null\n const updated = await manager\n .createQueryBuilder()\n .update(MaterialDefinitionRevision)\n .set({\n ...accepted,\n baseRevision: baseRevisionId ? { id: baseRevisionId } : null,\n editGeneration: expectedEditGeneration + 1\n })\n .where({\n id: revisionId,\n domain: { id: domainId },\n creator: { id: actorId },\n editGeneration: expectedEditGeneration\n })\n .andWhere('frozen_at IS NULL')\n .execute()\n if (updated.affected !== 1) refuse('DRAFT_CONFLICT')\n return revision(manager, domainId, revisionId)\n}\n\n/** Product calls this in the same transaction as issuing its worklist approval. */\nexport async function freezeMaterialDefinitionDraft(\n manager: EntityManager,\n domainId: string,\n actorId: string,\n revisionId: string,\n expectedEditGeneration: number\n) {\n const initial = await revision(manager, domainId, revisionId, false)\n const material = await lockSubject(manager, domainId, initial.materialId)\n const draft = await revision(manager, domainId, revisionId)\n if (draft.creatorId !== actorId) refuse('DRAFT_NOT_OWNED')\n if (draft.frozenAt) refuse('REVISION_FROZEN')\n if (draft.editGeneration !== expectedEditGeneration) refuse('DRAFT_CONFLICT')\n if (draft.baseGeneration !== material.planGeneration) refuse('PLAN_CONFLICT')\n const policy = await masterChangePolicy(manager, domainId, 'materialDefinition')\n if (draft.policyId !== policy.id) refuse('PLAN_CONFLICT')\n materialDraftContent(draft, true)\n await validateMaterialReferences(manager, domainId, draft)\n const contentHash = materialDefinitionSubmissionHash(draft)\n await manager.update(\n MaterialDefinitionRevision,\n { id: revisionId, domain: { id: domainId } },\n { frozenAt: new Date(), contentHash }\n )\n return revision(manager, domainId, revisionId)\n}\n\nexport async function bindMaterialDefinitionApproval(\n manager: EntityManager,\n domainId: string,\n revisionId: string,\n activityInstanceId: string\n) {\n transaction(manager)\n const initial = await revision(manager, domainId, revisionId, false)\n await lockSubject(manager, domainId, initial.materialId)\n const draft = await revision(manager, domainId, revisionId)\n if (!draft.frozenAt || draft.activityInstanceId) refuse('INVALID_SUBMISSION')\n await manager.update(\n MaterialDefinitionRevision,\n { id: revisionId, domain: { id: domainId } },\n { activityInstanceId: text(activityInstanceId, 'activityInstanceId') }\n )\n}\n\n/** Internal completion callback only. Never expose this as an approved=true mutation. */\nexport async function publishMaterialDefinitionRevision(\n manager: EntityManager,\n domainId: string,\n revisionId: string,\n decision: { activityInstanceId: string; approverId: string; submissionHash: string }\n) {\n return publishRevision(manager, domainId, revisionId, {\n kind: 'approved',\n activityInstanceId: decision.activityInstanceId,\n actorId: decision.approverId,\n submissionHash: decision.submissionHash\n })\n}\n\nexport async function publishUnregulatedMaterialDefinition(\n manager: EntityManager,\n domainId: string,\n actorId: string,\n revisionId: string,\n editGeneration: number\n) {\n transaction(manager)\n const initial = await revision(manager, domainId, revisionId, false)\n await lockSubject(manager, domainId, initial.materialId)\n const policy = await masterChangePolicy(manager, domainId, 'materialDefinition')\n if (policy.approvalRequired)\n throw new Refusal('MASTER_APPROVAL_REQUIRED', {}, 'This change requires product approval.')\n const prior = await manager.findOneBy(MaterialDefinitionPlan, {\n domain: { id: domainId },\n publishedRevision: { id: revisionId },\n releaseKind: 'policy'\n })\n if (prior) {\n if (prior.publisherId !== actorId) refuse('DRAFT_NOT_OWNED')\n return prior\n }\n const frozen = await freezeMaterialDefinitionDraft(manager, domainId, actorId, revisionId, editGeneration)\n return publishRevision(manager, domainId, revisionId, { kind: 'policy', actorId, submissionHash: frozen.contentHash })\n}\n\nasync function publishRevision(\n manager: EntityManager,\n domainId: string,\n revisionId: string,\n decision: { kind: 'approved' | 'policy'; activityInstanceId?: string; actorId: string; submissionHash: string }\n) {\n const initial = await revision(manager, domainId, revisionId, false)\n const material = await lockSubject(manager, domainId, initial.materialId)\n const draft = await revision(manager, domainId, revisionId)\n if (\n !draft.frozenAt ||\n (decision.kind === 'approved'\n ? draft.activityInstanceId !== decision.activityInstanceId\n : !!draft.activityInstanceId) ||\n draft.contentHash !== decision.submissionHash ||\n materialDefinitionSubmissionHash(draft) !== decision.submissionHash\n )\n refuse('INVALID_SUBMISSION')\n const policy = await masterChangePolicy(manager, domainId, 'materialDefinition')\n if (draft.policyId !== policy.id) refuse('PLAN_CONFLICT')\n if (policy.approvalRequired !== (decision.kind === 'approved'))\n throw new Refusal('MASTER_APPROVAL_POLICY_MISMATCH', {}, 'Use the publication path required by the domain policy.')\n if (decision.kind === 'approved' && draft.creatorId === decision.actorId) refuse('AUTHOR_CANNOT_APPROVE')\n text(decision.actorId, 'publisher')\n const previousDecision = await manager.findOne(MaterialDefinitionPlan, {\n where: { domain: { id: domainId }, publishedRevision: { id: revisionId } },\n ...currentLock(manager)\n })\n if (previousDecision) return previousDecision\n if (draft.baseGeneration !== material.planGeneration) refuse('PLAN_CONFLICT')\n // Only replacement from effectiveFrom onward is supported in this slice, not arbitrary rescheduling.\n await validateMaterialReferences(manager, domainId, draft)\n if (\n await manager.findOne(MaterialDefinitionBasis, {\n where: { domain: { id: domainId }, material: { id: material.id }, businessTime: MoreThanOrEqual(draft.effectiveFrom) },\n ...currentLock(manager)\n })\n )\n refuse('RETROACTIVE_IMPACT')\n const oldPlan = await manager.findOne(MaterialDefinitionPlan, {\n where: { domain: { id: domainId }, material: { id: material.id }, generation: material.planGeneration },\n ...currentLock(manager)\n })\n if (material.planGeneration > 0 && !oldPlan) refuse('PLAN_CONFLICT')\n const oldSegments = oldPlan\n ? await manager.find(MaterialDefinitionSegment, {\n where: { domain: { id: domainId }, material: { id: material.id }, plan: { id: oldPlan.id } },\n order: { validFrom: 'ASC' },\n ...currentLock(manager)\n })\n : []\n // Do not silently erase an already approved future appointment.\n if (oldSegments.some(segment => segment.validFrom >= draft.effectiveFrom)) refuse('FUTURE_PLAN_CONFLICT')\n const recordedAt = new Date()\n const plan = await manager.save(\n MaterialDefinitionPlan,\n manager.create(MaterialDefinitionPlan, {\n domain: { id: domainId },\n material: { id: material.id },\n generation: material.planGeneration + 1,\n recordedAt,\n activityInstanceId: decision.activityInstanceId ?? null,\n submissionHash: decision.submissionHash,\n approver: decision.kind === 'approved' ? { id: decision.actorId } : null,\n publisher: { id: decision.actorId },\n releaseKind: decision.kind,\n publishedRevision: { id: revisionId },\n policy: { id: policy.id }\n })\n )\n for (const old of oldSegments) {\n await manager.save(\n MaterialDefinitionSegment,\n manager.create(MaterialDefinitionSegment, {\n domain: { id: domainId },\n material: { id: material.id },\n plan: { id: plan.id },\n revision: { id: old.revisionId },\n validFrom: old.validFrom,\n validTo: !old.validTo || old.validTo > draft.effectiveFrom ? draft.effectiveFrom : old.validTo\n })\n )\n }\n await manager.save(\n MaterialDefinitionSegment,\n manager.create(MaterialDefinitionSegment, {\n domain: { id: domainId },\n material: { id: material.id },\n plan: { id: plan.id },\n revision: { id: revisionId },\n validFrom: draft.effectiveFrom,\n validTo: null\n })\n )\n const advanced = await manager.update(\n MaterialDefinition,\n { id: material.id, domain: { id: domainId }, planGeneration: material.planGeneration },\n { planGeneration: plan.generation }\n )\n if (advanced.affected !== 1) refuse('PLAN_CONFLICT')\n return plan\n}\n\nexport async function resolveMaterialDefinition(\n manager: EntityManager,\n domainId: string,\n materialId: string,\n businessTime: Date | string,\n knownAt: Date | string = new Date()\n) {\n const at = instant(businessTime)\n const known = instant(knownAt)\n const plan = await manager.findOne(MaterialDefinitionPlan, {\n where: { domain: { id: domainId }, material: { id: materialId }, recordedAt: LessThanOrEqual(known) },\n order: { generation: 'DESC' },\n ...currentLock(manager)\n })\n if (!plan) refuse('NO_EFFECTIVE_REVISION')\n const query = manager\n .createQueryBuilder(MaterialDefinitionSegment, 'segment')\n .where({ domain: { id: domainId }, material: { id: materialId }, plan: { id: plan.id } })\n .andWhere('segment.validFrom <= :at', { at })\n .andWhere('(segment.validTo IS NULL OR segment.validTo > :at)', { at })\n if (currentLock(manager).lock) query.setLock('pessimistic_write')\n const segments = await query.getMany()\n if (segments.length !== 1) refuse(segments.length ? 'AMBIGUOUS_EFFECTIVITY' : 'NO_EFFECTIVE_REVISION')\n const selected = await revision(manager, domainId, segments[0].revisionId)\n if (!selected.frozenAt || selected.contentHash !== materialDefinitionSubmissionHash(selected))\n refuse('REVISION_INTEGRITY_FAILURE')\n return { revision: selected, plan, segment: segments[0] }\n}\n\n/** Replay reads verify the captured digest too, not only the currently effective schedule. */\nexport async function materialDefinitionRevisionForBasis(manager: EntityManager, domainId: string, basis: MaterialDefinitionBasis) {\n if (basis.domainId !== domainId) refuse('BASIS_CONFLICT')\n const selected = await revision(manager, domainId, basis.revisionId)\n if (selected.materialId !== basis.materialId || !selected.frozenAt || selected.contentHash !== basis.contentHash || materialDefinitionSubmissionHash(selected) !== basis.contentHash) {\n refuse('REVISION_INTEGRITY_FAILURE')\n }\n const plan = await manager.findOneBy(MaterialDefinitionPlan, { id: basis.planId, domain: { id: domainId }, material: { id: basis.materialId } })\n const segments = await manager.findBy(MaterialDefinitionSegment, { domain: { id: domainId }, material: { id: basis.materialId }, plan: { id: basis.planId } })\n const matching = segments.filter(s => s.validFrom <= basis.businessTime && (!s.validTo || s.validTo > basis.businessTime))\n if (!plan || plan.recordedAt > basis.resolvedAt || matching.length !== 1 || matching[0].revisionId !== basis.revisionId) refuse('BASIS_CONFLICT')\n return selected\n}\n\n/** Must run inside the transaction that writes the business record. */\nexport async function captureMaterialDefinitionBasis(\n manager: EntityManager,\n domainId: string,\n materialId: string,\n recordKey: string,\n businessTime: Date | string\n) {\n const at = instant(businessTime)\n await lockSubject(manager, domainId, materialId)\n const existing = await manager.findOne(MaterialDefinitionBasis, {\n where: { domain: { id: domainId }, recordKey },\n ...currentLock(manager)\n })\n if (existing) {\n if (existing.materialId !== materialId || existing.businessTime.getTime() !== at.getTime()) refuse('BASIS_CONFLICT')\n await materialDefinitionRevisionForBasis(manager, domainId, existing)\n return existing\n }\n const resolvedAt = new Date()\n const selected = await resolveMaterialDefinition(manager, domainId, materialId, at, resolvedAt)\n const saved = await manager.save(\n MaterialDefinitionBasis,\n manager.create(MaterialDefinitionBasis, {\n domain: { id: domainId },\n material: { id: materialId },\n recordKey: text(recordKey, 'recordKey'),\n businessTime: at,\n resolvedAt,\n revision: { id: selected.revision.id },\n contentHash: selected.revision.contentHash,\n plan: { id: selected.plan.id }\n })\n )\n return manager.findOneByOrFail(MaterialDefinitionBasis, { id: saved.id, domain: { id: domainId } })\n}\n\nexport async function materialDefinitionRevisionOverview(\n manager: EntityManager,\n domainId: string,\n materialId: string,\n at = new Date()\n) {\n const material = await subject(manager, domainId, materialId)\n const revisions = await manager.find(MaterialDefinitionRevision, {\n where: { domain: { id: domainId }, material: { id: materialId } },\n order: { createdAt: 'ASC', id: 'ASC' }\n })\n const plan = await manager.findOne(MaterialDefinitionPlan, {\n where: { domain: { id: domainId }, material: { id: materialId }, generation: material.planGeneration }\n })\n const schedule = plan\n ? await manager.find(MaterialDefinitionSegment, {\n where: { domain: { id: domainId }, material: { id: materialId }, plan: { id: plan.id } },\n order: { validFrom: 'ASC' }\n })\n : []\n return {\n materialId,\n name: material.name,\n generation: material.planGeneration,\n at: at.toISOString(),\n schedule: schedule.map(segment => ({\n ...segment,\n state: segment.validFrom > at ? 'scheduled' : segment.validTo && segment.validTo <= at ? 'expired' : 'current'\n })),\n revisions: revisions.map(item => ({ ...item, state: item.frozenAt ? 'frozen' : 'draft' }))\n }\n}\n\n/** Read-time projection, never a clock-driven rewrite of the identity row. */\nexport async function effectiveMaterialDefinition(\n manager: EntityManager,\n domainId: string,\n material: MaterialDefinition,\n at = new Date()\n) {\n const selected = await resolveMaterialDefinition(manager, domainId, material.id, at)\n return Object.assign(new MaterialDefinition(), material, {\n revisionNumber: selected.revision.revisionNumber,\n description: selected.revision.description,\n materialClassIds: selected.revision.materialClassIds,\n properties: selected.revision.properties,\n testSpecificationIds: selected.revision.testSpecificationIds,\n effectiveStart: selected.segment.validFrom,\n effectiveEnd: selected.segment.validTo ?? undefined\n })\n}\n"]}
|