kibi-cli 2.26.2 → 2.26.3
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/commands/sync/cache.d.ts +21 -0
- package/dist/commands/sync/cache.d.ts.map +1 -1
- package/dist/commands/sync/cache.js +40 -15
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +21 -10
- package/dist/operations/mutation/requirement-semantics.d.ts +5 -0
- package/dist/operations/mutation/requirement-semantics.d.ts.map +1 -1
- package/dist/operations/mutation/requirement-semantics.js +24 -5
- package/dist/operations/mutation/stored-entity.d.ts +7 -0
- package/dist/operations/mutation/stored-entity.d.ts.map +1 -1
- package/dist/operations/mutation/uncompiled-source.d.ts +6 -0
- package/dist/operations/mutation/uncompiled-source.d.ts.map +1 -1
- package/dist/operations/mutation/uncompiled-source.js +17 -1
- package/dist/prolog/utf8-transport.d.ts +21 -0
- package/dist/prolog/utf8-transport.d.ts.map +1 -0
- package/dist/prolog/utf8-transport.js +49 -0
- package/dist/prolog.d.ts +2 -0
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +19 -10
- package/package.json +1 -1
|
@@ -41,7 +41,28 @@ export type SourceExtractionFailure = {
|
|
|
41
41
|
* new authoring.
|
|
42
42
|
*/
|
|
43
43
|
baseline?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The semantic source hash and contract flag of the source's last
|
|
46
|
+
* successful compilation, carried while it fails.
|
|
47
|
+
*/
|
|
48
|
+
semanticHash?: string;
|
|
49
|
+
semanticContract?: boolean;
|
|
44
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* Whether a requirement source the last sync could not extract is still
|
|
53
|
+
* accepted without a complete proposition inventory once it compiles with
|
|
54
|
+
* prose hashing to `sourceHash`. Legacy content that missed the store's first
|
|
55
|
+
* compilation keeps the baseline; a source that compiled before keeps it only
|
|
56
|
+
* while its prose is the prose it last compiled with. kibi sync and kb_upsert
|
|
57
|
+
* on the uncompiled file both apply this rule, so a write one accepts the
|
|
58
|
+
* other never refuses.
|
|
59
|
+
*/
|
|
60
|
+
export declare function failedSourceKeepsSemanticBaseline(failure: SourceExtractionFailure, sourceHash: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* What the last sync recorded about an authored source it could not extract,
|
|
63
|
+
* the record the next sync judges the source by; undefined when it compiled.
|
|
64
|
+
*/
|
|
65
|
+
export declare function sourceExtractionFailure(workspaceRoot: string, storePath: string, file: string, deps?: Partial<SyncCacheDeps>): SourceExtractionFailure | undefined;
|
|
45
66
|
/**
|
|
46
67
|
* v2: cache identities are workspace-root relative instead of process-cwd
|
|
47
68
|
* relative, and symbol manifests are fingerprinted together with their
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/cache.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAO7E,UAAU,aAAa;IACrB,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,aAAa,EAAE,OAAO,aAAa,CAAC;CACrC;AAaD,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,iEAAiE;IACjE,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3C,kEAAkE;IAClE,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;CAC9D,CAAC;AAEF,2DAA2D;AAE3D,MAAM,MAAM,uBAAuB,GAAG;IACpC,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,6 +32,45 @@ function resolveDeps(overrides) {
|
|
|
32
32
|
...overrides,
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
+
// implements REQ-kibi-uncompiled-source-semantic-baseline
|
|
36
|
+
/**
|
|
37
|
+
* Whether a requirement source the last sync could not extract is still
|
|
38
|
+
* accepted without a complete proposition inventory once it compiles with
|
|
39
|
+
* prose hashing to `sourceHash`. Legacy content that missed the store's first
|
|
40
|
+
* compilation keeps the baseline; a source that compiled before keeps it only
|
|
41
|
+
* while its prose is the prose it last compiled with. kibi sync and kb_upsert
|
|
42
|
+
* on the uncompiled file both apply this rule, so a write one accepts the
|
|
43
|
+
* other never refuses.
|
|
44
|
+
*/
|
|
45
|
+
export function failedSourceKeepsSemanticBaseline(failure, sourceHash) {
|
|
46
|
+
if (failure.semanticContract === true)
|
|
47
|
+
return false;
|
|
48
|
+
return failure.semanticHash === undefined
|
|
49
|
+
? failure.baseline === true
|
|
50
|
+
: failure.semanticHash === sourceHash;
|
|
51
|
+
}
|
|
52
|
+
function readExtractionFailures(cachePath, resolved) {
|
|
53
|
+
if (!resolved.existsSync(cachePath))
|
|
54
|
+
return {};
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(resolved.readFileSync(cachePath, "utf8"));
|
|
57
|
+
if (parsed.extractionFailures === null ||
|
|
58
|
+
typeof parsed.extractionFailures !== "object")
|
|
59
|
+
return {};
|
|
60
|
+
return parsed.extractionFailures;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* What the last sync recorded about an authored source it could not extract,
|
|
68
|
+
* the record the next sync judges the source by; undefined when it compiled.
|
|
69
|
+
*/
|
|
70
|
+
// implements REQ-kibi-uncompiled-source-semantic-baseline
|
|
71
|
+
export function sourceExtractionFailure(workspaceRoot, storePath, file, deps) {
|
|
72
|
+
return readExtractionFailures(path.join(storePath, "sync-cache.json"), resolveDeps(deps))[toCacheKey(workspaceRoot, path.resolve(workspaceRoot, file))];
|
|
73
|
+
}
|
|
35
74
|
/**
|
|
36
75
|
* v2: cache identities are workspace-root relative instead of process-cwd
|
|
37
76
|
* relative, and symbol manifests are fingerprinted together with their
|
|
@@ -93,21 +132,7 @@ export const SOURCE_EXTRACTION_FAILED = "source_extraction_failed";
|
|
|
93
132
|
*/
|
|
94
133
|
// implements REQ-kibi-uncompiled-source-visibility
|
|
95
134
|
export function sourceExtractionFailureReasons(workspaceRoot, storePath, deps) {
|
|
96
|
-
const
|
|
97
|
-
const cachePath = path.join(storePath, "sync-cache.json");
|
|
98
|
-
if (!resolved.existsSync(cachePath))
|
|
99
|
-
return [];
|
|
100
|
-
let failures;
|
|
101
|
-
try {
|
|
102
|
-
const parsed = JSON.parse(resolved.readFileSync(cachePath, "utf8"));
|
|
103
|
-
if (parsed.extractionFailures === null ||
|
|
104
|
-
typeof parsed.extractionFailures !== "object")
|
|
105
|
-
return [];
|
|
106
|
-
failures = parsed.extractionFailures;
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
return [];
|
|
110
|
-
}
|
|
135
|
+
const failures = readExtractionFailures(path.join(storePath, "sync-cache.json"), resolveDeps(deps));
|
|
111
136
|
const reasons = [];
|
|
112
137
|
for (const [key, failure] of Object.entries(failures).sort(([a], [b]) => a.localeCompare(b))) {
|
|
113
138
|
const absolute = path.resolve(workspaceRoot, key);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA6BjE,OAAO,EAEL,yBAAyB,EAE1B,MAAM,gDAAgD,CAAC;AAGxD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EACL,KAAK,gBAAgB,EAGtB,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA6BjE,OAAO,EAEL,yBAAyB,EAE1B,MAAM,gDAAgD,CAAC;AAGxD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EACL,KAAK,gBAAgB,EAGtB,MAAM,6BAA6B,CAAC;AA0BrC,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAkCxE,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AAmCD,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,EACrB,gBAAgB,EAAE,MAAM,EACxB,4BAA4B,UAAQ,EACpC,4BAA4B,GAAE,4BAA4B,EAAO,GAChE,MAAM,EAAE,CAuGV;AAiFD,MAAM,WAAW,UAAW,SAAQ,WAAW;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,yBAAyB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,kBAAkB;IAC1B,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,yBAAyB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3E,UAAU,CAAC,EAAE,CACX,OAAO,EAAE,yBAAyB,GAAG;QAAE,UAAU,EAAE,OAAO,CAAA;KAAE,KACzD,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,aAAa,CAAC;IAC/D,yBAAyB,CAAC,EAAE,OAAO,yBAAyB,CAAC;CAC9D;AA+FD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAGvE;AAGD,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,IAAI,GAChB,IAAI,CAMN;AAED,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,UAAU,EAAE,EACzB,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,IAAI,CAIN;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,IAAI,CAMP;AAED,wBAAgB,iCAAiC,CAC/C,WAAW,EAAE,UAAU,EAAE,EACzB,oBAAoB,EAAE,OAAO,EAC7B,iBAAiB,EAAE,MAAM,EACzB,WAAW,EAAE,MAAM,GAClB,IAAI,CAUN;AAGD,wBAAsB,WAAW,CAC/B,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAC;CACZ,EACN,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,UAAU,CAAC,CAuuCrB;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/commands/sync.js
CHANGED
|
@@ -30,7 +30,7 @@ import { PrologProcess } from "../prolog.js";
|
|
|
30
30
|
import { detachedHeadWriteRefusal, resolveBranchAttachment, } from "../utils/branch-resolver.js";
|
|
31
31
|
import { branchStoreManifestPath, branchStorePath, ensureBranchStoreManifest, expectedBranchStoreManifest, } from "../utils/branch-store-locator.js";
|
|
32
32
|
import { loadEntityPaths } from "../utils/config.js";
|
|
33
|
-
import { SYNC_CACHE_TTL_MS, SYNC_CACHE_VERSION, SYNC_COMPILER_FINGERPRINT, hashFile, hashManifestWithCoordinates, hashNormalized, readSyncCache, toCacheKey, writeSyncCache, } from "./sync/cache.js";
|
|
33
|
+
import { SYNC_CACHE_TTL_MS, SYNC_CACHE_VERSION, SYNC_COMPILER_FINGERPRINT, failedSourceKeepsSemanticBaseline, hashFile, hashManifestWithCoordinates, hashNormalized, readSyncCache, toCacheKey, writeSyncCache, } from "./sync/cache.js";
|
|
34
34
|
import { clearRecoveredPendingSourceReceipts, discoverSourceFiles, } from "./sync/discovery.js";
|
|
35
35
|
import { normalizeExtractionSources, processExtractions, } from "./sync/extraction.js";
|
|
36
36
|
import { normalizeManifestLayout, refreshManifestCoordinates, } from "./sync/manifest.js";
|
|
@@ -649,6 +649,8 @@ export async function syncCommand(options = {}, runtime = {}) {
|
|
|
649
649
|
// kb_status can say so. A source that failed in the store's first
|
|
650
650
|
// compilation (and in every sync since) is legacy content that missed the
|
|
651
651
|
// semantic baseline, so it keeps the baseline treatment once it compiles.
|
|
652
|
+
// One that compiled before keeps the semantic hash it last compiled with,
|
|
653
|
+
// so restoring its prose (or fixing only what failed) is not new authoring.
|
|
652
654
|
const previousExtractionFailures = syncCache.extractionFailures ?? {};
|
|
653
655
|
const nextExtractionFailures = {};
|
|
654
656
|
for (const failure of extraction.failures) {
|
|
@@ -656,13 +658,17 @@ export async function syncCommand(options = {}, runtime = {}) {
|
|
|
656
658
|
const hash = nextHashes[key];
|
|
657
659
|
if (hash === undefined)
|
|
658
660
|
continue;
|
|
661
|
+
const previousFailure = previousExtractionFailures[key];
|
|
662
|
+
const semanticHash = syncCache.semanticHashes[key] ?? previousFailure?.semanticHash;
|
|
663
|
+
const semanticContract = syncCache.semanticContracts[key] ?? previousFailure?.semanticContract;
|
|
659
664
|
nextExtractionFailures[key] = {
|
|
660
665
|
hash,
|
|
661
666
|
message: failure.message,
|
|
662
|
-
...(initialSemanticBaseline ||
|
|
663
|
-
previousExtractionFailures[key]?.baseline === true
|
|
667
|
+
...(initialSemanticBaseline || previousFailure?.baseline === true
|
|
664
668
|
? { baseline: true }
|
|
665
669
|
: {}),
|
|
670
|
+
...(semanticHash !== undefined ? { semanticHash } : {}),
|
|
671
|
+
...(semanticContract !== undefined ? { semanticContract } : {}),
|
|
666
672
|
};
|
|
667
673
|
}
|
|
668
674
|
const extractedResults = normalizeExtractionSources(extraction.results, workspaceRoot);
|
|
@@ -760,13 +766,18 @@ export async function syncCommand(options = {}, runtime = {}) {
|
|
|
760
766
|
const advertisedContract = result.entity.semantic_inventory_version !== undefined ||
|
|
761
767
|
result.entity.semantic_source_hash !== undefined ||
|
|
762
768
|
result.entity.semantic_inventory !== undefined;
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
769
|
+
// A source the last sync could not extract is judged by what it last
|
|
770
|
+
// compiled with, by the rule kb_upsert applies to that file.
|
|
771
|
+
const previousFailure = syncCache.hashes[key] === undefined
|
|
772
|
+
? previousExtractionFailures[key]
|
|
773
|
+
: undefined;
|
|
774
|
+
const enforceBoundary = advertisedContract ||
|
|
775
|
+
(previousFailure !== undefined
|
|
776
|
+
? !failedSourceKeepsSemanticBaseline(previousFailure, boundary.sourceHash)
|
|
777
|
+
: (!initialSemanticBaseline && syncCache.hashes[key] === undefined) ||
|
|
778
|
+
syncCache.semanticContracts[key] === true ||
|
|
779
|
+
(previousSemanticHash !== undefined &&
|
|
780
|
+
previousSemanticHash !== boundary.sourceHash));
|
|
770
781
|
if (enforceBoundary && boundary.errors.length > 0) {
|
|
771
782
|
inventoryFailures.push(`${key}: proposition-complete ingestion failed: ${boundary.errors.join("; ")}.`);
|
|
772
783
|
if (!advertisedContract)
|
|
@@ -29,6 +29,11 @@ export declare function withStoredRequirementSemantics(input: UpsertInput, prolo
|
|
|
29
29
|
* inventory is legacy content sync keeps accepting, so a write that changes
|
|
30
30
|
* neither keeps working. A write that makes the requirement current, or
|
|
31
31
|
* changes the prose sync checks, must carry the complete inventory.
|
|
32
|
+
*
|
|
33
|
+
* A requirement the store does not hold (its file failed the last sync, or
|
|
34
|
+
* was written since) is judged by the rule sync applies to that file: it
|
|
35
|
+
* keeps the legacy treatment only when the last sync recorded the file as
|
|
36
|
+
* legacy content or as having compiled with the same prose.
|
|
32
37
|
*/
|
|
33
38
|
export declare function assertStoredRequirementBoundary(input: UpsertInput, relationships: readonly SemanticRelationship[], prolog: Pick<PrologPort, "query">, staged?: StagedUpsertState, lookup?: StoredEntityLookup): Promise<void>;
|
|
34
39
|
//# sourceMappingURL=requirement-semantics.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requirement-semantics.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/requirement-semantics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAE3E,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,2CAA2C,CAAC;AAEnD,OAAO,EACL,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAwCjE;;;;;;;;;;;;GAYG;AACH,wBAAsB,8BAA8B,CAClD,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EACjC,MAAM,CAAC,EAAE,iBAAiB,EAC1B,MAAM,GAAE,kBAAqE,GAC5E,OAAO,CAAC,WAAW,CAAC,CAsCtB;
|
|
1
|
+
{"version":3,"file":"requirement-semantics.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/requirement-semantics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAE3E,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,2CAA2C,CAAC;AAEnD,OAAO,EACL,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAwCjE;;;;;;;;;;;;GAYG;AACH,wBAAsB,8BAA8B,CAClD,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EACjC,MAAM,CAAC,EAAE,iBAAiB,EAC1B,MAAM,GAAE,kBAAqE,GAC5E,OAAO,CAAC,WAAW,CAAC,CAsCtB;AAkCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,+BAA+B,CACnD,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EACjC,MAAM,CAAC,EAAE,iBAAiB,EAC1B,MAAM,GAAE,kBAAqE,GAC5E,OAAO,CAAC,IAAI,CAAC,CAwBf"}
|
|
@@ -82,6 +82,12 @@ const BOUNDARY_FIELDS = [
|
|
|
82
82
|
"text_ref",
|
|
83
83
|
...SEMANTIC_CONTRACT_FIELDS,
|
|
84
84
|
];
|
|
85
|
+
/** Whether a requirement states an inventory contract sync always checks. */
|
|
86
|
+
function advertisesSemanticContract(properties) {
|
|
87
|
+
return (properties.semantic_inventory_version !== undefined ||
|
|
88
|
+
properties.semantic_source_hash !== undefined ||
|
|
89
|
+
properties.semantic_inventory !== undefined);
|
|
90
|
+
}
|
|
85
91
|
function boundaryOf(input, properties, relationships) {
|
|
86
92
|
const payload = { type: input.type, id: input.id, properties };
|
|
87
93
|
const { receipt } = analyzeSemanticAdvisorInput({
|
|
@@ -89,7 +95,7 @@ function boundaryOf(input, properties, relationships) {
|
|
|
89
95
|
});
|
|
90
96
|
return validateSemanticInventoryBoundary(payload, relationships, receipt);
|
|
91
97
|
}
|
|
92
|
-
// implements REQ-kibi-upsert-preserves-requirement-semantics, REQ-kibi-proposition-complete-ingestion
|
|
98
|
+
// implements REQ-kibi-upsert-preserves-requirement-semantics, REQ-kibi-proposition-complete-ingestion, REQ-kibi-uncompiled-source-semantic-baseline
|
|
93
99
|
/**
|
|
94
100
|
* An update to a stored requirement is judged as its authored file will read
|
|
95
101
|
* afterwards: the stored status, prose and proposition ledger with the
|
|
@@ -103,6 +109,11 @@ function boundaryOf(input, properties, relationships) {
|
|
|
103
109
|
* inventory is legacy content sync keeps accepting, so a write that changes
|
|
104
110
|
* neither keeps working. A write that makes the requirement current, or
|
|
105
111
|
* changes the prose sync checks, must carry the complete inventory.
|
|
112
|
+
*
|
|
113
|
+
* A requirement the store does not hold (its file failed the last sync, or
|
|
114
|
+
* was written since) is judged by the rule sync applies to that file: it
|
|
115
|
+
* keeps the legacy treatment only when the last sync recorded the file as
|
|
116
|
+
* legacy content or as having compiled with the same prose.
|
|
106
117
|
*/
|
|
107
118
|
export async function assertStoredRequirementBoundary(input, relationships, prolog, staged, lookup = storedEntityLookup(prolog, input.id, input.type)) {
|
|
108
119
|
if (input.type !== "req")
|
|
@@ -115,11 +126,19 @@ export async function assertStoredRequirementBoundary(input, relationships, prol
|
|
|
115
126
|
if (stored[field] !== undefined)
|
|
116
127
|
before[field] = stored[field];
|
|
117
128
|
}
|
|
118
|
-
const
|
|
129
|
+
const written = { ...before, ...(input.properties ?? {}) };
|
|
130
|
+
const after = boundaryOf(input, written, relationships);
|
|
119
131
|
if (after.errors.length === 0)
|
|
120
132
|
return;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
133
|
+
if (lookup.keepsSemanticBaseline !== undefined) {
|
|
134
|
+
if (!advertisesSemanticContract(written) &&
|
|
135
|
+
lookup.keepsSemanticBaseline(after.sourceHash))
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const previous = boundaryOf(input, before, relationships);
|
|
140
|
+
if (previous.applicable && previous.sourceHash === after.sourceHash)
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
124
143
|
throw new Error(`Proposition-complete ingestion failed: ${input.id} would be written as a current requirement without a complete proposition inventory (${after.errors.join("; ")}). Run kb_model mode analyze (CLI: semantic-advisor) with id '${input.id}' and its complete semantic_text, then send the returned inventory contract (semantic_inventory, semantic_inventory_version, semantic_source_field, semantic_source_hash, logic_claims) in this kb_upsert, or retire the requirement through a superseding one instead.`);
|
|
125
144
|
}
|
|
@@ -14,6 +14,13 @@ export type StoredEntityLookup = {
|
|
|
14
14
|
* rather than the store (an entity the store does not hold yet).
|
|
15
15
|
*/
|
|
16
16
|
readonly relationships?: readonly Readonly<Record<string, unknown>>[];
|
|
17
|
+
/**
|
|
18
|
+
* For an entity the store does not hold: whether kibi sync will accept its
|
|
19
|
+
* requirement prose (hashing to the given semantic source hash) without a
|
|
20
|
+
* complete inventory. Absent for a stored entity, which is judged by its
|
|
21
|
+
* stored prose instead.
|
|
22
|
+
*/
|
|
23
|
+
readonly keepsSemanticBaseline?: (sourceHash: string) => boolean;
|
|
17
24
|
};
|
|
18
25
|
/**
|
|
19
26
|
* One read of the entity an upsert replaces, shared by every step of the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stored-entity.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/stored-entity.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAE3E;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAGhF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7C;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"stored-entity.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/stored-entity.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAE3E;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAGhF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7C;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IACtE;;;;;OAKG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;CAClE,CAAC;AAGF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EACjC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GACX,kBAAkB,CAsBpB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SourceExtractionFailure } from "../../commands/sync/cache.js";
|
|
1
2
|
import type { OperationContext } from "../../public/operations/runtime-types.js";
|
|
2
3
|
import type { StoredEntityLookup } from "./stored-entity.js";
|
|
3
4
|
import type { RelationshipInput, UpsertInput } from "./types.js";
|
|
@@ -17,6 +18,11 @@ export type UncompiledAuthoredSource = Readonly<{
|
|
|
17
18
|
stored: Readonly<Record<string, unknown>>;
|
|
18
19
|
/** Why kibi sync cannot compile the file as authored; null when it can. */
|
|
19
20
|
extractionError: string | null;
|
|
21
|
+
/**
|
|
22
|
+
* What the last sync recorded when it could not extract the file; undefined
|
|
23
|
+
* when no sync has failed on it (the file was written since).
|
|
24
|
+
*/
|
|
25
|
+
syncFailure: SourceExtractionFailure | undefined;
|
|
20
26
|
}>;
|
|
21
27
|
/**
|
|
22
28
|
* The authored document an upsert of `input` would merge into although the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uncompiled-source.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/uncompiled-source.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"uncompiled-source.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/uncompiled-source.ts"],"names":[],"mappings":"AAmBA,OAAO,EACL,KAAK,uBAAuB,EAI7B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAQjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEjE;;;GAGG;AAEH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1C,2EAA2E;IAC3E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;OAGG;IACH,WAAW,EAAE,uBAAuB,GAAG,SAAS,CAAC;CAClD,CAAC,CAAC;AAqBH;;;;;;;GAOG;AAEH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAgD1C;AAED;;;;GAIG;AAEH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,wBAAwB,GAC/B,kBAAkB,CAWpB;AAED;;;;;GAKG;AAEH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,wBAAwB,EAChC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,SAAS,iBAAiB,EAAE,CAa9B"}
|
|
@@ -16,9 +16,18 @@
|
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
import path from "node:path";
|
|
19
|
-
import { sourceExtractionRemediation } from "../../commands/sync/cache.js";
|
|
19
|
+
import { failedSourceKeepsSemanticBaseline, sourceExtractionFailure, sourceExtractionRemediation, } from "../../commands/sync/cache.js";
|
|
20
20
|
import { extractFromMarkdownString } from "../../extractors/markdown.js";
|
|
21
|
+
import { resolveBranchAttachment } from "../../utils/branch-resolver.js";
|
|
22
|
+
import { branchStorePath } from "../../utils/branch-store-locator.js";
|
|
21
23
|
import { authoredFrontmatter, newEntitySourceTarget, renderSourceDocument, } from "./source-authoring.js";
|
|
24
|
+
// implements REQ-kibi-uncompiled-source-semantic-baseline
|
|
25
|
+
function lastSyncFailure(context, relative) {
|
|
26
|
+
const attachment = context.branchAttachment ?? resolveBranchAttachment(context.workspaceRoot);
|
|
27
|
+
if ("error" in attachment)
|
|
28
|
+
return undefined;
|
|
29
|
+
return sourceExtractionFailure(context.workspaceRoot, branchStorePath(context.workspaceRoot, attachment.kbBranch), relative);
|
|
30
|
+
}
|
|
22
31
|
function errorText(error) {
|
|
23
32
|
return error instanceof Error ? error.message : String(error);
|
|
24
33
|
}
|
|
@@ -59,6 +68,7 @@ export async function readUncompiledAuthoredSource(context, input) {
|
|
|
59
68
|
content,
|
|
60
69
|
stored: entity,
|
|
61
70
|
extractionError: null,
|
|
71
|
+
syncFailure: lastSyncFailure(context, relative),
|
|
62
72
|
};
|
|
63
73
|
}
|
|
64
74
|
catch (error) {
|
|
@@ -80,6 +90,7 @@ export async function readUncompiledAuthoredSource(context, input) {
|
|
|
80
90
|
content,
|
|
81
91
|
stored: { ...frontmatter, id: input.id, type: input.type },
|
|
82
92
|
extractionError,
|
|
93
|
+
syncFailure: lastSyncFailure(context, relative),
|
|
83
94
|
};
|
|
84
95
|
}
|
|
85
96
|
}
|
|
@@ -90,9 +101,14 @@ export async function readUncompiledAuthoredSource(context, input) {
|
|
|
90
101
|
*/
|
|
91
102
|
// implements REQ-kibi-uncompiled-source-visibility
|
|
92
103
|
export function authoredEntityLookup(source) {
|
|
104
|
+
const failure = source.syncFailure;
|
|
93
105
|
return {
|
|
94
106
|
exists: async () => true,
|
|
95
107
|
entity: async () => source.stored,
|
|
108
|
+
// The next sync judges the file by its last failure record, not by the
|
|
109
|
+
// state the file states now, so the write guard does the same.
|
|
110
|
+
keepsSemanticBaseline: (sourceHash) => failure !== undefined &&
|
|
111
|
+
failedSourceKeepsSemanticBaseline(failure, sourceHash),
|
|
96
112
|
};
|
|
97
113
|
}
|
|
98
114
|
/**
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prolog goal that switches the child's standard streams and its default
|
|
3
|
+
* file encoding to UTF-8. SWI-Prolog otherwise follows the process locale,
|
|
4
|
+
* and under a POSIX locale every non-ASCII byte read from stdin becomes
|
|
5
|
+
* U+FFFD, which the store then persists.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PROLOG_UTF8_STREAMS_GOAL = "set_prolog_flag(encoding, utf8), set_stream(user_input, encoding(utf8)), set_stream(user_output, encoding(utf8)), set_stream(user_error, encoding(utf8))";
|
|
8
|
+
/**
|
|
9
|
+
* Encode a goal for an environment variable. SWI-Prolog decodes environment
|
|
10
|
+
* values with the process locale, so non-ASCII goal text travels as base64
|
|
11
|
+
* of its UTF-8 bytes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function encodeGoalForEnv(goal: string): string;
|
|
14
|
+
/** Prolog goal that binds `Atom` to the goal stored by `encodeGoalForEnv`. */
|
|
15
|
+
export declare function decodeGoalFromEnvGoal(variable: string, atom: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* A UTF-8 decoder for one child stream that keeps a character split across
|
|
18
|
+
* two chunks whole instead of decoding each half to U+FFFD.
|
|
19
|
+
*/
|
|
20
|
+
export declare function utf8ChunkDecoder(): (chunk: Buffer) => string;
|
|
21
|
+
//# sourceMappingURL=utf8-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utf8-transport.d.ts","sourceRoot":"","sources":["../../src/prolog/utf8-transport.ts"],"names":[],"mappings":"AAqBA;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,6JACuH,CAAC;AAG7J;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAGD,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5E;AAGD;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAG5D"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { StringDecoder } from "node:string_decoder";
|
|
19
|
+
// implements REQ-prolog-utf8-text
|
|
20
|
+
/**
|
|
21
|
+
* Prolog goal that switches the child's standard streams and its default
|
|
22
|
+
* file encoding to UTF-8. SWI-Prolog otherwise follows the process locale,
|
|
23
|
+
* and under a POSIX locale every non-ASCII byte read from stdin becomes
|
|
24
|
+
* U+FFFD, which the store then persists.
|
|
25
|
+
*/
|
|
26
|
+
export const PROLOG_UTF8_STREAMS_GOAL = "set_prolog_flag(encoding, utf8), set_stream(user_input, encoding(utf8)), set_stream(user_output, encoding(utf8)), set_stream(user_error, encoding(utf8))";
|
|
27
|
+
// implements REQ-prolog-utf8-text
|
|
28
|
+
/**
|
|
29
|
+
* Encode a goal for an environment variable. SWI-Prolog decodes environment
|
|
30
|
+
* values with the process locale, so non-ASCII goal text travels as base64
|
|
31
|
+
* of its UTF-8 bytes.
|
|
32
|
+
*/
|
|
33
|
+
export function encodeGoalForEnv(goal) {
|
|
34
|
+
return Buffer.from(goal, "utf8").toString("base64");
|
|
35
|
+
}
|
|
36
|
+
// implements REQ-prolog-utf8-text
|
|
37
|
+
/** Prolog goal that binds `Atom` to the goal stored by `encodeGoalForEnv`. */
|
|
38
|
+
export function decodeGoalFromEnvGoal(variable, atom) {
|
|
39
|
+
return `getenv('${variable}', ${atom}Encoded), base64_encoded(${atom}, ${atom}Encoded, [encoding(utf8), as(atom)])`;
|
|
40
|
+
}
|
|
41
|
+
// implements REQ-prolog-utf8-text
|
|
42
|
+
/**
|
|
43
|
+
* A UTF-8 decoder for one child stream that keeps a character split across
|
|
44
|
+
* two chunks whole instead of decoding each half to U+FFFD.
|
|
45
|
+
*/
|
|
46
|
+
export function utf8ChunkDecoder() {
|
|
47
|
+
const decoder = new StringDecoder("utf8");
|
|
48
|
+
return (chunk) => decoder.write(chunk);
|
|
49
|
+
}
|
package/dist/prolog.d.ts
CHANGED
package/dist/prolog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AA2BA,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,KAAK,aAAa,EAGnB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AA2BA,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,KAAK,aAAa,EAGnB,MAAM,4BAA4B,CAAC;AAgBpC,wBAAgB,eAAe,IAAI,MAAM,CAsCxC;AACD,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,aAAa,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,QAAQ,CAAC,IAAI,+BAA+B;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,MAAM,EAAE,MAAM;CAO3B;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,iBAAiB,CAAC;CACjC;AAGD,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,EAC5B,OAAO,EAAE,MAAM,IAAI,EACnB,EAAE,GAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,IAChB,GAC5B,MAAM,IAAI,CAIZ;AAGD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,EAC5B,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACrC,EAAE,GAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,IAChB,GAC5B,MAAM,IAAI,CAQZ;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsB;IACpD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,kBAAkB,CAA8B;IACxD,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC;;;;OAIG;IACH,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,iBAAiB,CAAuB;IAChD,6EAA6E;IAC7E,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,mBAAmB,CAAS;gBAExB,OAAO,GAAE,aAAkB;IAiBvC,wBAAwB,CACtB,EAAE,GAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,IAChB,GAC5B,IAAI;IAQP;;;OAGG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;OAGG;IACH,YAAY,IAAI,OAAO;IAIvB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAOd,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAuEd,YAAY;IAgDpB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IA6Q1D,wEAAwE;IAClE,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAIhE,eAAe,IAAI,IAAI;IAIvB,OAAO,CAAC,eAAe;IA2BvB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,kBAAkB;YAIZ,YAAY;IA2C1B,OAAO,CAAC,WAAW;IAqOnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,gBAAgB;IAcxB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,iBAAiB;YAkBX,oBAAoB;IAwClC,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,eAAe;IAmCvB,sEAAsE;IACtE,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,cAAc;IAoGtB,SAAS,IAAI,OAAO;IASpB,MAAM,IAAI,MAAM;IAIhB,OAAO,CAAC,gBAAgB;IASxB;;;OAGG;IACG,SAAS,CAAC,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAqCnE"}
|
package/dist/prolog.js
CHANGED
|
@@ -26,6 +26,7 @@ import { kibiPackageVersions } from "./package-versions.js";
|
|
|
26
26
|
import { writePerformanceTraceEvent } from "./performance-trace.js";
|
|
27
27
|
import { extractPrologErrorRecord, } from "./prolog/error-terms.js";
|
|
28
28
|
import { resolveSwipl, swiplChildEnv, } from "./prolog/swipl-resolver.js";
|
|
29
|
+
import { PROLOG_UTF8_STREAMS_GOAL, decodeGoalFromEnvGoal, encodeGoalForEnv, utf8ChunkDecoder, } from "./prolog/utf8-transport.js";
|
|
29
30
|
const importMetaDir = path.dirname(fileURLToPath(import.meta.url));
|
|
30
31
|
const DEFAULT_PROLOG_OUTPUT_MAX_BUFFER_BYTES = 8 * 1024 * 1024;
|
|
31
32
|
const PROLOG_OUTPUT_OVERFLOW_ERROR = "Query exceeded bounded Prolog output capacity (ENOBUFS); narrow the operation or reduce stored entity size";
|
|
@@ -106,6 +107,8 @@ export class PrologProcess {
|
|
|
106
107
|
outputOverflowed = false;
|
|
107
108
|
errorBuffer = "";
|
|
108
109
|
errorBufferBytes = 0;
|
|
110
|
+
decodeOutput = utf8ChunkDecoder();
|
|
111
|
+
decodeError = utf8ChunkDecoder();
|
|
109
112
|
cache = new Map();
|
|
110
113
|
interactiveQueryTail = Promise.resolve();
|
|
111
114
|
terminationPromise = null;
|
|
@@ -183,7 +186,7 @@ export class PrologProcess {
|
|
|
183
186
|
this.invalidateCache();
|
|
184
187
|
this.process = spawn(launch.bin, [
|
|
185
188
|
"-g",
|
|
186
|
-
|
|
189
|
+
`${PROLOG_UTF8_STREAMS_GOAL}, use_module('${kbPath}'), use_module(library(semweb/rdf_db)), set_prolog_flag(answer_write_options, [max_depth(0), quoted(true)])`,
|
|
187
190
|
"--quiet",
|
|
188
191
|
], {
|
|
189
192
|
detached: process.platform !== "win32",
|
|
@@ -196,6 +199,8 @@ export class PrologProcess {
|
|
|
196
199
|
},
|
|
197
200
|
});
|
|
198
201
|
this.clearQueryBuffers();
|
|
202
|
+
this.decodeOutput = utf8ChunkDecoder();
|
|
203
|
+
this.decodeError = utf8ChunkDecoder();
|
|
199
204
|
if (!this.process.stdout || !this.process.stderr || !this.process.stdin) {
|
|
200
205
|
throw new Error("Failed to spawn Prolog process");
|
|
201
206
|
}
|
|
@@ -574,10 +579,11 @@ export class PrologProcess {
|
|
|
574
579
|
: `(${goalList.join(", ")})`;
|
|
575
580
|
const kbModulePath = resolveKbPlPath();
|
|
576
581
|
const prologGoal = [
|
|
582
|
+
PROLOG_UTF8_STREAMS_GOAL,
|
|
577
583
|
`use_module('${kbModulePath}')`,
|
|
578
584
|
"use_module(library(semweb/rdf_db))",
|
|
579
585
|
"set_prolog_flag(answer_write_options, [max_depth(0), quoted(true)])",
|
|
580
|
-
"
|
|
586
|
+
decodeGoalFromEnvGoal("KIBI_GOAL", "GoalAtom"),
|
|
581
587
|
"read_term_from_atom(GoalAtom, Goal, [variable_names(Vars)])",
|
|
582
588
|
kbPath ? "getenv('KIBI_KB_PATH', KBPath), kb_attach(KBPath)" : "true",
|
|
583
589
|
isBatch ? "WrappedGoal = rdf_transaction(Goal)" : "WrappedGoal = Goal",
|
|
@@ -601,7 +607,7 @@ export class PrologProcess {
|
|
|
601
607
|
env: {
|
|
602
608
|
...process.env,
|
|
603
609
|
...launch.env,
|
|
604
|
-
KIBI_GOAL: combinedGoal,
|
|
610
|
+
KIBI_GOAL: encodeGoalForEnv(combinedGoal ?? "true"),
|
|
605
611
|
KIBI_RUNTIME_NAME: runtimeName,
|
|
606
612
|
KIBI_RUNTIME_VERSION: runtimeVersion,
|
|
607
613
|
KIBI_PACKAGE_VERSIONS: packageVersions,
|
|
@@ -627,13 +633,16 @@ export class PrologProcess {
|
|
|
627
633
|
let settled = false;
|
|
628
634
|
let closed = false;
|
|
629
635
|
let termination = null;
|
|
636
|
+
const decodeStdout = utf8ChunkDecoder();
|
|
637
|
+
const decodeStderr = utf8ChunkDecoder();
|
|
630
638
|
const appendChunk = (chunk, target) => {
|
|
631
639
|
if (outputOverflowed)
|
|
632
640
|
return;
|
|
633
641
|
const bytes = target === "stdout" ? outputBytes : errorBytes;
|
|
634
642
|
const remaining = this.maxOutputBytes - bytes;
|
|
643
|
+
const decode = target === "stdout" ? decodeStdout : decodeStderr;
|
|
635
644
|
if (chunk.byteLength > remaining) {
|
|
636
|
-
const clipped = chunk.subarray(0, Math.max(0, remaining))
|
|
645
|
+
const clipped = decode(chunk.subarray(0, Math.max(0, remaining)));
|
|
637
646
|
if (target === "stdout")
|
|
638
647
|
stdout += clipped;
|
|
639
648
|
else
|
|
@@ -643,11 +652,11 @@ export class PrologProcess {
|
|
|
643
652
|
return;
|
|
644
653
|
}
|
|
645
654
|
if (target === "stdout") {
|
|
646
|
-
stdout += chunk
|
|
655
|
+
stdout += decode(chunk);
|
|
647
656
|
outputBytes += chunk.byteLength;
|
|
648
657
|
}
|
|
649
658
|
else {
|
|
650
|
-
stderr += chunk
|
|
659
|
+
stderr += decode(chunk);
|
|
651
660
|
errorBytes += chunk.byteLength;
|
|
652
661
|
}
|
|
653
662
|
};
|
|
@@ -749,12 +758,12 @@ export class PrologProcess {
|
|
|
749
758
|
return;
|
|
750
759
|
const remaining = this.maxOutputBytes - this.outputBufferBytes;
|
|
751
760
|
if (chunk.byteLength > remaining) {
|
|
752
|
-
this.outputBuffer += chunk.subarray(0, remaining)
|
|
761
|
+
this.outputBuffer += this.decodeOutput(chunk.subarray(0, remaining));
|
|
753
762
|
this.outputBufferBytes = this.maxOutputBytes;
|
|
754
763
|
this.outputOverflowed = true;
|
|
755
764
|
return;
|
|
756
765
|
}
|
|
757
|
-
this.outputBuffer +=
|
|
766
|
+
this.outputBuffer += this.decodeOutput(chunk);
|
|
758
767
|
this.outputBufferBytes += chunk.byteLength;
|
|
759
768
|
this.wakeOutputWaiter();
|
|
760
769
|
}
|
|
@@ -763,12 +772,12 @@ export class PrologProcess {
|
|
|
763
772
|
return;
|
|
764
773
|
const remaining = this.maxOutputBytes - this.errorBufferBytes;
|
|
765
774
|
if (chunk.byteLength > remaining) {
|
|
766
|
-
this.errorBuffer += chunk.subarray(0, remaining)
|
|
775
|
+
this.errorBuffer += this.decodeError(chunk.subarray(0, remaining));
|
|
767
776
|
this.errorBufferBytes = this.maxOutputBytes;
|
|
768
777
|
this.outputOverflowed = true;
|
|
769
778
|
return;
|
|
770
779
|
}
|
|
771
|
-
this.errorBuffer +=
|
|
780
|
+
this.errorBuffer += this.decodeError(chunk);
|
|
772
781
|
this.errorBufferBytes += chunk.byteLength;
|
|
773
782
|
this.wakeOutputWaiter();
|
|
774
783
|
}
|