@untangle-code/context-artifacts 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +90 -0
  3. package/README.md +247 -2
  4. package/THIRD_PARTY_LICENSES.md +24 -0
  5. package/dist/adapters/asyncapi.d.ts +20 -0
  6. package/dist/adapters/asyncapi.d.ts.map +1 -0
  7. package/dist/adapters/asyncapi.js +95 -0
  8. package/dist/adapters/asyncapi.js.map +1 -0
  9. package/dist/adapters/docker.d.ts +43 -0
  10. package/dist/adapters/docker.d.ts.map +1 -0
  11. package/dist/adapters/docker.js +97 -0
  12. package/dist/adapters/docker.js.map +1 -0
  13. package/dist/adapters/kubernetes.d.ts +22 -0
  14. package/dist/adapters/kubernetes.d.ts.map +1 -0
  15. package/dist/adapters/kubernetes.js +56 -0
  16. package/dist/adapters/kubernetes.js.map +1 -0
  17. package/dist/adapters/markdown.d.ts +35 -0
  18. package/dist/adapters/markdown.d.ts.map +1 -0
  19. package/dist/adapters/markdown.js +36 -0
  20. package/dist/adapters/markdown.js.map +1 -0
  21. package/dist/adapters/openapi.d.ts +21 -0
  22. package/dist/adapters/openapi.d.ts.map +1 -0
  23. package/dist/adapters/openapi.js +101 -0
  24. package/dist/adapters/openapi.js.map +1 -0
  25. package/dist/adapters/protobuf.d.ts +34 -0
  26. package/dist/adapters/protobuf.d.ts.map +1 -0
  27. package/dist/adapters/protobuf.js +221 -0
  28. package/dist/adapters/protobuf.js.map +1 -0
  29. package/dist/adapters/shared.d.ts +65 -0
  30. package/dist/adapters/shared.d.ts.map +1 -0
  31. package/dist/adapters/shared.js +95 -0
  32. package/dist/adapters/shared.js.map +1 -0
  33. package/dist/adapters/sql.d.ts +104 -0
  34. package/dist/adapters/sql.d.ts.map +1 -0
  35. package/dist/adapters/sql.js +427 -0
  36. package/dist/adapters/sql.js.map +1 -0
  37. package/dist/adapters/terraform.d.ts +40 -0
  38. package/dist/adapters/terraform.d.ts.map +1 -0
  39. package/dist/adapters/terraform.js +186 -0
  40. package/dist/adapters/terraform.js.map +1 -0
  41. package/dist/adapters/text.d.ts +24 -0
  42. package/dist/adapters/text.d.ts.map +1 -0
  43. package/dist/adapters/text.js +31 -0
  44. package/dist/adapters/text.js.map +1 -0
  45. package/dist/config-validation.d.ts +18 -0
  46. package/dist/config-validation.d.ts.map +1 -0
  47. package/dist/config-validation.js +30 -0
  48. package/dist/config-validation.js.map +1 -0
  49. package/dist/coverage.d.ts +114 -0
  50. package/dist/coverage.d.ts.map +1 -0
  51. package/dist/coverage.js +0 -0
  52. package/dist/coverage.js.map +1 -0
  53. package/dist/ids.d.ts +12 -0
  54. package/dist/ids.d.ts.map +1 -0
  55. package/dist/ids.js +42 -0
  56. package/dist/ids.js.map +1 -0
  57. package/dist/index.d.ts +55 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +18 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/registry.d.ts +51 -0
  62. package/dist/registry.d.ts.map +1 -0
  63. package/dist/registry.js +86 -0
  64. package/dist/registry.js.map +1 -0
  65. package/dist/staleness.d.ts +44 -0
  66. package/dist/staleness.d.ts.map +1 -0
  67. package/dist/staleness.js +35 -0
  68. package/dist/staleness.js.map +1 -0
  69. package/dist/text/delimiter-balance.d.ts +95 -0
  70. package/dist/text/delimiter-balance.d.ts.map +1 -0
  71. package/dist/text/delimiter-balance.js +209 -0
  72. package/dist/text/delimiter-balance.js.map +1 -0
  73. package/dist/text/line-index.d.ts +61 -0
  74. package/dist/text/line-index.d.ts.map +1 -0
  75. package/dist/text/line-index.js +75 -0
  76. package/dist/text/line-index.js.map +1 -0
  77. package/dist/text/yaml-lite.d.ts +73 -0
  78. package/dist/text/yaml-lite.d.ts.map +1 -0
  79. package/dist/text/yaml-lite.js +486 -0
  80. package/dist/text/yaml-lite.js.map +1 -0
  81. package/package.json +61 -3
  82. package/src/adapters/__golden__/adr-0001-sections.json +68 -0
  83. package/src/adapters/__golden__/app-compose-services.json +24 -0
  84. package/src/adapters/__golden__/app-image-stages.json +24 -0
  85. package/src/adapters/__golden__/infra-main-sections.json +46 -0
  86. package/src/adapters/__golden__/orders-events-operations.json +24 -0
  87. package/src/adapters/__golden__/orders-proto-sections.json +57 -0
  88. package/src/adapters/__golden__/orders-schema-statements.json +68 -0
  89. package/src/adapters/__golden__/users-api-operations.json +46 -0
  90. package/src/adapters/__golden__/web-manifests-resources.json +24 -0
  91. package/src/adapters/asyncapi.ts +147 -0
  92. package/src/adapters/docker.ts +170 -0
  93. package/src/adapters/kubernetes.ts +103 -0
  94. package/src/adapters/markdown.ts +88 -0
  95. package/src/adapters/openapi.ts +156 -0
  96. package/src/adapters/protobuf.ts +286 -0
  97. package/src/adapters/shared.ts +151 -0
  98. package/src/adapters/sql.ts +555 -0
  99. package/src/adapters/terraform.ts +261 -0
  100. package/src/adapters/text.ts +72 -0
  101. package/src/config-validation.ts +41 -0
  102. package/src/coverage.ts +336 -0
  103. package/src/ids.ts +57 -0
  104. package/src/index.ts +105 -0
  105. package/src/registry.ts +155 -0
  106. package/src/staleness.ts +69 -0
  107. package/src/text/delimiter-balance.ts +295 -0
  108. package/src/text/line-index.ts +108 -0
  109. package/src/text/yaml-lite.ts +565 -0
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ export { createArtifactAdapterRegistry } from "./registry.js";
2
+ export { createMarkdownAdapter } from "./adapters/markdown.js";
3
+ export { createSqlAdapter, DEFAULT_SQL_DIALECT } from "./adapters/sql.js";
4
+ export { createOpenApiAdapter } from "./adapters/openapi.js";
5
+ export { createAsyncApiAdapter } from "./adapters/asyncapi.js";
6
+ export { createProtobufAdapter } from "./adapters/protobuf.js";
7
+ export { createTerraformAdapter } from "./adapters/terraform.js";
8
+ export { createKubernetesAdapter } from "./adapters/kubernetes.js";
9
+ export { createDockerAdapter } from "./adapters/docker.js";
10
+ export { createTextFallbackAdapter, TEXT_WINDOW_LINES } from "./adapters/text.js";
11
+ export { validateArtifactConfigs } from "./config-validation.js";
12
+ export { computeArtifactConfigHash, computeArtifactContentHash, computeArtifactStalenessSnapshot, isArtifactStale, } from "./staleness.js";
13
+ export { buildArtifactSkipLedger, defaultSkipLedgerReporter } from "./coverage.js";
14
+ export { buildArtifactChunkId } from "./ids.js";
15
+ export { buildLineIndex, rangeForLines, sliceBytes, sliceLines, splitLines, } from "./text/line-index.js";
16
+ export { describeDelimiterImbalances, findDelimiterImbalances } from "./text/delimiter-balance.js";
17
+ export { YamlParseError, mapGet, parseYamlNode, scalarString, splitYamlDocuments, } from "./text/yaml-lite.js";
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AAG9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAElF,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAGjE,OAAO,EACL,yBAAyB,EACzB,0BAA0B,EAC1B,gCAAgC,EAChC,eAAe,GAChB,MAAM,gBAAgB,CAAC;AASxB,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAEnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAOhD,OAAO,EACL,cAAc,EACd,aAAa,EACb,UAAU,EACV,UAAU,EACV,UAAU,GACX,MAAM,sBAAsB,CAAC;AAa9B,OAAO,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAUnG,OAAO,EACL,cAAc,EACd,MAAM,EACN,aAAa,EACb,YAAY,EACZ,kBAAkB,GACnB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * `ArtifactAdapterRegistry` (architecture.md §20) — a composite
3
+ * `ArtifactAdapter` that dispatches to the right concrete format adapter
4
+ * and NEVER fails to produce a result: the generic text-fallback adapter
5
+ * is always registered last as a catch-all, so `read()`/`chunk()` always
6
+ * succeed for every file (spec.md §19.2's "sicherer Text-Fallback";
7
+ * rule 4 / BUILD_PLAN §12 E4 — nothing is silently unindexable).
8
+ *
9
+ * Dispatch order (auto-detection priority, see each adapter's own
10
+ * `supports()` for its exact rule):
11
+ *
12
+ * 1. OpenAPI / AsyncAPI — filename-hint + yaml/json extension
13
+ * 2. Docker / Compose — `Dockerfile*` / `(docker-)compose.y*ml` filename
14
+ * 3. Kubernetes — any other `.yaml`/`.yml`
15
+ * 4. Markdown, SQL, Protobuf, Terraform — unambiguous extensions
16
+ * 5. Text fallback — catch-all
17
+ *
18
+ * `chunk()`/`links()` reuse the SAME per-adapter `supports()` rule, called
19
+ * with `(document.type, document.path)` instead of the config's declared
20
+ * type — every non-"text" `ArtifactDocument.type` is itself one adapter's
21
+ * own literal (`"sql"`, `"openapi"`, …), so this always resolves
22
+ * correctly without needing a second identity mechanism; the Docker
23
+ * adapter's `supports()` additionally recognizes `type === "text"` for
24
+ * exactly this reason (its documents carry type `"text"`, see `docker.ts`).
25
+ */
26
+ import type { ArtifactAdapter } from "@untangle-code/domain";
27
+ import type { SkipLedgerReporter } from "./adapters/shared.js";
28
+ export interface ArtifactAdapterRegistry extends ArtifactAdapter {
29
+ /** Registers an additional adapter with LOWER priority than every built-in one (inserted just before the text fallback). */
30
+ register(adapter: ArtifactAdapter): void;
31
+ listAdapters(): readonly ArtifactAdapter[];
32
+ }
33
+ export interface ArtifactAdapterRegistryOptions {
34
+ /**
35
+ * BUILD_PLAN §12 E4: overrides how every BUILT-IN adapter's `chunk()`
36
+ * surfaces a real skip-ledger finding (default: one stderr warning line
37
+ * per finding — see `coverage.ts`'s `defaultSkipLedgerReporter`). Wire in
38
+ * a real structured logger here once, instead of passing the same
39
+ * `onSkipLedgerGap` to every individual `create*Adapter()` call. Does
40
+ * NOT apply to `extra` adapters — those are already fully constructed
41
+ * `ArtifactAdapter`s by the time they reach this factory.
42
+ */
43
+ onSkipLedgerGap?: SkipLedgerReporter;
44
+ }
45
+ /**
46
+ * Builds a registry with every built-in format adapter registered
47
+ * (priority order per this module's doc comment), plus any `extra`
48
+ * adapters appended just before the text fallback.
49
+ */
50
+ export declare function createArtifactAdapterRegistry(extra?: ArtifactAdapter[], options?: ArtifactAdapterRegistryOptions): ArtifactAdapterRegistry;
51
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EACV,eAAe,EAMhB,MAAM,uBAAuB,CAAC;AAS/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAK/D,MAAM,WAAW,uBAAwB,SAAQ,eAAe;IAC9D,4HAA4H;IAC5H,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,YAAY,IAAI,SAAS,eAAe,EAAE,CAAC;CAC5C;AAoED,MAAM,WAAW,8BAA8B;IAC7C;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,kBAAkB,CAAC;CACtC;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,GAAE,eAAe,EAAO,EAC7B,OAAO,GAAE,8BAAmC,GAC3C,uBAAuB,CAezB"}
@@ -0,0 +1,86 @@
1
+ import { jobCancelledError } from "@untangle-code/observability";
2
+ import { createAsyncApiAdapter } from "./adapters/asyncapi.js";
3
+ import { createDockerAdapter } from "./adapters/docker.js";
4
+ import { createKubernetesAdapter } from "./adapters/kubernetes.js";
5
+ import { createMarkdownAdapter } from "./adapters/markdown.js";
6
+ import { createOpenApiAdapter } from "./adapters/openapi.js";
7
+ import { createProtobufAdapter } from "./adapters/protobuf.js";
8
+ import { createSqlAdapter } from "./adapters/sql.js";
9
+ import { createTerraformAdapter } from "./adapters/terraform.js";
10
+ import { createTextFallbackAdapter } from "./adapters/text.js";
11
+ class DefaultArtifactAdapterRegistry {
12
+ textFallback;
13
+ adapters;
14
+ constructor(builtins, textFallback) {
15
+ this.textFallback = textFallback;
16
+ this.adapters = [...builtins, textFallback];
17
+ }
18
+ register(adapter) {
19
+ // Keep the text fallback last no matter when custom adapters are added.
20
+ const withoutFallback = this.adapters.filter((existing) => existing !== this.textFallback);
21
+ this.adapters = [...withoutFallback, adapter, this.textFallback];
22
+ }
23
+ listAdapters() {
24
+ return [...this.adapters];
25
+ }
26
+ supports(type, path) {
27
+ return this.adapters.some((adapter) => adapter.supports(type, path));
28
+ }
29
+ resolve(type, path) {
30
+ return this.adapters.find((adapter) => adapter.supports(type, path)) ?? this.textFallback;
31
+ }
32
+ async read(input, options) {
33
+ const declaredType = input.config.type ?? "auto";
34
+ const groups = new Map();
35
+ for (const file of input.files) {
36
+ if (options?.signal?.aborted) {
37
+ throw jobCancelledError(`context artifact "${input.config.name}" read was aborted`, {
38
+ operation: "context_artifact_read",
39
+ context: { artifactName: input.config.name },
40
+ });
41
+ }
42
+ const adapter = this.resolve(declaredType, file.path);
43
+ const existing = groups.get(adapter);
44
+ if (existing) {
45
+ existing.push(file);
46
+ }
47
+ else {
48
+ groups.set(adapter, [file]);
49
+ }
50
+ }
51
+ const documents = [];
52
+ for (const [adapter, files] of groups) {
53
+ const group = await adapter.read({ config: input.config, files }, options);
54
+ documents.push(...group);
55
+ }
56
+ return documents;
57
+ }
58
+ chunk(document) {
59
+ return this.resolve(document.type, document.path).chunk(document);
60
+ }
61
+ links(document) {
62
+ const adapter = this.resolve(document.type, document.path);
63
+ return adapter.links?.(document) ?? [];
64
+ }
65
+ }
66
+ /**
67
+ * Builds a registry with every built-in format adapter registered
68
+ * (priority order per this module's doc comment), plus any `extra`
69
+ * adapters appended just before the text fallback.
70
+ */
71
+ export function createArtifactAdapterRegistry(extra = [], options = {}) {
72
+ const adapterOptions = options.onSkipLedgerGap === undefined ? {} : { onSkipLedgerGap: options.onSkipLedgerGap };
73
+ const builtins = [
74
+ createOpenApiAdapter(adapterOptions),
75
+ createAsyncApiAdapter(adapterOptions),
76
+ createDockerAdapter(adapterOptions),
77
+ createKubernetesAdapter(adapterOptions),
78
+ createMarkdownAdapter(adapterOptions),
79
+ createSqlAdapter(adapterOptions),
80
+ createProtobufAdapter(adapterOptions),
81
+ createTerraformAdapter(adapterOptions),
82
+ ...extra,
83
+ ];
84
+ return new DefaultArtifactAdapterRegistry(builtins, createTextFallbackAdapter(adapterOptions));
85
+ }
86
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Staleness detection (spec.md §19.2: "Artefaktkonfiguration und
3
+ * Inhaltshash MÜSSEN Staleness bestimmen.").
4
+ *
5
+ * A context artifact goes stale when EITHER its configuration changes
6
+ * (e.g. `include`/`exclude` edited, `description` reworded, `path`
7
+ * repointed) OR its underlying file content changes (a file was edited,
8
+ * added, or removed), independently of each other — so both are hashed
9
+ * separately and compared separately; collapsing them into one combined
10
+ * hash would make "what changed" undiagnosable from the stored snapshot
11
+ * alone.
12
+ */
13
+ import type { ArtifactDocument, ContextArtifactConfig } from "@untangle-code/domain";
14
+ /** The two independent inputs to staleness (spec.md §19.2), snapshotted together. */
15
+ export interface ArtifactStalenessSnapshot {
16
+ /** SHA-256 of the canonical JSON of the artifact's `ContextArtifactConfig`. */
17
+ configHash: string;
18
+ /**
19
+ * SHA-256 combining every current document's path and content hash
20
+ * (spec.md §19.2's "Inhaltshash"). Sorted by path first so the result is
21
+ * independent of file-discovery order.
22
+ */
23
+ contentHash: string;
24
+ }
25
+ /** SHA-256 of an artifact's canonical configuration (spec.md §19.2). */
26
+ export declare function computeArtifactConfigHash(config: ContextArtifactConfig): string;
27
+ /**
28
+ * Combines the per-document `contentHash`es (already SHA-256 of each
29
+ * document's raw content, per `ArtifactDocument.contentHash`) into one
30
+ * artifact-level content hash. A directory-backed artifact is stale when
31
+ * any file's content changes, a file is added, or a file is removed — all
32
+ * three change this combined hash.
33
+ */
34
+ export declare function computeArtifactContentHash(documents: readonly ArtifactDocument[]): string;
35
+ /** Snapshots both staleness inputs for one artifact's current config + documents. */
36
+ export declare function computeArtifactStalenessSnapshot(config: ContextArtifactConfig, documents: readonly ArtifactDocument[]): ArtifactStalenessSnapshot;
37
+ /**
38
+ * True when `current` differs from the last-indexed `previous` snapshot in
39
+ * either the config hash or the content hash (spec.md §19.2) — a pure
40
+ * comparison; persisting/loading the previous snapshot is a metadata-store
41
+ * concern outside this package.
42
+ */
43
+ export declare function isArtifactStale(previous: ArtifactStalenessSnapshot, current: ArtifactStalenessSnapshot): boolean;
44
+ //# sourceMappingURL=staleness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staleness.d.ts","sourceRoot":"","sources":["../src/staleness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGrF,qFAAqF;AACrF,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wEAAwE;AACxE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAE/E;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CAKzF;AAED,qFAAqF;AACrF,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,qBAAqB,EAC7B,SAAS,EAAE,SAAS,gBAAgB,EAAE,GACrC,yBAAyB,CAK3B;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,yBAAyB,EACnC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAET"}
@@ -0,0 +1,35 @@
1
+ import { canonicalJson, sha256Hex } from "@untangle-code/domain";
2
+ /** SHA-256 of an artifact's canonical configuration (spec.md §19.2). */
3
+ export function computeArtifactConfigHash(config) {
4
+ return sha256Hex(canonicalJson(config));
5
+ }
6
+ /**
7
+ * Combines the per-document `contentHash`es (already SHA-256 of each
8
+ * document's raw content, per `ArtifactDocument.contentHash`) into one
9
+ * artifact-level content hash. A directory-backed artifact is stale when
10
+ * any file's content changes, a file is added, or a file is removed — all
11
+ * three change this combined hash.
12
+ */
13
+ export function computeArtifactContentHash(documents) {
14
+ const sorted = [...documents]
15
+ .map((document) => ({ path: document.path, contentHash: document.contentHash }))
16
+ .sort((a, b) => a.path.localeCompare(b.path));
17
+ return sha256Hex(canonicalJson(sorted));
18
+ }
19
+ /** Snapshots both staleness inputs for one artifact's current config + documents. */
20
+ export function computeArtifactStalenessSnapshot(config, documents) {
21
+ return {
22
+ configHash: computeArtifactConfigHash(config),
23
+ contentHash: computeArtifactContentHash(documents),
24
+ };
25
+ }
26
+ /**
27
+ * True when `current` differs from the last-indexed `previous` snapshot in
28
+ * either the config hash or the content hash (spec.md §19.2) — a pure
29
+ * comparison; persisting/loading the previous snapshot is a metadata-store
30
+ * concern outside this package.
31
+ */
32
+ export function isArtifactStale(previous, current) {
33
+ return previous.configHash !== current.configHash || previous.contentHash !== current.contentHash;
34
+ }
35
+ //# sourceMappingURL=staleness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staleness.js","sourceRoot":"","sources":["../src/staleness.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAcjE,wEAAwE;AACxE,MAAM,UAAU,yBAAyB,CAAC,MAA6B;IACrE,OAAO,SAAS,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CAAC,SAAsC;IAC/E,MAAM,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;SAC1B,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;SAC/E,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAChD,OAAO,SAAS,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,gCAAgC,CAC9C,MAA6B,EAC7B,SAAsC;IAEtC,OAAO;QACL,UAAU,EAAE,yBAAyB,CAAC,MAAM,CAAC;QAC7C,WAAW,EAAE,0BAA0B,CAAC,SAAS,CAAC;KACnD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAmC,EACnC,OAAkC;IAElC,OAAO,QAAQ,CAAC,UAAU,KAAK,OAAO,CAAC,UAAU,IAAI,QAAQ,CAAC,WAAW,KAAK,OAAO,CAAC,WAAW,CAAC;AACpG,CAAC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Plausibilitätsprüfung von Fragmentgrenzen (WP-118, BUILD_PLAN §12 E4).
3
+ *
4
+ * `coverage.ts` beantwortet die Frage „**fehlt** etwas?" — es rechnet
5
+ * Byte-Abdeckung und meldet jeden Bereich, der in keinem Chunk landet.
6
+ * Diese Frage ist blind gegenüber dem zweiten Fehlerbild: es fehlt nichts,
7
+ * aber es ist **falsch geschnitten**. Der Auslöser war die SQL-Zerlegung
8
+ * (`adapters/sql.ts`), die `INSERT INTO t VALUES ('it\'s a test; nested
9
+ * clause', 2);` in zwei syntaktisch kaputte Fragmente zerriss — bei
10
+ * lückenloser Byte-Abdeckung, ohne leere oder überlappende Ranges, also bei
11
+ * **null** Ledger-Einträgen. Der Verlust ist real (kein Fragment ist ein
12
+ * gültiges Statement, die Einbettung beider ist Müll), nur eben unsichtbar.
13
+ *
14
+ * Dieses Modul liefert das fehlende Signal, format-agnostisch und ohne
15
+ * Grammatik: ein korrekt geschnittenes Fragment einer klammer- und
16
+ * zeichenketten-strukturierten Sprache ist in sich **ausbalanciert**. Bleibt
17
+ * eine Zeichenkette, ein Blockkommentar, ein Heredoc oder eine Klammer am
18
+ * Fragmentende offen, dann verlief die Schnittkante mitten durch ein
19
+ * Konstrukt — unabhängig davon, welcher Adapter geschnitten hat und warum.
20
+ *
21
+ * Bewusste Asymmetrie zum jeweiligen Zerleger: dieser Prüfer darf
22
+ * **misstrauischer** sein als der Zerleger. Ein Fehlalarm kostet eine
23
+ * Warnzeile, ein verschwiegener Fehlschnitt kostet einen unbrauchbaren
24
+ * Chunk (E4). Deshalb kennt er z. B. Dollar-Quoting auch dort, wo ein
25
+ * Dialekt es nicht erwarten ließe.
26
+ *
27
+ * Was er NICHT ist: ein Parser. Er kennt keine Schlüsselwörter, keine
28
+ * Ausdrücke, keine Typen. Ein Fragment, das balanciert und trotzdem
29
+ * bedeutungslos ist (`FROM orders WHERE`), passiert ihn unbeanstandet. Das
30
+ * ist die dokumentierte Grenze — er schließt die Lücke „mitten durch ein
31
+ * Konstrukt geschnitten", nicht „semantisch unvollständig".
32
+ */
33
+ /** Eine Zeichenketten-/Bezeichner-Anführungskonvention (`'…'`, `"…"`, `` `…` ``). */
34
+ export interface QuoteRule {
35
+ /** Öffnende Zeichenfolge, meist ein einzelnes Zeichen. */
36
+ readonly open: string;
37
+ /** Schließende Zeichenfolge; bei symmetrischen Anführungszeichen gleich `open`. */
38
+ readonly close: string;
39
+ /** Ob ein `\` innerhalb des Literals das folgende Zeichen maskiert (dialektabhängig, siehe `adapters/sql.ts`). */
40
+ readonly backslashEscapes: boolean;
41
+ /** Ob ein verdoppeltes Schlusszeichen (`''`) im Literal ein maskiertes Anführungszeichen ist. */
42
+ readonly doubledEscapes: boolean;
43
+ /** Ob das Literal einen Zeilenumbruch enthalten darf. Ist es das nicht, ist ein Zeilenende im Literal selbst schon ein Befund. */
44
+ readonly spansLines: boolean;
45
+ }
46
+ /** Ein Klammerpaar, dessen Verschachtelung aufgehen muss. */
47
+ export interface BracketRule {
48
+ readonly open: string;
49
+ readonly close: string;
50
+ }
51
+ /** Ein Blockkommentar-Paar (`/*` … `*\/`). */
52
+ export interface BlockCommentRule {
53
+ readonly open: string;
54
+ readonly close: string;
55
+ }
56
+ /**
57
+ * Heredoc-Konvention. Bisher nur `"hcl"`: `<<TAG` bzw. `<<-TAG`, beendet
58
+ * durch eine Zeile, deren getrimmter Inhalt genau `TAG` ist. Der Inhalt
59
+ * eines Heredocs ist Rohtext — Klammern und Anführungszeichen darin zählen
60
+ * nicht, sonst meldet jedes eingebettete Shell-Skript einen Fehlalarm.
61
+ */
62
+ export type HeredocStyle = "hcl";
63
+ export interface DelimiterScanOptions {
64
+ readonly quotes: readonly QuoteRule[];
65
+ readonly brackets: readonly BracketRule[];
66
+ /** Zeilenkommentar-Präfixe (`"--"`, `"//"`, `"#"`), in Prüfreihenfolge. */
67
+ readonly lineComments: readonly string[];
68
+ readonly blockComment?: BlockCommentRule;
69
+ /**
70
+ * `$tag$ … $tag$`-Literale erkennen. Ein Öffner zählt nur, wenn davor
71
+ * kein Bezeichnerzeichen steht — sonst würde ein Bezeichner mit zwei
72
+ * `$` (`my$col$x`) fälschlich ein Literal öffnen.
73
+ */
74
+ readonly dollarQuotedStrings?: boolean;
75
+ readonly heredocs?: HeredocStyle;
76
+ }
77
+ export type DelimiterImbalanceKind = "unterminated_string" | "unterminated_block_comment" | "unterminated_heredoc" | "unclosed_bracket" | "unmatched_closing_bracket";
78
+ export interface DelimiterImbalance {
79
+ readonly kind: DelimiterImbalanceKind;
80
+ /** Das beteiligte Trennzeichen, wörtlich (`"'"`, `"("`, `"<<EOF"`). */
81
+ readonly delimiter: string;
82
+ /** 0-basierter Offset in der geprüften Zeichenkette (UTF-16-Einheiten), an dem das Konstrukt geöffnet wurde. */
83
+ readonly offset: number;
84
+ /** 1-basierte Zeile INNERHALB des geprüften Fragments (nicht des Dokuments). */
85
+ readonly line: number;
86
+ }
87
+ /**
88
+ * Findet jede Stelle, an der `text` mitten durch ein Konstrukt geschnitten
89
+ * ist. Ergebnis ist deterministisch nach Offset sortiert (numerisch, kein
90
+ * `localeCompare`) und auf {@link MAX_IMBALANCES} Einträge begrenzt.
91
+ */
92
+ export declare function findDelimiterImbalances(text: string, options: DelimiterScanOptions): DelimiterImbalance[];
93
+ /** Einzeilige, menschenlesbare Zusammenfassung für den `contentPreview` eines Ledger-Eintrags. */
94
+ export declare function describeDelimiterImbalances(imbalances: readonly DelimiterImbalance[]): string;
95
+ //# sourceMappingURL=delimiter-balance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delimiter-balance.d.ts","sourceRoot":"","sources":["../../src/text/delimiter-balance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,qFAAqF;AACrF,MAAM,WAAW,SAAS;IACxB,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kHAAkH;IAClH,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,iGAAiG;IACjG,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,kIAAkI;IAClI,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,6DAA6D;AAC7D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC;AAEjC,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,2EAA2E;IAC3E,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,YAAY,CAAC,EAAE,gBAAgB,CAAC;IACzC;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;CAClC;AAED,MAAM,MAAM,sBAAsB,GAC9B,qBAAqB,GACrB,4BAA4B,GAC5B,sBAAsB,GACtB,kBAAkB,GAClB,2BAA2B,CAAC;AAEhC,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACtC,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,gHAAgH;IAChH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAmFD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,kBAAkB,EAAE,CAyFtB;AAUD,kGAAkG;AAClG,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,GAAG,MAAM,CAM7F"}
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Plausibilitätsprüfung von Fragmentgrenzen (WP-118, BUILD_PLAN §12 E4).
3
+ *
4
+ * `coverage.ts` beantwortet die Frage „**fehlt** etwas?" — es rechnet
5
+ * Byte-Abdeckung und meldet jeden Bereich, der in keinem Chunk landet.
6
+ * Diese Frage ist blind gegenüber dem zweiten Fehlerbild: es fehlt nichts,
7
+ * aber es ist **falsch geschnitten**. Der Auslöser war die SQL-Zerlegung
8
+ * (`adapters/sql.ts`), die `INSERT INTO t VALUES ('it\'s a test; nested
9
+ * clause', 2);` in zwei syntaktisch kaputte Fragmente zerriss — bei
10
+ * lückenloser Byte-Abdeckung, ohne leere oder überlappende Ranges, also bei
11
+ * **null** Ledger-Einträgen. Der Verlust ist real (kein Fragment ist ein
12
+ * gültiges Statement, die Einbettung beider ist Müll), nur eben unsichtbar.
13
+ *
14
+ * Dieses Modul liefert das fehlende Signal, format-agnostisch und ohne
15
+ * Grammatik: ein korrekt geschnittenes Fragment einer klammer- und
16
+ * zeichenketten-strukturierten Sprache ist in sich **ausbalanciert**. Bleibt
17
+ * eine Zeichenkette, ein Blockkommentar, ein Heredoc oder eine Klammer am
18
+ * Fragmentende offen, dann verlief die Schnittkante mitten durch ein
19
+ * Konstrukt — unabhängig davon, welcher Adapter geschnitten hat und warum.
20
+ *
21
+ * Bewusste Asymmetrie zum jeweiligen Zerleger: dieser Prüfer darf
22
+ * **misstrauischer** sein als der Zerleger. Ein Fehlalarm kostet eine
23
+ * Warnzeile, ein verschwiegener Fehlschnitt kostet einen unbrauchbaren
24
+ * Chunk (E4). Deshalb kennt er z. B. Dollar-Quoting auch dort, wo ein
25
+ * Dialekt es nicht erwarten ließe.
26
+ *
27
+ * Was er NICHT ist: ein Parser. Er kennt keine Schlüsselwörter, keine
28
+ * Ausdrücke, keine Typen. Ein Fragment, das balanciert und trotzdem
29
+ * bedeutungslos ist (`FROM orders WHERE`), passiert ihn unbeanstandet. Das
30
+ * ist die dokumentierte Grenze — er schließt die Lücke „mitten durch ein
31
+ * Konstrukt geschnitten", nicht „semantisch unvollständig".
32
+ */
33
+ const IDENTIFIER_CHARACTER = /[A-Za-z0-9_]/;
34
+ const IDENTIFIER_START = /[A-Za-z_]/;
35
+ const HEREDOC_OPEN = /<<-?([A-Za-z_][A-Za-z0-9_]*)/y;
36
+ /** Höchstzahl gemeldeter Befunde je Fragment — ein durchgehend kaputtes Fragment soll den Ledger nicht fluten. */
37
+ const MAX_IMBALANCES = 8;
38
+ function lineOf(text, offset) {
39
+ let line = 1;
40
+ for (let i = 0; i < offset && i < text.length; i += 1) {
41
+ if (text[i] === "\n")
42
+ line += 1;
43
+ }
44
+ return line;
45
+ }
46
+ /**
47
+ * `$tag$`-Öffner an Position `i`, oder `undefined`. Der Tag darf leer sein
48
+ * (`$$`); ein vorangehendes Bezeichnerzeichen verhindert die Erkennung.
49
+ */
50
+ function matchDollarQuote(text, i) {
51
+ if (text[i] !== "$")
52
+ return undefined;
53
+ const before = i > 0 ? text[i - 1] : "";
54
+ if (before !== "" && (IDENTIFIER_CHARACTER.test(before) || before === "$"))
55
+ return undefined;
56
+ let j = i + 1;
57
+ if (j < text.length && IDENTIFIER_START.test(text[j])) {
58
+ j += 1;
59
+ while (j < text.length && IDENTIFIER_CHARACTER.test(text[j]))
60
+ j += 1;
61
+ }
62
+ if (text[j] !== "$")
63
+ return undefined;
64
+ return text.slice(i, j + 1);
65
+ }
66
+ /** Überliest ein Literal ab dem Öffner an `start`. */
67
+ function scanQuoted(text, start, rule) {
68
+ let i = start + rule.open.length;
69
+ while (i < text.length) {
70
+ if (!rule.spansLines && text[i] === "\n")
71
+ return { next: i, terminated: false };
72
+ if (rule.backslashEscapes && text[i] === "\\") {
73
+ i += 2;
74
+ continue;
75
+ }
76
+ if (text.startsWith(rule.close, i)) {
77
+ if (rule.doubledEscapes && text.startsWith(rule.close, i + rule.close.length)) {
78
+ i += rule.close.length * 2;
79
+ continue;
80
+ }
81
+ return { next: i + rule.close.length, terminated: true };
82
+ }
83
+ i += 1;
84
+ }
85
+ return { next: text.length, terminated: false };
86
+ }
87
+ /** Überliest ein `$tag$`-Literal ab dem Öffner an `start`. */
88
+ function scanDollarQuoted(text, start, tag) {
89
+ const close = text.indexOf(tag, start + tag.length);
90
+ if (close === -1)
91
+ return { next: text.length, terminated: false };
92
+ return { next: close + tag.length, terminated: true };
93
+ }
94
+ /** Überliest ein Heredoc ab dem `<<TAG`-Öffner an `start`. */
95
+ function scanHeredoc(text, start, opener, tag) {
96
+ let lineStart = text.indexOf("\n", start + opener.length);
97
+ if (lineStart === -1)
98
+ return { next: text.length, terminated: false };
99
+ lineStart += 1;
100
+ while (lineStart <= text.length) {
101
+ const newline = text.indexOf("\n", lineStart);
102
+ const lineEnd = newline === -1 ? text.length : newline;
103
+ if (text.slice(lineStart, lineEnd).trim() === tag)
104
+ return { next: lineEnd, terminated: true };
105
+ if (newline === -1)
106
+ break;
107
+ lineStart = newline + 1;
108
+ }
109
+ return { next: text.length, terminated: false };
110
+ }
111
+ /**
112
+ * Findet jede Stelle, an der `text` mitten durch ein Konstrukt geschnitten
113
+ * ist. Ergebnis ist deterministisch nach Offset sortiert (numerisch, kein
114
+ * `localeCompare`) und auf {@link MAX_IMBALANCES} Einträge begrenzt.
115
+ */
116
+ export function findDelimiterImbalances(text, options) {
117
+ const found = [];
118
+ const openBrackets = [];
119
+ const { blockComment } = options;
120
+ let i = 0;
121
+ while (i < text.length) {
122
+ if (blockComment !== undefined && text.startsWith(blockComment.open, i)) {
123
+ const close = text.indexOf(blockComment.close, i + blockComment.open.length);
124
+ if (close === -1) {
125
+ found.push({ kind: "unterminated_block_comment", delimiter: blockComment.open, offset: i });
126
+ i = text.length;
127
+ break;
128
+ }
129
+ i = close + blockComment.close.length;
130
+ continue;
131
+ }
132
+ const lineComment = options.lineComments.find((prefix) => text.startsWith(prefix, i));
133
+ if (lineComment !== undefined) {
134
+ const newline = text.indexOf("\n", i);
135
+ i = newline === -1 ? text.length : newline + 1;
136
+ continue;
137
+ }
138
+ if (options.heredocs === "hcl" && text.startsWith("<<", i)) {
139
+ HEREDOC_OPEN.lastIndex = i;
140
+ const match = HEREDOC_OPEN.exec(text);
141
+ if (match !== null) {
142
+ const result = scanHeredoc(text, i, match[0], match[1]);
143
+ if (!result.terminated)
144
+ found.push({ kind: "unterminated_heredoc", delimiter: match[0], offset: i });
145
+ i = result.next;
146
+ continue;
147
+ }
148
+ }
149
+ if (options.dollarQuotedStrings === true) {
150
+ const tag = matchDollarQuote(text, i);
151
+ if (tag !== undefined) {
152
+ const result = scanDollarQuoted(text, i, tag);
153
+ if (!result.terminated)
154
+ found.push({ kind: "unterminated_string", delimiter: tag, offset: i });
155
+ i = result.next;
156
+ continue;
157
+ }
158
+ }
159
+ const quote = options.quotes.find((rule) => text.startsWith(rule.open, i));
160
+ if (quote !== undefined) {
161
+ const result = scanQuoted(text, i, quote);
162
+ if (!result.terminated)
163
+ found.push({ kind: "unterminated_string", delimiter: quote.open, offset: i });
164
+ i = Math.max(result.next, i + 1);
165
+ continue;
166
+ }
167
+ const opening = options.brackets.find((rule) => text.startsWith(rule.open, i));
168
+ if (opening !== undefined) {
169
+ openBrackets.push({ delimiter: opening.open, close: opening.close, offset: i });
170
+ i += opening.open.length;
171
+ continue;
172
+ }
173
+ const closing = options.brackets.find((rule) => text.startsWith(rule.close, i));
174
+ if (closing !== undefined) {
175
+ const top = openBrackets.at(-1);
176
+ if (top !== undefined && top.close === closing.close) {
177
+ openBrackets.pop();
178
+ }
179
+ else {
180
+ // Ein Schlusszeichen ohne passenden Öffner IM FRAGMENT: die
181
+ // Schnittkante lag hinter dem zugehörigen Öffner.
182
+ found.push({ kind: "unmatched_closing_bracket", delimiter: closing.close, offset: i });
183
+ }
184
+ i += closing.close.length;
185
+ continue;
186
+ }
187
+ i += 1;
188
+ }
189
+ for (const open of openBrackets) {
190
+ found.push({ kind: "unclosed_bracket", delimiter: open.delimiter, offset: open.offset });
191
+ }
192
+ return found
193
+ .sort((a, b) => a.offset - b.offset)
194
+ .slice(0, MAX_IMBALANCES)
195
+ .map((entry) => ({ ...entry, line: lineOf(text, entry.offset) }));
196
+ }
197
+ const IMBALANCE_LABELS = {
198
+ unterminated_string: "unterminated string literal opened by",
199
+ unterminated_block_comment: "unterminated block comment opened by",
200
+ unterminated_heredoc: "unterminated heredoc opened by",
201
+ unclosed_bracket: "unclosed bracket",
202
+ unmatched_closing_bracket: "closing bracket without a matching opener in this fragment:",
203
+ };
204
+ /** Einzeilige, menschenlesbare Zusammenfassung für den `contentPreview` eines Ledger-Eintrags. */
205
+ export function describeDelimiterImbalances(imbalances) {
206
+ const parts = imbalances.map((entry) => `${IMBALANCE_LABELS[entry.kind]} ${JSON.stringify(entry.delimiter)} at fragment line ${entry.line}`);
207
+ return `implausible fragment boundary: ${parts.join("; ")}`;
208
+ }
209
+ //# sourceMappingURL=delimiter-balance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delimiter-balance.js","sourceRoot":"","sources":["../../src/text/delimiter-balance.ts"],"names":[],"mappings":"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"}