project-tiny-context-harness 0.8.12 → 0.8.13
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 +43 -19
- package/assets/README.md +81 -40
- package/assets/README.zh-CN.md +44 -22
- package/assets/agents/AGENTS_CORE.md +8 -4
- package/assets/skills/context_development_engineer/SKILL.md +9 -1
- package/assets/skills/design-resource-authoring/SKILL.md +7 -5
- package/assets/skills/design-resource-authoring/references/downstream-handoff.md +2 -2
- package/assets/skills/design-resource-authoring/references/open-design-provider.md +22 -2
- package/assets/skills/design-resource-authoring/references/recovery-and-writeback.md +199 -0
- package/assets/skills/design-resource-authoring/references/resource-selection.md +2 -2
- package/assets/skills/long-task-workflow/SKILL.md +6 -6
- package/assets/skills/long-task-workflow/references/contract-authoring.md +27 -22
- package/assets/skills/long-task-workflow/references/evidence-design.md +49 -32
- package/assets/skills/long-task-workflow/references/source-authoring.md +2 -2
- package/dist/commands/design-resource-recovery.d.ts +1 -0
- package/dist/commands/design-resource-recovery.js +152 -0
- package/dist/commands/design-resource.js +20 -1
- package/dist/commands/index.js +1 -1
- package/dist/lib/design-resource-reconciliation-codec.d.ts +2 -0
- package/dist/lib/design-resource-reconciliation-codec.js +174 -0
- package/dist/lib/design-resource-reconciliation-types.d.ts +62 -0
- package/dist/lib/design-resource-reconciliation-types.js +1 -0
- package/dist/lib/design-resource-reconciliation.d.ts +3 -0
- package/dist/lib/design-resource-reconciliation.js +224 -0
- package/dist/lib/design-resource-recovery-authority-policy.d.ts +8 -0
- package/dist/lib/design-resource-recovery-authority-policy.js +123 -0
- package/dist/lib/design-resource-recovery-catalog-resources.d.ts +6 -0
- package/dist/lib/design-resource-recovery-catalog-resources.js +121 -0
- package/dist/lib/design-resource-recovery-catalog-shape.d.ts +3 -0
- package/dist/lib/design-resource-recovery-catalog-shape.js +104 -0
- package/dist/lib/design-resource-recovery-catalog.d.ts +9 -0
- package/dist/lib/design-resource-recovery-catalog.js +103 -0
- package/dist/lib/design-resource-recovery-cleanup.d.ts +2 -0
- package/dist/lib/design-resource-recovery-cleanup.js +11 -0
- package/dist/lib/design-resource-recovery-codec-primitives.d.ts +19 -0
- package/dist/lib/design-resource-recovery-codec-primitives.js +92 -0
- package/dist/lib/design-resource-recovery-codec.d.ts +4 -0
- package/dist/lib/design-resource-recovery-codec.js +93 -0
- package/dist/lib/design-resource-recovery-current.d.ts +9 -0
- package/dist/lib/design-resource-recovery-current.js +41 -0
- package/dist/lib/design-resource-recovery-delta-policy.d.ts +5 -0
- package/dist/lib/design-resource-recovery-delta-policy.js +108 -0
- package/dist/lib/design-resource-recovery-files.d.ts +37 -0
- package/dist/lib/design-resource-recovery-files.js +204 -0
- package/dist/lib/design-resource-recovery-final-disposition-shape.d.ts +2 -0
- package/dist/lib/design-resource-recovery-final-disposition-shape.js +47 -0
- package/dist/lib/design-resource-recovery-patch-types.d.ts +54 -0
- package/dist/lib/design-resource-recovery-patch-types.js +1 -0
- package/dist/lib/design-resource-recovery-replay.d.ts +7 -0
- package/dist/lib/design-resource-recovery-replay.js +112 -0
- package/dist/lib/design-resource-recovery-repository-bindings.d.ts +9 -0
- package/dist/lib/design-resource-recovery-repository-bindings.js +49 -0
- package/dist/lib/design-resource-recovery-schema.d.ts +5 -0
- package/dist/lib/design-resource-recovery-schema.js +5 -0
- package/dist/lib/design-resource-recovery-shape.d.ts +10 -0
- package/dist/lib/design-resource-recovery-shape.js +184 -0
- package/dist/lib/design-resource-recovery-source-authority.d.ts +5 -0
- package/dist/lib/design-resource-recovery-source-authority.js +219 -0
- package/dist/lib/design-resource-recovery-text.d.ts +17 -0
- package/dist/lib/design-resource-recovery-text.js +186 -0
- package/dist/lib/design-resource-recovery-types.d.ts +197 -0
- package/dist/lib/design-resource-recovery-types.js +1 -0
- package/dist/lib/design-resource-recovery-writeback-policy.d.ts +5 -0
- package/dist/lib/design-resource-recovery-writeback-policy.js +326 -0
- package/dist/lib/design-resource-recovery-writeback-shape.d.ts +6 -0
- package/dist/lib/design-resource-recovery-writeback-shape.js +155 -0
- package/dist/lib/design-resource-recovery.d.ts +44 -0
- package/dist/lib/design-resource-recovery.js +216 -0
- package/dist/lib/long-task-acceptance-shape.js +8 -2
- package/dist/lib/long-task-activation-validation.js +13 -3
- package/dist/lib/long-task-admitted-observation-records.d.ts +25 -0
- package/dist/lib/long-task-admitted-observation-records.js +40 -0
- package/dist/lib/long-task-admitted-observation.d.ts +93 -0
- package/dist/lib/long-task-admitted-observation.js +201 -0
- package/dist/lib/long-task-artifacts.d.ts +1 -0
- package/dist/lib/long-task-artifacts.js +1 -0
- package/dist/lib/long-task-authority-material-diff.js +1 -0
- package/dist/lib/long-task-authority-policy.d.ts +2 -0
- package/dist/lib/long-task-authority-policy.js +2 -0
- package/dist/lib/long-task-authority-revision-details.js +3 -1
- package/dist/lib/long-task-authority.js +2 -1
- package/dist/lib/long-task-check-execution-policy.js +5 -0
- package/dist/lib/long-task-check-runner.d.ts +2 -2
- package/dist/lib/long-task-check-runner.js +440 -23
- package/dist/lib/long-task-claims.d.ts +18 -0
- package/dist/lib/long-task-claims.js +35 -0
- package/dist/lib/long-task-compact-structure-targets.js +1 -0
- package/dist/lib/long-task-conformance-policy.js +3 -1
- package/dist/lib/long-task-counterfactual-claim-policy.d.ts +3 -4
- package/dist/lib/long-task-counterfactual-claim-policy.js +11 -3
- package/dist/lib/long-task-counterfactual-sandbox.js +1 -0
- package/dist/lib/long-task-counterfactual-types.d.ts +2 -0
- package/dist/lib/long-task-delivery-compiler.js +2 -0
- package/dist/lib/long-task-evidence-capability-policy.d.ts +3 -2
- package/dist/lib/long-task-evidence-capability-policy.js +114 -8
- package/dist/lib/long-task-evidence-capability-runtime.d.ts +5 -2
- package/dist/lib/long-task-evidence-capability-runtime.js +149 -5
- package/dist/lib/long-task-evidence-sensitivity-policy.d.ts +9 -0
- package/dist/lib/long-task-evidence-sensitivity-policy.js +31 -1
- package/dist/lib/long-task-evidence-v2.d.ts +4 -4
- package/dist/lib/long-task-evidence-v2.js +321 -43
- package/dist/lib/long-task-exact-comparison.d.ts +16 -0
- package/dist/lib/long-task-exact-comparison.js +28 -0
- package/dist/lib/long-task-execution-observation.d.ts +24 -0
- package/dist/lib/long-task-execution-observation.js +332 -0
- package/dist/lib/long-task-explain-acceptance-link.d.ts +4 -0
- package/dist/lib/long-task-explain-acceptance-link.js +4 -0
- package/dist/lib/long-task-explain-claim-links.d.ts +2 -0
- package/dist/lib/long-task-explain-claim-links.js +2 -0
- package/dist/lib/long-task-explain-source-links.d.ts +6 -0
- package/dist/lib/long-task-final-v2.js +7 -4
- package/dist/lib/long-task-json-pointer-observation.d.ts +59 -0
- package/dist/lib/long-task-json-pointer-observation.js +184 -0
- package/dist/lib/long-task-observation-artifact.d.ts +8 -0
- package/dist/lib/long-task-observation-artifact.js +58 -0
- package/dist/lib/long-task-observation-authority.d.ts +17 -0
- package/dist/lib/long-task-observation-authority.js +348 -0
- package/dist/lib/long-task-paths.js +1 -1
- package/dist/lib/long-task-process-observation.d.ts +6 -0
- package/dist/lib/long-task-process-observation.js +117 -0
- package/dist/lib/long-task-process-runtime-closure.d.ts +13 -0
- package/dist/lib/long-task-process-runtime-closure.js +175 -0
- package/dist/lib/long-task-progress.js +2 -0
- package/dist/lib/long-task-protected-files.d.ts +1 -1
- package/dist/lib/long-task-protected-files.js +1 -30
- package/dist/lib/long-task-risk.js +8 -3
- package/dist/lib/long-task-root-shape.js +10 -1
- package/dist/lib/long-task-runner-environment.d.ts +1 -1
- package/dist/lib/long-task-runner-environment.js +4 -4
- package/dist/lib/long-task-runner-freeze.d.ts +4 -2
- package/dist/lib/long-task-runner-freeze.js +128 -28
- package/dist/lib/long-task-runtime-types.d.ts +111 -0
- package/dist/lib/long-task-scoped-binding.d.ts +13 -0
- package/dist/lib/long-task-scoped-binding.js +11 -0
- package/dist/lib/long-task-semantic-contract-types.d.ts +1 -0
- package/dist/lib/long-task-semantic-fact-contract-closure.js +3 -3
- package/dist/lib/long-task-semantic-fact-evidence.d.ts +3 -3
- package/dist/lib/long-task-semantic-fact-evidence.js +75 -58
- package/dist/lib/long-task-source-claim-validation.js +4 -1
- package/dist/lib/long-task-source-target-continuity.d.ts +2 -2
- package/dist/lib/long-task-source-target-continuity.js +35 -2
- package/dist/lib/long-task-source-target-index.d.ts +3 -0
- package/dist/lib/long-task-source-target-index.js +19 -0
- package/dist/lib/long-task-stage-policy.js +10 -4
- package/dist/lib/long-task-static-observation-freeze.d.ts +83 -0
- package/dist/lib/long-task-static-observation-freeze.js +428 -0
- package/dist/lib/long-task-status-projection.js +5 -1
- package/dist/lib/long-task-target-policy.d.ts +1 -1
- package/dist/lib/long-task-target-policy.js +3 -0
- package/dist/lib/long-task-verifier-dependency-closure.js +7 -3
- package/dist/lib/long-task-verifier-v2.js +70 -9
- package/dist/lib/long-task-workspace.d.ts +1 -1
- package/dist/lib/long-task-workspace.js +1 -12
- package/dist/lib/repository-path-safety.d.ts +14 -0
- package/dist/lib/repository-path-safety.js +126 -0
- package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +53 -8
- package/migrations/README.md +77 -0
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type DesignResourceAuthorityIdentity, type DesignResourceAuthoritySourceItem, type DesignResourceDecisionAuthority, type DesignResourceDelegation, type DesignResourceDelta, type DesignResourceRecoveryBase, type DesignResourceRecoveryBaseInput } from "./design-resource-recovery-types.js";
|
|
2
|
+
export declare const DESIGN_RESOURCE_ORIGINS: readonly ["user-direct", "necessary-derived", "repository-evidence-backed", "provider-suggested"];
|
|
3
|
+
export declare const DESIGN_RESOURCE_SEMANTIC_KINDS: readonly ["exact-visual", "product", "business", "permission", "data", "algorithm", "commercial", "safety-security", "technical"];
|
|
4
|
+
export declare function parseRecoveryBaseInput(value: unknown, label: string): DesignResourceRecoveryBaseInput;
|
|
5
|
+
export declare function parseRecoveryBase(value: unknown, label: string): DesignResourceRecoveryBase;
|
|
6
|
+
export declare function parseDelegation(value: unknown, label: string): DesignResourceDelegation;
|
|
7
|
+
export declare function parseAuthoritySourceItem(value: unknown, label: string): DesignResourceAuthoritySourceItem;
|
|
8
|
+
export declare function parseDelta(value: unknown, label: string): DesignResourceDelta;
|
|
9
|
+
export declare function parseAuthorityIdentity(value: unknown, label: string): DesignResourceAuthorityIdentity;
|
|
10
|
+
export declare function parseDecisionAuthority(value: unknown, label: string): DesignResourceDecisionAuthority;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { arrayOf, digest, integer, object, oneOf, stringSet, text, unknownSemantics, } from "./design-resource-recovery-codec-primitives.js";
|
|
2
|
+
export const DESIGN_RESOURCE_ORIGINS = [
|
|
3
|
+
"user-direct",
|
|
4
|
+
"necessary-derived",
|
|
5
|
+
"repository-evidence-backed",
|
|
6
|
+
"provider-suggested",
|
|
7
|
+
];
|
|
8
|
+
const STATUSES = ["accepted", "rejected", "unresolved"];
|
|
9
|
+
const OPERATIONS = ["add", "replace", "remove", "preserve"];
|
|
10
|
+
const ENCODINGS = ["utf8", "utf8-bom", "utf16le", "utf16be"];
|
|
11
|
+
const EOLS = ["none", "lf", "crlf", "cr", "mixed"];
|
|
12
|
+
export const DESIGN_RESOURCE_SEMANTIC_KINDS = [
|
|
13
|
+
"exact-visual",
|
|
14
|
+
"product",
|
|
15
|
+
"business",
|
|
16
|
+
"permission",
|
|
17
|
+
"data",
|
|
18
|
+
"algorithm",
|
|
19
|
+
"commercial",
|
|
20
|
+
"safety-security",
|
|
21
|
+
"technical",
|
|
22
|
+
];
|
|
23
|
+
const SOURCE_ITEM_KINDS = [
|
|
24
|
+
"outcome_result",
|
|
25
|
+
"requirement",
|
|
26
|
+
"control",
|
|
27
|
+
"acceptance",
|
|
28
|
+
"technical_obligation",
|
|
29
|
+
"non_completing",
|
|
30
|
+
"non_goal",
|
|
31
|
+
"forbidden_shortcut",
|
|
32
|
+
"risk_fact",
|
|
33
|
+
"external_confirmation",
|
|
34
|
+
"decision",
|
|
35
|
+
];
|
|
36
|
+
export function parseRecoveryBaseInput(value, label) {
|
|
37
|
+
const row = object(value, label, [
|
|
38
|
+
"locator",
|
|
39
|
+
"raw_byte_digest",
|
|
40
|
+
"materialization",
|
|
41
|
+
"scope_ceiling",
|
|
42
|
+
"in_scope_keys",
|
|
43
|
+
"explicitly_excluded_keys",
|
|
44
|
+
]);
|
|
45
|
+
const materialization = object(row.materialization, `${label}.materialization`, ["kind"], ["authorization_ref"]);
|
|
46
|
+
const kind = oneOf(materialization.kind, ["repository-source", "authorized-recovery-snapshot"], `${label}.materialization.kind`);
|
|
47
|
+
const normalizedMaterialization = kind === "repository-source"
|
|
48
|
+
? { kind }
|
|
49
|
+
: {
|
|
50
|
+
kind,
|
|
51
|
+
authorization_ref: text(materialization.authorization_ref, `${label}.materialization.authorization_ref`),
|
|
52
|
+
};
|
|
53
|
+
if (kind === "repository-source" && "authorization_ref" in materialization)
|
|
54
|
+
throw new Error(`design_resource_recovery_invalid:${label}.materialization:unexpected_authorization_ref`);
|
|
55
|
+
return {
|
|
56
|
+
locator: text(row.locator, `${label}.locator`),
|
|
57
|
+
raw_byte_digest: digest(row.raw_byte_digest, `${label}.raw_byte_digest`),
|
|
58
|
+
materialization: normalizedMaterialization,
|
|
59
|
+
scope_ceiling: text(row.scope_ceiling, `${label}.scope_ceiling`),
|
|
60
|
+
in_scope_keys: stringSet(row.in_scope_keys, `${label}.in_scope_keys`),
|
|
61
|
+
explicitly_excluded_keys: stringSet(row.explicitly_excluded_keys, `${label}.explicitly_excluded_keys`, { allowEmpty: true }),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function parseRecoveryBase(value, label) {
|
|
65
|
+
const row = object(value, label, [
|
|
66
|
+
"locator",
|
|
67
|
+
"raw_byte_digest",
|
|
68
|
+
"materialization",
|
|
69
|
+
"scope_ceiling",
|
|
70
|
+
"in_scope_keys",
|
|
71
|
+
"explicitly_excluded_keys",
|
|
72
|
+
"encoding",
|
|
73
|
+
"eol_policy",
|
|
74
|
+
]);
|
|
75
|
+
const base = parseRecoveryBaseInput(Object.fromEntries(Object.entries(row).filter(([key]) => key !== "encoding" && key !== "eol_policy")), label);
|
|
76
|
+
return {
|
|
77
|
+
...base,
|
|
78
|
+
encoding: oneOf(row.encoding, ENCODINGS, `${label}.encoding`),
|
|
79
|
+
eol_policy: oneOf(row.eol_policy, EOLS, `${label}.eol_policy`),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function parseDelegation(value, label) {
|
|
83
|
+
const row = object(value, label, [
|
|
84
|
+
"key",
|
|
85
|
+
"source_ref",
|
|
86
|
+
"allowed_origins",
|
|
87
|
+
"allowed_target_keys",
|
|
88
|
+
"allowed_semantic_kinds",
|
|
89
|
+
]);
|
|
90
|
+
return {
|
|
91
|
+
key: text(row.key, `${label}.key`),
|
|
92
|
+
source_ref: text(row.source_ref, `${label}.source_ref`),
|
|
93
|
+
allowed_origins: arrayOf(row.allowed_origins, `${label}.allowed_origins`, (item, itemLabel) => oneOf(item, DESIGN_RESOURCE_ORIGINS, itemLabel)),
|
|
94
|
+
allowed_target_keys: stringSet(row.allowed_target_keys, `${label}.allowed_target_keys`),
|
|
95
|
+
allowed_semantic_kinds: stringSet(row.allowed_semantic_kinds, `${label}.allowed_semantic_kinds`).map((value, index) => oneOf(value, DESIGN_RESOURCE_SEMANTIC_KINDS, `${label}.allowed_semantic_kinds[${index}]`)),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export function parseAuthoritySourceItem(value, label) {
|
|
99
|
+
const row = object(value, label, [
|
|
100
|
+
"source_ref",
|
|
101
|
+
"locator",
|
|
102
|
+
"raw_byte_digest",
|
|
103
|
+
"source_item_key",
|
|
104
|
+
"source_item_kind",
|
|
105
|
+
"source_item_text_sha256",
|
|
106
|
+
]);
|
|
107
|
+
return {
|
|
108
|
+
source_ref: text(row.source_ref, `${label}.source_ref`),
|
|
109
|
+
locator: text(row.locator, `${label}.locator`),
|
|
110
|
+
raw_byte_digest: digest(row.raw_byte_digest, `${label}.raw_byte_digest`),
|
|
111
|
+
source_item_key: text(row.source_item_key, `${label}.source_item_key`),
|
|
112
|
+
source_item_kind: oneOf(row.source_item_kind, SOURCE_ITEM_KINDS, `${label}.source_item_kind`),
|
|
113
|
+
source_item_text_sha256: digest(row.source_item_text_sha256, `${label}.source_item_text_sha256`),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export function parseDelta(value, label) {
|
|
117
|
+
const row = object(value, label, [
|
|
118
|
+
"delta_id",
|
|
119
|
+
"sequence",
|
|
120
|
+
"supersedes",
|
|
121
|
+
"proposes_replacement_of",
|
|
122
|
+
"operation",
|
|
123
|
+
"semantic_kind",
|
|
124
|
+
"target_keys",
|
|
125
|
+
"before_semantics",
|
|
126
|
+
"after_semantics",
|
|
127
|
+
"origin",
|
|
128
|
+
"decision_authority",
|
|
129
|
+
"evidence_refs",
|
|
130
|
+
"source_refs",
|
|
131
|
+
"explicitly_unchanged_keys",
|
|
132
|
+
"status",
|
|
133
|
+
]);
|
|
134
|
+
return {
|
|
135
|
+
delta_id: text(row.delta_id, `${label}.delta_id`),
|
|
136
|
+
sequence: integer(row.sequence, `${label}.sequence`, 1),
|
|
137
|
+
supersedes: stringSet(row.supersedes, `${label}.supersedes`, {
|
|
138
|
+
allowEmpty: true,
|
|
139
|
+
}),
|
|
140
|
+
proposes_replacement_of: stringSet(row.proposes_replacement_of, `${label}.proposes_replacement_of`, { allowEmpty: true }),
|
|
141
|
+
operation: oneOf(row.operation, OPERATIONS, `${label}.operation`),
|
|
142
|
+
semantic_kind: oneOf(row.semantic_kind, DESIGN_RESOURCE_SEMANTIC_KINDS, `${label}.semantic_kind`),
|
|
143
|
+
target_keys: stringSet(row.target_keys, `${label}.target_keys`),
|
|
144
|
+
before_semantics: unknownSemantics(row, "before_semantics", label),
|
|
145
|
+
after_semantics: unknownSemantics(row, "after_semantics", label),
|
|
146
|
+
origin: oneOf(row.origin, DESIGN_RESOURCE_ORIGINS, `${label}.origin`),
|
|
147
|
+
decision_authority: parseDecisionAuthority(row.decision_authority, `${label}.decision_authority`),
|
|
148
|
+
evidence_refs: stringSet(row.evidence_refs, `${label}.evidence_refs`, {
|
|
149
|
+
allowEmpty: true,
|
|
150
|
+
}),
|
|
151
|
+
source_refs: stringSet(row.source_refs, `${label}.source_refs`, {
|
|
152
|
+
allowEmpty: true,
|
|
153
|
+
}),
|
|
154
|
+
explicitly_unchanged_keys: stringSet(row.explicitly_unchanged_keys, `${label}.explicitly_unchanged_keys`, { allowEmpty: true }),
|
|
155
|
+
status: oneOf(row.status, STATUSES, `${label}.status`),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export function parseAuthorityIdentity(value, label) {
|
|
159
|
+
const first = object(value, label, ["kind"], ["locator", "raw_byte_digest", "rationale"]);
|
|
160
|
+
const kind = oneOf(first.kind, ["repository-file", "external-immutable", "not-applicable"], `${label}.kind`);
|
|
161
|
+
if (kind === "not-applicable") {
|
|
162
|
+
if ("locator" in first || "raw_byte_digest" in first)
|
|
163
|
+
throw new Error(`design_resource_recovery_invalid:${label}:not_applicable_identity_fields`);
|
|
164
|
+
return {
|
|
165
|
+
kind,
|
|
166
|
+
rationale: text(first.rationale, `${label}.rationale`),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if ("rationale" in first)
|
|
170
|
+
throw new Error(`design_resource_recovery_invalid:${label}:unexpected_rationale`);
|
|
171
|
+
return {
|
|
172
|
+
kind,
|
|
173
|
+
locator: text(first.locator, `${label}.locator`),
|
|
174
|
+
raw_byte_digest: digest(first.raw_byte_digest, `${label}.raw_byte_digest`),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
export function parseDecisionAuthority(value, label) {
|
|
178
|
+
const result = text(value, label);
|
|
179
|
+
if (result === "explicit-user" || result === "none")
|
|
180
|
+
return result;
|
|
181
|
+
if (/^delegated:[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(result))
|
|
182
|
+
return result;
|
|
183
|
+
throw new Error(`design_resource_recovery_invalid:${label}:invalid_authority`);
|
|
184
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DesignResourceAuthorityProjection, DesignResourceRecoveryCheckpoint, DesignResourceRecoveryCreateInput } from "./design-resource-recovery-types.js";
|
|
2
|
+
type RecoveryState = DesignResourceRecoveryCreateInput | DesignResourceRecoveryCheckpoint;
|
|
3
|
+
export declare function validateDesignResourceAuthoritySourceItems(repository: string, state: RecoveryState): Promise<void>;
|
|
4
|
+
export declare function parseAuthorityProjections(sourcePath: string, itemKey: string, normalizedText: string): DesignResourceAuthorityProjection[];
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { decodeDesignResourceText } from "./design-resource-recovery-text.js";
|
|
2
|
+
import { readRecoveryRepositoryFile } from "./design-resource-recovery-files.js";
|
|
3
|
+
import { parseSourceDocument } from "./long-task-source-item-parser.js";
|
|
4
|
+
import { arrayOf, digest, literal, object, oneOf, parseStrictJsonObject, stringSet, text, } from "./design-resource-recovery-codec-primitives.js";
|
|
5
|
+
import { DESIGN_RESOURCE_AUTHORITY_PROJECTION_SCHEMA } from "./design-resource-recovery-schema.js";
|
|
6
|
+
import { DESIGN_RESOURCE_ORIGINS, DESIGN_RESOURCE_SEMANTIC_KINDS, } from "./design-resource-recovery-shape.js";
|
|
7
|
+
import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
|
|
8
|
+
import { isDesignResourceAuthoritativeMeaningSourceKind } from "./design-resource-recovery-authority-policy.js";
|
|
9
|
+
export async function validateDesignResourceAuthoritySourceItems(repository, state) {
|
|
10
|
+
const byLocator = new Map();
|
|
11
|
+
const bySourceRef = new Map();
|
|
12
|
+
for (const declared of state.authority_sources) {
|
|
13
|
+
let loaded = byLocator.get(declared.locator);
|
|
14
|
+
if (!loaded) {
|
|
15
|
+
loaded = await loadAuthoritySourceDocument(repository, declared.locator);
|
|
16
|
+
byLocator.set(declared.locator, loaded);
|
|
17
|
+
}
|
|
18
|
+
if (loaded.rawByteDigest !== declared.raw_byte_digest)
|
|
19
|
+
invalid(`authority_source_raw_digest_mismatch:${declared.source_ref}:${declared.raw_byte_digest}:${loaded.rawByteDigest}`);
|
|
20
|
+
const item = loaded.items.get(declared.source_item_key);
|
|
21
|
+
if (!item)
|
|
22
|
+
invalid(`authority_source_item_missing:${declared.source_ref}:${declared.source_item_key}`);
|
|
23
|
+
if (item.kind !== declared.source_item_kind)
|
|
24
|
+
invalid(`authority_source_item_kind_mismatch:${declared.source_ref}:${declared.source_item_kind}:${item.kind}`);
|
|
25
|
+
if (item.text_sha256 !== declared.source_item_text_sha256)
|
|
26
|
+
invalid(`authority_source_item_digest_mismatch:${declared.source_ref}:${declared.source_item_text_sha256}:${item.text_sha256}`);
|
|
27
|
+
bySourceRef.set(declared.source_ref, {
|
|
28
|
+
kind: item.kind,
|
|
29
|
+
projections: item.projections,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
validateDelegationProjections(state, bySourceRef);
|
|
33
|
+
validateAcceptedDeltaProjections(state, bySourceRef);
|
|
34
|
+
}
|
|
35
|
+
function validateDelegationProjections(state, sources) {
|
|
36
|
+
for (const delegation of state.delegations) {
|
|
37
|
+
const source = sources.get(delegation.source_ref);
|
|
38
|
+
if (!source)
|
|
39
|
+
invalid(`delegation_source_ref_unresolved:${delegation.key}`);
|
|
40
|
+
if (source.kind !== "decision")
|
|
41
|
+
invalid(`delegation_source_not_decision:${delegation.key}`);
|
|
42
|
+
const matching = source.projections.filter((projection) => projection.mode === "delegation" &&
|
|
43
|
+
projection.delegation_key === delegation.key);
|
|
44
|
+
if (matching.length !== 1)
|
|
45
|
+
invalid(`delegation_projection_count:${delegation.key}:${matching.length}`);
|
|
46
|
+
const projection = matching[0];
|
|
47
|
+
if (projection.mode !== "delegation")
|
|
48
|
+
throw new Error("unreachable");
|
|
49
|
+
assertExactSet(projection.allowed_target_keys, delegation.allowed_target_keys, `delegation_projection_targets:${delegation.key}`);
|
|
50
|
+
assertExactSet(projection.allowed_semantic_kinds, delegation.allowed_semantic_kinds, `delegation_projection_semantic_kinds:${delegation.key}`);
|
|
51
|
+
assertExactSet(projection.allowed_origins, delegation.allowed_origins, `delegation_projection_origins:${delegation.key}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function validateAcceptedDeltaProjections(state, sources) {
|
|
55
|
+
const delegations = new Map(state.delegations.map((item) => [item.key, item]));
|
|
56
|
+
for (const delta of state.deltas) {
|
|
57
|
+
if (delta.status !== "accepted")
|
|
58
|
+
continue;
|
|
59
|
+
const sourceRows = delta.source_refs.map((sourceRef) => {
|
|
60
|
+
const source = sources.get(sourceRef);
|
|
61
|
+
if (!source)
|
|
62
|
+
invalid(`delta_source_ref_unresolved:${delta.delta_id}:${sourceRef}`);
|
|
63
|
+
return { sourceRef, ...source };
|
|
64
|
+
});
|
|
65
|
+
const meaningDigest = sha256Hex(canonicalValueJson(delta.after_semantics));
|
|
66
|
+
const matchingMeaning = sourceRows.filter(({ projections }) => projections.some((projection) => projection.mode === "explicit-user" &&
|
|
67
|
+
projection.meaning_sha256 === meaningDigest &&
|
|
68
|
+
projection.semantic_kinds.includes(delta.semantic_kind) &&
|
|
69
|
+
projection.allowed_origins.includes(delta.origin) &&
|
|
70
|
+
sameStringSet(projection.target_keys, delta.target_keys)));
|
|
71
|
+
if (delta.decision_authority === "explicit-user") {
|
|
72
|
+
const decisions = matchingMeaning.filter(({ kind }) => kind === "decision");
|
|
73
|
+
if (!decisions.length)
|
|
74
|
+
invalid(`explicit_user_projection_required:${delta.delta_id}`);
|
|
75
|
+
}
|
|
76
|
+
if (delta.decision_authority.startsWith("delegated:")) {
|
|
77
|
+
const key = delta.decision_authority.slice("delegated:".length);
|
|
78
|
+
const delegation = delegations.get(key);
|
|
79
|
+
if (!delegation)
|
|
80
|
+
invalid(`delegation_not_found:${delta.delta_id}:${key}`);
|
|
81
|
+
const source = sourceRows.find((item) => item.sourceRef === delegation.source_ref);
|
|
82
|
+
if (!source?.projections.some((projection) => projection.mode === "delegation" &&
|
|
83
|
+
projection.delegation_key === key &&
|
|
84
|
+
projection.allowed_origins.includes(delta.origin) &&
|
|
85
|
+
projection.allowed_semantic_kinds.includes(delta.semantic_kind) &&
|
|
86
|
+
delta.target_keys.every((target) => projection.allowed_target_keys.includes(target))))
|
|
87
|
+
invalid(`delegation_projection_mismatch:${delta.delta_id}:${key}`);
|
|
88
|
+
}
|
|
89
|
+
const delegatedExactVisual = delta.semantic_kind === "exact-visual" &&
|
|
90
|
+
delta.decision_authority.startsWith("delegated:");
|
|
91
|
+
const delegatedNonvisual = delta.semantic_kind !== "exact-visual" &&
|
|
92
|
+
delta.decision_authority.startsWith("delegated:");
|
|
93
|
+
if (delegatedNonvisual) {
|
|
94
|
+
const key = delta.decision_authority.slice("delegated:".length);
|
|
95
|
+
const delegation = delegations.get(key);
|
|
96
|
+
const independent = matchingMeaning.filter(({ sourceRef, kind }) => sourceRef !== delegation?.source_ref &&
|
|
97
|
+
isDesignResourceAuthoritativeMeaningSourceKind(kind));
|
|
98
|
+
if (!independent.length)
|
|
99
|
+
invalid(`delegated_nonvisual_meaning_projection_required:${delta.delta_id}`);
|
|
100
|
+
}
|
|
101
|
+
else if (!delegatedExactVisual && !matchingMeaning.length)
|
|
102
|
+
invalid(`accepted_meaning_projection_required:${delta.delta_id}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function loadAuthoritySourceDocument(repository, locator) {
|
|
106
|
+
const snapshot = await readRecoveryRepositoryFile(repository, locator, "design_resource_recovery_authority_source");
|
|
107
|
+
const decoded = decodeDesignResourceText(snapshot.bytes);
|
|
108
|
+
try {
|
|
109
|
+
const parsed = parseSourceDocument(snapshot.relative, decoded.text);
|
|
110
|
+
return {
|
|
111
|
+
rawByteDigest: snapshot.raw_byte_digest,
|
|
112
|
+
items: new Map(parsed.items.map((item) => [
|
|
113
|
+
item.key,
|
|
114
|
+
{
|
|
115
|
+
kind: item.kind,
|
|
116
|
+
text_sha256: item.text_sha256,
|
|
117
|
+
projections: parseAuthorityProjections(snapshot.relative, item.key, item.normalized_text),
|
|
118
|
+
},
|
|
119
|
+
])),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
invalid(`authority_source_parse_failed:${snapshot.relative}:${error.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export function parseAuthorityProjections(sourcePath, itemKey, normalizedText) {
|
|
127
|
+
const projections = [];
|
|
128
|
+
for (const [index, line] of normalizedText.split("\n").entries()) {
|
|
129
|
+
if (!line.includes(DESIGN_RESOURCE_AUTHORITY_PROJECTION_SCHEMA))
|
|
130
|
+
continue;
|
|
131
|
+
const match = /^<!-- ty-dra-authority-v1 (\{.*\}) -->$/u.exec(line);
|
|
132
|
+
if (!match)
|
|
133
|
+
invalid(`authority_projection_marker_invalid:${sourcePath}:${itemKey}:${index + 1}`);
|
|
134
|
+
projections.push(parseAuthorityProjection(match[1], `${sourcePath}:${itemKey}:${index + 1}`));
|
|
135
|
+
}
|
|
136
|
+
const identities = projections.map(projectionIdentity);
|
|
137
|
+
if (new Set(identities).size !== identities.length)
|
|
138
|
+
invalid(`authority_projection_duplicate:${sourcePath}:${itemKey}`);
|
|
139
|
+
return projections;
|
|
140
|
+
}
|
|
141
|
+
function parseAuthorityProjection(content, label) {
|
|
142
|
+
const parsed = parseStrictJsonObject(content, `authority_projection:${label}`);
|
|
143
|
+
const header = object(parsed, `authority_projection:${label}`, ["schema_version", "mode"], [
|
|
144
|
+
"target_keys",
|
|
145
|
+
"semantic_kinds",
|
|
146
|
+
"allowed_origins",
|
|
147
|
+
"meaning_sha256",
|
|
148
|
+
"delegation_key",
|
|
149
|
+
"allowed_target_keys",
|
|
150
|
+
"allowed_semantic_kinds",
|
|
151
|
+
]);
|
|
152
|
+
literal(header.schema_version, DESIGN_RESOURCE_AUTHORITY_PROJECTION_SCHEMA, `authority_projection:${label}.schema_version`);
|
|
153
|
+
const mode = oneOf(header.mode, ["explicit-user", "delegation"], `authority_projection:${label}.mode`);
|
|
154
|
+
if (mode === "explicit-user") {
|
|
155
|
+
const row = object(parsed, `authority_projection:${label}`, [
|
|
156
|
+
"schema_version",
|
|
157
|
+
"mode",
|
|
158
|
+
"target_keys",
|
|
159
|
+
"semantic_kinds",
|
|
160
|
+
"allowed_origins",
|
|
161
|
+
"meaning_sha256",
|
|
162
|
+
]);
|
|
163
|
+
return {
|
|
164
|
+
schema_version: DESIGN_RESOURCE_AUTHORITY_PROJECTION_SCHEMA,
|
|
165
|
+
mode,
|
|
166
|
+
target_keys: stringSet(row.target_keys, `authority_projection:${label}.target_keys`),
|
|
167
|
+
semantic_kinds: enumSet(row.semantic_kinds, DESIGN_RESOURCE_SEMANTIC_KINDS, `authority_projection:${label}.semantic_kinds`),
|
|
168
|
+
allowed_origins: enumSet(row.allowed_origins, DESIGN_RESOURCE_ORIGINS, `authority_projection:${label}.allowed_origins`),
|
|
169
|
+
meaning_sha256: digest(row.meaning_sha256, `authority_projection:${label}.meaning_sha256`),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const row = object(parsed, `authority_projection:${label}`, [
|
|
173
|
+
"schema_version",
|
|
174
|
+
"mode",
|
|
175
|
+
"delegation_key",
|
|
176
|
+
"allowed_target_keys",
|
|
177
|
+
"allowed_semantic_kinds",
|
|
178
|
+
"allowed_origins",
|
|
179
|
+
]);
|
|
180
|
+
const delegationKey = text(row.delegation_key, `authority_projection:${label}.delegation_key`);
|
|
181
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(delegationKey))
|
|
182
|
+
invalid(`authority_projection_delegation_key_invalid:${label}`);
|
|
183
|
+
return {
|
|
184
|
+
schema_version: DESIGN_RESOURCE_AUTHORITY_PROJECTION_SCHEMA,
|
|
185
|
+
mode,
|
|
186
|
+
delegation_key: delegationKey,
|
|
187
|
+
allowed_target_keys: stringSet(row.allowed_target_keys, `authority_projection:${label}.allowed_target_keys`),
|
|
188
|
+
allowed_semantic_kinds: enumSet(row.allowed_semantic_kinds, DESIGN_RESOURCE_SEMANTIC_KINDS, `authority_projection:${label}.allowed_semantic_kinds`),
|
|
189
|
+
allowed_origins: enumSet(row.allowed_origins, DESIGN_RESOURCE_ORIGINS, `authority_projection:${label}.allowed_origins`),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function enumSet(value, allowed, label) {
|
|
193
|
+
const result = arrayOf(value, label, (item, itemLabel) => oneOf(item, allowed, itemLabel));
|
|
194
|
+
if (new Set(result).size !== result.length)
|
|
195
|
+
invalid(`authority_projection_duplicate_value:${label}`);
|
|
196
|
+
return result.sort(compareText);
|
|
197
|
+
}
|
|
198
|
+
function projectionIdentity(projection) {
|
|
199
|
+
return canonicalValueJson(projection);
|
|
200
|
+
}
|
|
201
|
+
function assertExactSet(actual, expected, label) {
|
|
202
|
+
const left = [...actual].sort(compareText);
|
|
203
|
+
const right = [...expected].sort(compareText);
|
|
204
|
+
if (left.length !== right.length ||
|
|
205
|
+
left.some((value, index) => value !== right[index]))
|
|
206
|
+
invalid(label);
|
|
207
|
+
}
|
|
208
|
+
function sameStringSet(left, right) {
|
|
209
|
+
const first = [...left].sort(compareText);
|
|
210
|
+
const second = [...right].sort(compareText);
|
|
211
|
+
return (first.length === second.length &&
|
|
212
|
+
first.every((value, index) => value === second[index]));
|
|
213
|
+
}
|
|
214
|
+
function compareText(left, right) {
|
|
215
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
216
|
+
}
|
|
217
|
+
function invalid(reason) {
|
|
218
|
+
throw new Error(`design_resource_recovery_invalid:${reason}`);
|
|
219
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DesignResourceEolPolicy, DesignResourceTextEncoding } from "./design-resource-recovery-types.js";
|
|
2
|
+
import type { DesignResourceExactPatch } from "./design-resource-recovery-patch-types.js";
|
|
3
|
+
export interface DesignResourceDecodedText {
|
|
4
|
+
text: string;
|
|
5
|
+
encoding: DesignResourceTextEncoding;
|
|
6
|
+
eol_policy: DesignResourceEolPolicy;
|
|
7
|
+
}
|
|
8
|
+
export declare function decodeDesignResourceText(bytes: Uint8Array): DesignResourceDecodedText;
|
|
9
|
+
export declare function encodeDesignResourceText(text: string, encoding: DesignResourceTextEncoding): Buffer;
|
|
10
|
+
export declare function applyDesignResourceExactPatch(bytes: Uint8Array, patch: DesignResourceExactPatch, expectedEncoding?: DesignResourceTextEncoding, expectedEol?: Exclude<DesignResourceEolPolicy, "mixed">): {
|
|
11
|
+
bytes: Buffer;
|
|
12
|
+
encoding: DesignResourceTextEncoding;
|
|
13
|
+
eol_policy: Exclude<DesignResourceEolPolicy, "mixed">;
|
|
14
|
+
};
|
|
15
|
+
export declare function verifyDesignResourceExactPatchReadback(bytes: Uint8Array, patch: DesignResourceExactPatch): void;
|
|
16
|
+
export declare function verifyDesignResourceSupersededTextReadback(bytes: Uint8Array, patch: DesignResourceExactPatch, supersedingDeltaIds: ReadonlySet<string>): void;
|
|
17
|
+
export declare function detectEolPolicy(text: string): DesignResourceEolPolicy;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { sha256Hex } from "./strict-codec.js";
|
|
2
|
+
export function decodeDesignResourceText(bytes) {
|
|
3
|
+
let encoding;
|
|
4
|
+
let body;
|
|
5
|
+
if (startsWith(bytes, [0xef, 0xbb, 0xbf])) {
|
|
6
|
+
encoding = "utf8-bom";
|
|
7
|
+
body = bytes.subarray(3);
|
|
8
|
+
}
|
|
9
|
+
else if (startsWith(bytes, [0xff, 0xfe])) {
|
|
10
|
+
encoding = "utf16le";
|
|
11
|
+
body = bytes.subarray(2);
|
|
12
|
+
}
|
|
13
|
+
else if (startsWith(bytes, [0xfe, 0xff])) {
|
|
14
|
+
encoding = "utf16be";
|
|
15
|
+
body = swapUtf16(bytes.subarray(2));
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
encoding = "utf8";
|
|
19
|
+
body = bytes;
|
|
20
|
+
}
|
|
21
|
+
if ((encoding === "utf16le" || encoding === "utf16be") && body.length % 2)
|
|
22
|
+
invalid("odd_utf16_byte_length");
|
|
23
|
+
let text;
|
|
24
|
+
try {
|
|
25
|
+
text = new TextDecoder(encoding === "utf16le" || encoding === "utf16be" ? "utf-16le" : "utf-8", { fatal: true }).decode(body);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
invalid(`invalid_${encoding}`);
|
|
29
|
+
}
|
|
30
|
+
if (text.includes("\0"))
|
|
31
|
+
invalid("nul_character_not_allowed");
|
|
32
|
+
return { text, encoding, eol_policy: detectEolPolicy(text) };
|
|
33
|
+
}
|
|
34
|
+
export function encodeDesignResourceText(text, encoding) {
|
|
35
|
+
if (text.includes("\0"))
|
|
36
|
+
invalid("nul_character_not_allowed");
|
|
37
|
+
if (encoding === "utf8")
|
|
38
|
+
return Buffer.from(text, "utf8");
|
|
39
|
+
if (encoding === "utf8-bom")
|
|
40
|
+
return Buffer.concat([Buffer.from([0xef, 0xbb, 0xbf]), Buffer.from(text)]);
|
|
41
|
+
const littleEndian = Buffer.from(text, "utf16le");
|
|
42
|
+
if (encoding === "utf16le")
|
|
43
|
+
return Buffer.concat([Buffer.from([0xff, 0xfe]), littleEndian]);
|
|
44
|
+
return Buffer.concat([
|
|
45
|
+
Buffer.from([0xfe, 0xff]),
|
|
46
|
+
Buffer.from(swapUtf16(littleEndian)),
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
export function applyDesignResourceExactPatch(bytes, patch, expectedEncoding, expectedEol) {
|
|
50
|
+
const decoded = decodeDesignResourceText(bytes);
|
|
51
|
+
if (decoded.eol_policy === "mixed")
|
|
52
|
+
invalid("mixed_eol_writeback_unsupported");
|
|
53
|
+
if (expectedEncoding && decoded.encoding !== expectedEncoding)
|
|
54
|
+
invalid(`encoding_changed:${expectedEncoding}:${decoded.encoding}`);
|
|
55
|
+
if (expectedEol && decoded.eol_policy !== expectedEol)
|
|
56
|
+
invalid(`eol_changed:${expectedEol}:${decoded.eol_policy}`);
|
|
57
|
+
const operations = resolveOriginalSourceSpans(decoded.text, patch);
|
|
58
|
+
let text = decoded.text;
|
|
59
|
+
for (const operation of [...operations].reverse()) {
|
|
60
|
+
const { start_offset: start, end_offset: end } = operation.source_span;
|
|
61
|
+
text = `${text.slice(0, start)}${operation.after_text}${text.slice(end)}`;
|
|
62
|
+
}
|
|
63
|
+
assertPatchFinalIntervals(text, patch);
|
|
64
|
+
assertPatchAfterTextOccurrences(text, patch);
|
|
65
|
+
const postEol = detectEolPolicy(text);
|
|
66
|
+
if (postEol !== decoded.eol_policy)
|
|
67
|
+
invalid(`patch_changes_eol_policy:${decoded.eol_policy}:${postEol}`);
|
|
68
|
+
return {
|
|
69
|
+
bytes: encodeDesignResourceText(text, decoded.encoding),
|
|
70
|
+
encoding: decoded.encoding,
|
|
71
|
+
eol_policy: decoded.eol_policy,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function verifyDesignResourceExactPatchReadback(bytes, patch) {
|
|
75
|
+
const decoded = decodeDesignResourceText(bytes);
|
|
76
|
+
assertPatchFinalIntervals(decoded.text, patch);
|
|
77
|
+
assertPatchAfterTextOccurrences(decoded.text, patch);
|
|
78
|
+
}
|
|
79
|
+
export function verifyDesignResourceSupersededTextReadback(bytes, patch, supersedingDeltaIds) {
|
|
80
|
+
const text = decodeDesignResourceText(bytes).text;
|
|
81
|
+
for (const operation of patch.operations) {
|
|
82
|
+
if (!supersedingDeltaIds.has(operation.delta_id))
|
|
83
|
+
continue;
|
|
84
|
+
const count = occurrenceCount(text, operation.before_text);
|
|
85
|
+
if (count !== 0)
|
|
86
|
+
invalid(`superseded_before_text_leakage:${operation.operation_id}:${count}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function detectEolPolicy(text) {
|
|
90
|
+
const withoutCrLf = text.replace(/\r\n/gu, "");
|
|
91
|
+
const kinds = [
|
|
92
|
+
text.includes("\r\n") ? "crlf" : null,
|
|
93
|
+
withoutCrLf.includes("\n") ? "lf" : null,
|
|
94
|
+
withoutCrLf.includes("\r") ? "cr" : null,
|
|
95
|
+
].filter((value) => value !== null);
|
|
96
|
+
if (!kinds.length)
|
|
97
|
+
return "none";
|
|
98
|
+
return kinds.length === 1 ? kinds[0] : "mixed";
|
|
99
|
+
}
|
|
100
|
+
function occurrenceCount(value, search) {
|
|
101
|
+
if (!search)
|
|
102
|
+
invalid("patch_before_text_empty");
|
|
103
|
+
let count = 0;
|
|
104
|
+
let offset = 0;
|
|
105
|
+
while (true) {
|
|
106
|
+
const found = value.indexOf(search, offset);
|
|
107
|
+
if (found < 0)
|
|
108
|
+
return count;
|
|
109
|
+
count += 1;
|
|
110
|
+
offset = found + search.length;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function resolveOriginalSourceSpans(text, patch) {
|
|
114
|
+
const operations = [...patch.operations].sort((left, right) => left.source_span.start_offset - right.source_span.start_offset ||
|
|
115
|
+
left.source_span.end_offset - right.source_span.end_offset ||
|
|
116
|
+
left.operation_id.localeCompare(right.operation_id));
|
|
117
|
+
let previous;
|
|
118
|
+
for (const operation of operations) {
|
|
119
|
+
const span = operation.source_span;
|
|
120
|
+
if (span.coordinate_system !== "utf16-code-unit-v1" ||
|
|
121
|
+
span.start_offset < 0 ||
|
|
122
|
+
span.end_offset <= span.start_offset ||
|
|
123
|
+
span.end_offset > text.length ||
|
|
124
|
+
span.end_offset - span.start_offset !== operation.before_text.length)
|
|
125
|
+
invalid(`patch_source_span_range:${operation.operation_id}`);
|
|
126
|
+
const actual = text.slice(span.start_offset, span.end_offset);
|
|
127
|
+
if (actual !== operation.before_text ||
|
|
128
|
+
sha256Hex(actual) !== span.before_text_sha256 ||
|
|
129
|
+
span.before_text_sha256 !== operation.before_text_sha256)
|
|
130
|
+
invalid(`patch_source_span_preimage:${operation.operation_id}`);
|
|
131
|
+
if ((operation.operation === "remove" ||
|
|
132
|
+
(operation.operation === "add" &&
|
|
133
|
+
operation.after_text.startsWith("<!-- ty-dra-proposal-scalar-v1 "))) &&
|
|
134
|
+
span.start_offset > 0 &&
|
|
135
|
+
!["\n", "\r"].includes(text[span.start_offset - 1]))
|
|
136
|
+
invalid(`patch_carrier_line_boundary:${operation.operation_id}`);
|
|
137
|
+
const count = occurrenceCount(text, operation.before_text);
|
|
138
|
+
if (count !== operation.expected_occurrences)
|
|
139
|
+
invalid(`patch_occurrence_mismatch:${operation.operation_id}:${operation.expected_occurrences}:${count}`);
|
|
140
|
+
if (previous && span.start_offset < previous.source_span.end_offset)
|
|
141
|
+
invalid(`patch_source_span_overlap:${previous.operation_id}:${operation.operation_id}`);
|
|
142
|
+
previous = operation;
|
|
143
|
+
}
|
|
144
|
+
return operations;
|
|
145
|
+
}
|
|
146
|
+
function assertPatchFinalIntervals(text, patch) {
|
|
147
|
+
const operations = [...patch.operations].sort((left, right) => left.source_span.start_offset - right.source_span.start_offset ||
|
|
148
|
+
left.source_span.end_offset - right.source_span.end_offset ||
|
|
149
|
+
left.operation_id.localeCompare(right.operation_id));
|
|
150
|
+
let offsetDelta = 0;
|
|
151
|
+
for (const operation of operations) {
|
|
152
|
+
const start = operation.source_span.start_offset + offsetDelta;
|
|
153
|
+
const end = start + operation.after_text.length;
|
|
154
|
+
if (start < 0 ||
|
|
155
|
+
end > text.length ||
|
|
156
|
+
text.slice(start, end) !== operation.after_text)
|
|
157
|
+
invalid(`patch_readback_output_span:${operation.operation_id}`);
|
|
158
|
+
offsetDelta +=
|
|
159
|
+
operation.after_text.length -
|
|
160
|
+
(operation.source_span.end_offset - operation.source_span.start_offset);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function assertPatchAfterTextOccurrences(text, patch) {
|
|
164
|
+
for (const operation of patch.operations) {
|
|
165
|
+
const remove = operation.operation === "remove";
|
|
166
|
+
const count = occurrenceCount(text, remove ? operation.before_text : operation.after_text);
|
|
167
|
+
const expected = remove ? 0 : operation.expected_occurrences;
|
|
168
|
+
if (count !== expected)
|
|
169
|
+
invalid(`${remove ? "patch_remove_before_still_present" : "patch_after_occurrence_mismatch"}:${operation.operation_id}:${expected}:${count}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function startsWith(bytes, prefix) {
|
|
173
|
+
return prefix.every((value, index) => bytes[index] === value);
|
|
174
|
+
}
|
|
175
|
+
function swapUtf16(bytes) {
|
|
176
|
+
const result = Uint8Array.from(bytes);
|
|
177
|
+
for (let index = 0; index < result.length; index += 2) {
|
|
178
|
+
const first = result[index];
|
|
179
|
+
result[index] = result[index + 1];
|
|
180
|
+
result[index + 1] = first;
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
function invalid(reason) {
|
|
185
|
+
throw new Error(`design_resource_recovery_invalid:text:${reason}`);
|
|
186
|
+
}
|