dna-sdk 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.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/adapters/embedding/onnx.d.ts +32 -0
- package/dist/adapters/embedding/onnx.js +51 -0
- package/dist/adapters/filesystem/cache.d.ts +16 -0
- package/dist/adapters/filesystem/cache.js +113 -0
- package/dist/adapters/filesystem/index.d.ts +2 -0
- package/dist/adapters/filesystem/index.js +2 -0
- package/dist/adapters/filesystem/source.d.ts +82 -0
- package/dist/adapters/filesystem/source.js +357 -0
- package/dist/adapters/postgres/index.d.ts +2 -0
- package/dist/adapters/postgres/index.js +2 -0
- package/dist/adapters/postgres/migrations.d.ts +14 -0
- package/dist/adapters/postgres/migrations.js +88 -0
- package/dist/adapters/postgres/source.d.ts +172 -0
- package/dist/adapters/postgres/source.js +569 -0
- package/dist/adapters/resolvers/github.d.ts +12 -0
- package/dist/adapters/resolvers/github.js +47 -0
- package/dist/adapters/resolvers/http.d.ts +22 -0
- package/dist/adapters/resolvers/http.js +145 -0
- package/dist/adapters/resolvers/index.d.ts +3 -0
- package/dist/adapters/resolvers/index.js +3 -0
- package/dist/adapters/resolvers/local.d.ts +24 -0
- package/dist/adapters/resolvers/local.js +132 -0
- package/dist/adapters/search/driver.d.ts +45 -0
- package/dist/adapters/search/driver.js +138 -0
- package/dist/adapters/search/migrations.d.ts +26 -0
- package/dist/adapters/search/migrations.js +58 -0
- package/dist/adapters/search/rrf.d.ts +23 -0
- package/dist/adapters/search/rrf.js +42 -0
- package/dist/adapters/search/sqlite-vec.d.ts +66 -0
- package/dist/adapters/search/sqlite-vec.js +294 -0
- package/dist/bootstrap.d.ts +45 -0
- package/dist/bootstrap.js +134 -0
- package/dist/extensions/agentskills/DOCS.md +18 -0
- package/dist/extensions/agentskills.d.ts +28 -0
- package/dist/extensions/agentskills.js +252 -0
- package/dist/extensions/agentsmd/DOCS.md +18 -0
- package/dist/extensions/agentsmd.d.ts +22 -0
- package/dist/extensions/agentsmd.js +170 -0
- package/dist/extensions/audit/kinds/audit-log.kind.yaml +148 -0
- package/dist/extensions/audit.d.ts +25 -0
- package/dist/extensions/audit.js +62 -0
- package/dist/extensions/automation/kinds/automation.kind.yaml +296 -0
- package/dist/extensions/automation/query.d.ts +49 -0
- package/dist/extensions/automation/query.js +77 -0
- package/dist/extensions/automation/write-guards.d.ts +59 -0
- package/dist/extensions/automation/write-guards.js +153 -0
- package/dist/extensions/automation.d.ts +41 -0
- package/dist/extensions/automation.js +54 -0
- package/dist/extensions/collab.d.ts +16 -0
- package/dist/extensions/collab.js +79 -0
- package/dist/extensions/doc/kinds/doc.kind.yaml +118 -0
- package/dist/extensions/doc.d.ts +25 -0
- package/dist/extensions/doc.js +10 -0
- package/dist/extensions/eval/kinds/eval-baseline.kind.yaml +65 -0
- package/dist/extensions/eval/kinds/eval-case.kind.yaml +140 -0
- package/dist/extensions/eval/kinds/eval-run.kind.yaml +134 -0
- package/dist/extensions/eval/kinds/eval-suite.kind.yaml +87 -0
- package/dist/extensions/eval.d.ts +24 -0
- package/dist/extensions/eval.js +31 -0
- package/dist/extensions/evidence/kinds/evidence.kind.yaml +92 -0
- package/dist/extensions/evidence.d.ts +17 -0
- package/dist/extensions/evidence.js +94 -0
- package/dist/extensions/federation.d.ts +22 -0
- package/dist/extensions/federation.js +173 -0
- package/dist/extensions/guardrails/DOCS.md +20 -0
- package/dist/extensions/guardrails.d.ts +31 -0
- package/dist/extensions/guardrails.js +228 -0
- package/dist/extensions/helix/DOCS-Actor.md +20 -0
- package/dist/extensions/helix/DOCS-Agent.md +20 -0
- package/dist/extensions/helix/DOCS-UseCase.md +20 -0
- package/dist/extensions/helix/layers.d.ts +12 -0
- package/dist/extensions/helix/layers.js +18 -0
- package/dist/extensions/helix/write-guards.d.ts +70 -0
- package/dist/extensions/helix/write-guards.js +172 -0
- package/dist/extensions/helix.d.ts +29 -0
- package/dist/extensions/helix.js +963 -0
- package/dist/extensions/helix_extras.d.ts +124 -0
- package/dist/extensions/helix_extras.js +328 -0
- package/dist/extensions/hooks.d.ts +31 -0
- package/dist/extensions/hooks.js +247 -0
- package/dist/extensions/kinddef/DOCS.md +24 -0
- package/dist/extensions/kinddef.d.ts +40 -0
- package/dist/extensions/kinddef.js +171 -0
- package/dist/extensions/lesson.d.ts +6 -0
- package/dist/extensions/lesson.js +194 -0
- package/dist/extensions/modelreg/kinds/model-profile.kind.yaml +155 -0
- package/dist/extensions/modelreg.d.ts +30 -0
- package/dist/extensions/modelreg.js +38 -0
- package/dist/extensions/recognizer.d.ts +34 -0
- package/dist/extensions/recognizer.js +272 -0
- package/dist/extensions/research.d.ts +32 -0
- package/dist/extensions/research.js +292 -0
- package/dist/extensions/safety.d.ts +33 -0
- package/dist/extensions/safety.js +230 -0
- package/dist/extensions/sdlc/kinds/adr.kind.yaml +136 -0
- package/dist/extensions/sdlc/kinds/archive-proposal.kind.yaml +107 -0
- package/dist/extensions/sdlc/kinds/changelog.kind.yaml +108 -0
- package/dist/extensions/sdlc/kinds/cognitive-policy.kind.yaml +470 -0
- package/dist/extensions/sdlc/kinds/forecast.kind.yaml +125 -0
- package/dist/extensions/sdlc/kinds/insight.kind.yaml +87 -0
- package/dist/extensions/sdlc/kinds/kaizen.kind.yaml +106 -0
- package/dist/extensions/sdlc/kinds/lesson-learned.kind.yaml +270 -0
- package/dist/extensions/sdlc/kinds/narrative.kind.yaml +189 -0
- package/dist/extensions/sdlc/kinds/pattern-insight.kind.yaml +204 -0
- package/dist/extensions/sdlc/kinds/postmortem.kind.yaml +168 -0
- package/dist/extensions/sdlc/kinds/pre-mortem.kind.yaml +145 -0
- package/dist/extensions/sdlc/kinds/prompt-template.kind.yaml +109 -0
- package/dist/extensions/sdlc/kinds/retrospective.kind.yaml +186 -0
- package/dist/extensions/sdlc/kinds/risk-register.kind.yaml +178 -0
- package/dist/extensions/sdlc/kinds/saved-view.kind.yaml +127 -0
- package/dist/extensions/sdlc/kinds/status-report.kind.yaml +129 -0
- package/dist/extensions/sdlc/kinds/synthesis-run.kind.yaml +279 -0
- package/dist/extensions/sdlc/kinds/synthesizer-state.kind.yaml +196 -0
- package/dist/extensions/sdlc/kinds/workflow-event.kind.yaml +195 -0
- package/dist/extensions/sdlc.d.ts +32 -0
- package/dist/extensions/sdlc.js +1318 -0
- package/dist/extensions/soulspec/DOCS.md +19 -0
- package/dist/extensions/soulspec.d.ts +22 -0
- package/dist/extensions/soulspec.js +287 -0
- package/dist/extensions/tenant.d.ts +6 -0
- package/dist/extensions/tenant.js +291 -0
- package/dist/extensions/testkit.d.ts +24 -0
- package/dist/extensions/testkit.js +211 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.js +102 -0
- package/dist/kernel/_text.d.ts +28 -0
- package/dist/kernel/_text.js +42 -0
- package/dist/kernel/bundle-handle.d.ts +83 -0
- package/dist/kernel/bundle-handle.js +184 -0
- package/dist/kernel/capabilities.d.ts +157 -0
- package/dist/kernel/capabilities.js +109 -0
- package/dist/kernel/collaborator-ports.d.ts +91 -0
- package/dist/kernel/collaborator-ports.js +1 -0
- package/dist/kernel/composition-resolver.d.ts +215 -0
- package/dist/kernel/composition-resolver.js +558 -0
- package/dist/kernel/composition.d.ts +10 -0
- package/dist/kernel/composition.js +1 -0
- package/dist/kernel/descriptor-loader.d.ts +13 -0
- package/dist/kernel/descriptor-loader.js +48 -0
- package/dist/kernel/document.d.ts +43 -0
- package/dist/kernel/document.js +71 -0
- package/dist/kernel/embedding.d.ts +28 -0
- package/dist/kernel/embedding.js +78 -0
- package/dist/kernel/errors.d.ts +62 -0
- package/dist/kernel/errors.js +82 -0
- package/dist/kernel/events.d.ts +6 -0
- package/dist/kernel/events.js +21 -0
- package/dist/kernel/evidence-capture.d.ts +69 -0
- package/dist/kernel/evidence-capture.js +138 -0
- package/dist/kernel/fs.d.ts +20 -0
- package/dist/kernel/fs.js +128 -0
- package/dist/kernel/generic-rw.d.ts +33 -0
- package/dist/kernel/generic-rw.js +235 -0
- package/dist/kernel/hooks.d.ts +172 -0
- package/dist/kernel/hooks.js +256 -0
- package/dist/kernel/index.d.ts +624 -0
- package/dist/kernel/index.js +1638 -0
- package/dist/kernel/instance.d.ts +211 -0
- package/dist/kernel/instance.js +487 -0
- package/dist/kernel/kind-registry.d.ts +183 -0
- package/dist/kernel/kind-registry.js +710 -0
- package/dist/kernel/kind_base.d.ts +106 -0
- package/dist/kernel/kind_base.js +244 -0
- package/dist/kernel/layer-resolver.d.ts +48 -0
- package/dist/kernel/layer-resolver.js +284 -0
- package/dist/kernel/lock-manager.d.ts +21 -0
- package/dist/kernel/lock-manager.js +43 -0
- package/dist/kernel/lock.d.ts +38 -0
- package/dist/kernel/lock.js +115 -0
- package/dist/kernel/meta.d.ts +135 -0
- package/dist/kernel/meta.js +817 -0
- package/dist/kernel/models.d.ts +3089 -0
- package/dist/kernel/models.js +764 -0
- package/dist/kernel/navigator.d.ts +36 -0
- package/dist/kernel/navigator.js +132 -0
- package/dist/kernel/port-surface.d.ts +33 -0
- package/dist/kernel/port-surface.js +180 -0
- package/dist/kernel/preview.d.ts +60 -0
- package/dist/kernel/preview.js +54 -0
- package/dist/kernel/prompt-budget.d.ts +39 -0
- package/dist/kernel/prompt-budget.js +43 -0
- package/dist/kernel/prompt-builder.d.ts +25 -0
- package/dist/kernel/prompt-builder.js +237 -0
- package/dist/kernel/protocols.d.ts +751 -0
- package/dist/kernel/protocols.js +493 -0
- package/dist/kernel/reports.d.ts +26 -0
- package/dist/kernel/reports.js +215 -0
- package/dist/kernel/resolver.d.ts +146 -0
- package/dist/kernel/resolver.js +232 -0
- package/dist/kernel/resource.d.ts +57 -0
- package/dist/kernel/resource.js +97 -0
- package/dist/kernel/runtime.d.ts +16 -0
- package/dist/kernel/runtime.js +18 -0
- package/dist/kernel/safety-scanner.d.ts +38 -0
- package/dist/kernel/safety-scanner.js +228 -0
- package/dist/kernel/serialize-to-files.d.ts +29 -0
- package/dist/kernel/serialize-to-files.js +23 -0
- package/dist/kernel/spec-access.d.ts +23 -0
- package/dist/kernel/spec-access.js +40 -0
- package/dist/kernel/studio_ui.d.ts +88 -0
- package/dist/kernel/studio_ui.js +135 -0
- package/dist/kernel/templates.d.ts +77 -0
- package/dist/kernel/templates.js +116 -0
- package/dist/kernel/tool-registry.d.ts +52 -0
- package/dist/kernel/tool-registry.js +89 -0
- package/dist/kernel/write-pipeline.d.ts +65 -0
- package/dist/kernel/write-pipeline.js +271 -0
- package/dist/kernel/writer-helpers.d.ts +24 -0
- package/dist/kernel/writer-helpers.js +42 -0
- package/dist/memory/decay.d.ts +31 -0
- package/dist/memory/decay.js +97 -0
- package/dist/memory/ecphory.d.ts +25 -0
- package/dist/memory/ecphory.js +178 -0
- package/dist/memory/encodingContext.d.ts +21 -0
- package/dist/memory/encodingContext.js +62 -0
- package/dist/memory/index.d.ts +23 -0
- package/dist/memory/index.js +23 -0
- package/dist/memory/memoryType.d.ts +7 -0
- package/dist/memory/memoryType.js +24 -0
- package/dist/memory/policy.d.ts +35 -0
- package/dist/memory/policy.js +35 -0
- package/dist/memory/retrieval.d.ts +21 -0
- package/dist/memory/retrieval.js +145 -0
- package/dist/testing/index.d.ts +8 -0
- package/dist/testing/index.js +8 -0
- package/dist/testing/recordSearchConformance.d.ts +43 -0
- package/dist/testing/recordSearchConformance.js +147 -0
- package/dist/viz/ascii.d.ts +7 -0
- package/dist/viz/ascii.js +60 -0
- package/dist/viz/health.d.ts +8 -0
- package/dist/viz/health.js +161 -0
- package/dist/viz/index.d.ts +10 -0
- package/dist/viz/index.js +10 -0
- package/dist/viz/matrix.d.ts +8 -0
- package/dist/viz/matrix.js +84 -0
- package/dist/viz/mermaid.d.ts +38 -0
- package/dist/viz/mermaid.js +706 -0
- package/package.json +82 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FilesystemSource — SourcePort backed by local .dna/ directories.
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity with Python dna.v3.adapters.filesystem.source.
|
|
5
|
+
*/
|
|
6
|
+
import { access, readFile, readdir, stat } from "node:fs/promises";
|
|
7
|
+
import { resolve, join } from "node:path";
|
|
8
|
+
async function pathExists(p) {
|
|
9
|
+
try {
|
|
10
|
+
await access(p);
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
import yaml from "js-yaml";
|
|
18
|
+
import { countDocs, queryDocs } from "../../kernel/protocols.js";
|
|
19
|
+
import { FilesystemBundleHandle } from "../../kernel/bundle-handle.js";
|
|
20
|
+
export class FilesystemSource {
|
|
21
|
+
baseDir;
|
|
22
|
+
supportsReaders = true;
|
|
23
|
+
constructor(baseDir) {
|
|
24
|
+
this.baseDir = resolve(baseDir);
|
|
25
|
+
}
|
|
26
|
+
// v1.0 — true async via fs/promises. Concurrent reads don't block
|
|
27
|
+
// the event loop, which matters for repos with many bundles or any
|
|
28
|
+
// parallel scope-load workload.
|
|
29
|
+
async loadBootstrapDocs(scope, opts) {
|
|
30
|
+
// Phase 16 — filesystem walks ``loadAll`` and filters by Kind
|
|
31
|
+
// name. SQL adapters override with a fast WHERE filter.
|
|
32
|
+
//
|
|
33
|
+
// Tenant semantics: when ``opts.tenant`` is set, the
|
|
34
|
+
// tenant-published Genome shadows the platform Genome (Phase 9).
|
|
35
|
+
// KindDefinition + LayerPolicy are non-overlayable per Phase 16 —
|
|
36
|
+
// always read from platform.
|
|
37
|
+
//
|
|
38
|
+
// 1:1 parity with Python ``FilesystemSource.load_bootstrap_docs``.
|
|
39
|
+
const { BOOTSTRAP_KIND_NAMES } = await import("../../kernel/protocols.js");
|
|
40
|
+
const bootstrapSet = new Set(BOOTSTRAP_KIND_NAMES);
|
|
41
|
+
let allRaws;
|
|
42
|
+
try {
|
|
43
|
+
allRaws = await this.loadAll(scope);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Platform scope dir absent — tenant-only scopes still resolve.
|
|
47
|
+
allRaws = [];
|
|
48
|
+
}
|
|
49
|
+
let out = allRaws.filter((d) => bootstrapSet.has(d.kind ?? ""));
|
|
50
|
+
const tenant = opts?.tenant;
|
|
51
|
+
if (tenant) {
|
|
52
|
+
const tenantPath = join(this.baseDir, "tenants", tenant, "scopes", scope, "Genome.yaml");
|
|
53
|
+
if (await pathExists(tenantPath)) {
|
|
54
|
+
const tenantPkg = yaml.load(await readFile(tenantPath, "utf-8"));
|
|
55
|
+
if (tenantPkg
|
|
56
|
+
&& typeof tenantPkg === "object"
|
|
57
|
+
&& tenantPkg.kind === "Genome") {
|
|
58
|
+
out = out.filter((d) => d.kind !== "Genome");
|
|
59
|
+
out.push(tenantPkg);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
async loadAll(scope, readers) {
|
|
66
|
+
const scopeDir = join(this.baseDir, scope);
|
|
67
|
+
if (!(await pathExists(scopeDir)))
|
|
68
|
+
throw new Error(`Scope not found: ${scopeDir}`);
|
|
69
|
+
return await this._loadDir(scopeDir, readers ?? [], new Set(["layers", "tenants"]));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Two-planes F2 — record-plane query over `loadAll` + the shared pure
|
|
73
|
+
* helpers (mirror of the Py `SourcePort.query` protocol-default). FS
|
|
74
|
+
* is dev-mode with small scopes; native push-down is the purview of
|
|
75
|
+
* the SQL adapters.
|
|
76
|
+
*
|
|
77
|
+
* `opts.tenant` is a documented NO-OP here: the FS TS adapter has no
|
|
78
|
+
* tenant-aware overlay merge in `loadAll` (divergence from the Py FS
|
|
79
|
+
* source, which unions base + tenant layer with shadowing) — overlay
|
|
80
|
+
* support is an F2.5 candidate alongside the writable FS source.
|
|
81
|
+
*
|
|
82
|
+
* Bundle-format kinds are not visible to this query (no kernel reader
|
|
83
|
+
* back-ref on the TS source) — record-plane docs are plain YAML.
|
|
84
|
+
*/
|
|
85
|
+
async *query(scope, kind, opts = {}) {
|
|
86
|
+
const docs = await this.loadAll(scope);
|
|
87
|
+
for (const row of queryDocs(docs, kind, opts))
|
|
88
|
+
yield row;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Two-planes F2 — record-plane count over `loadAll` + `countDocs`
|
|
92
|
+
* (mirror of the Py protocol-default). Same `opts.tenant` NO-OP
|
|
93
|
+
* caveat as `query`.
|
|
94
|
+
*/
|
|
95
|
+
async count(scope, kind, opts = {}) {
|
|
96
|
+
const docs = await this.loadAll(scope);
|
|
97
|
+
return countDocs(docs, kind, opts);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Explicit contract declaration (s-sourceport-contract-cleanup) — kept
|
|
101
|
+
* honest by the adapter conformance test (declaration == structural
|
|
102
|
+
* derivation). Read-only FS source: in-memory query/count + the L1
|
|
103
|
+
* granular reads (loadOne + listDocRefs, s-dna-port-surface-parity);
|
|
104
|
+
* no bundle/write surface on the TS twin yet.
|
|
105
|
+
*/
|
|
106
|
+
capabilities() {
|
|
107
|
+
return {
|
|
108
|
+
source: "filesystem",
|
|
109
|
+
drafts: false,
|
|
110
|
+
versions: false,
|
|
111
|
+
layers: true,
|
|
112
|
+
bundleRead: false,
|
|
113
|
+
bundleWrite: false,
|
|
114
|
+
kernelAttachable: false,
|
|
115
|
+
granularList: true,
|
|
116
|
+
granularOne: true,
|
|
117
|
+
queryPushdown: true,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Documented NO-OP — the FS source holds no pooled resources (each read
|
|
122
|
+
* opens/closes its own file handles via fs/promises). The member exists
|
|
123
|
+
* for SourcePort surface parity with Python, where `close` is a
|
|
124
|
+
* SOURCE_PORT_CORE_MEMBERS boot-gate entry.
|
|
125
|
+
*/
|
|
126
|
+
async close() {
|
|
127
|
+
// nothing to release
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* L1 granular access — FS impl projects from `loadAll` (mirror of the
|
|
131
|
+
* Py `FilesystemSource.list_doc_refs`): `[kind, name]` refs only, no
|
|
132
|
+
* bundle rehydration. No perf gain over `loadAll` on FS, but keeps the
|
|
133
|
+
* SourcePort contract consistent across adapters (PG is where the gain
|
|
134
|
+
* lives). Tenant: union of base + overlay with the overlay shadowing
|
|
135
|
+
* base, same as the Py twin. Result sorted by (kind, name).
|
|
136
|
+
*/
|
|
137
|
+
async listDocRefs(scope, opts) {
|
|
138
|
+
const refOf = (d) => {
|
|
139
|
+
const meta = d.metadata;
|
|
140
|
+
return [
|
|
141
|
+
typeof d.kind === "string" ? d.kind : "",
|
|
142
|
+
String(meta?.name ?? d.name ?? ""),
|
|
143
|
+
];
|
|
144
|
+
};
|
|
145
|
+
let docs;
|
|
146
|
+
if (opts?.tenant) {
|
|
147
|
+
const overlay = await this.loadLayer(scope, "tenant", opts.tenant);
|
|
148
|
+
const overlayKeys = new Set(overlay.map((d) => refOf(d).join("\0")));
|
|
149
|
+
const base = (await this.loadAll(scope)).filter((d) => !overlayKeys.has(refOf(d).join("\0")));
|
|
150
|
+
docs = [...overlay, ...base];
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
docs = await this.loadAll(scope);
|
|
154
|
+
}
|
|
155
|
+
const refs = [];
|
|
156
|
+
for (const d of docs) {
|
|
157
|
+
const [k, n] = refOf(d);
|
|
158
|
+
if (!k || !n)
|
|
159
|
+
continue;
|
|
160
|
+
if (opts?.kind && k !== opts.kind)
|
|
161
|
+
continue;
|
|
162
|
+
refs.push([k, n]);
|
|
163
|
+
}
|
|
164
|
+
refs.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0);
|
|
165
|
+
return refs;
|
|
166
|
+
}
|
|
167
|
+
async resolveRef(scope, ref) {
|
|
168
|
+
const path = join(this.baseDir, scope, ref);
|
|
169
|
+
if (!(await pathExists(path)))
|
|
170
|
+
return "";
|
|
171
|
+
return await readFile(path, "utf-8");
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* L1 granular access — FS impl projects from `loadAll` (mirror of the
|
|
175
|
+
* Py `FilesystemSource.load_one`). No perf gain over `loadAll` on FS
|
|
176
|
+
* (cheap in-process disk reads) but keeps the SourcePort contract
|
|
177
|
+
* consistent across adapters. Tenant overlay shadows base: when
|
|
178
|
+
* `opts.tenant` is set the tenant layer is consulted first and the
|
|
179
|
+
* BASE layer is the fallback (same as Python — a tenant read of a doc
|
|
180
|
+
* with no overlay still returns the base doc).
|
|
181
|
+
*/
|
|
182
|
+
async loadOne(scope, kind, name, opts) {
|
|
183
|
+
const readers = opts?.readers ?? [];
|
|
184
|
+
const matches = (d) => {
|
|
185
|
+
if (d.kind !== kind)
|
|
186
|
+
return false;
|
|
187
|
+
const meta = d.metadata;
|
|
188
|
+
return meta?.name === name || d.name === name;
|
|
189
|
+
};
|
|
190
|
+
if (opts?.tenant) {
|
|
191
|
+
const overlay = await this.loadLayer(scope, "tenant", opts.tenant, readers);
|
|
192
|
+
for (const d of overlay)
|
|
193
|
+
if (matches(d))
|
|
194
|
+
return d;
|
|
195
|
+
}
|
|
196
|
+
const base = await this.loadAll(scope, readers);
|
|
197
|
+
for (const d of base)
|
|
198
|
+
if (matches(d))
|
|
199
|
+
return d;
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
async loadLayer(scope, layerId, layerValue, readers) {
|
|
203
|
+
// Phase 2b (parity with the Py FS source): tenant layers live at
|
|
204
|
+
// tenants/<X>/scopes/<S>/; other layers (branch, region, user) keep
|
|
205
|
+
// the legacy <scope>/layers/ path. Tenant reads check the new path
|
|
206
|
+
// first, falling back to the legacy layers/tenant/<X>/ for
|
|
207
|
+
// pre-migration data.
|
|
208
|
+
if (layerId === "tenant") {
|
|
209
|
+
const newDir = join(this.baseDir, "tenants", layerValue, "scopes", scope);
|
|
210
|
+
if (await pathExists(newDir)) {
|
|
211
|
+
return await this._loadDir(newDir, readers ?? [], new Set());
|
|
212
|
+
}
|
|
213
|
+
const legacyDir = join(this.baseDir, scope, "layers", "tenant", layerValue);
|
|
214
|
+
if (await pathExists(legacyDir)) {
|
|
215
|
+
return await this._loadDir(legacyDir, readers ?? [], new Set());
|
|
216
|
+
}
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
const layerDir = join(this.baseDir, scope, "layers", layerId, layerValue);
|
|
220
|
+
if (!(await pathExists(layerDir)))
|
|
221
|
+
return [];
|
|
222
|
+
return await this._loadDir(layerDir, readers ?? [], new Set());
|
|
223
|
+
}
|
|
224
|
+
async _loadDir(directory, readers, skip) {
|
|
225
|
+
const documents = [];
|
|
226
|
+
const readerMatched = new Set();
|
|
227
|
+
// 1. Readers first (bundles take priority over YAML)
|
|
228
|
+
if (readers.length > 0) {
|
|
229
|
+
// Readers on the scope root directory itself (Python parity — a
|
|
230
|
+
// standalone marker like AGENTS.md at the scope root is a document
|
|
231
|
+
// of the scope; agents.md/v1 uses exactly that layout).
|
|
232
|
+
const rootHandle = new FilesystemBundleHandle(directory);
|
|
233
|
+
for (const reader of readers) {
|
|
234
|
+
try {
|
|
235
|
+
if (await reader.detect(rootHandle)) {
|
|
236
|
+
const doc = await reader.read(rootHandle);
|
|
237
|
+
if (doc != null && typeof doc === "object" && "kind" in doc) {
|
|
238
|
+
documents.push(doc);
|
|
239
|
+
}
|
|
240
|
+
readerMatched.add(directory.split("/").filter(Boolean).pop() ?? "");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
// Skip reader errors (Python logs a warning and continues)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
await this._readRecursive(directory, readers, documents, skip, readerMatched);
|
|
248
|
+
}
|
|
249
|
+
// 2. YAML files, skipping stems already matched by readers
|
|
250
|
+
await this._collectYaml(directory, directory, skip, documents, readerMatched);
|
|
251
|
+
// 3. Deduplicate by kind/name — readers (first) take priority over YAML (later)
|
|
252
|
+
const seen = new Set();
|
|
253
|
+
const deduped = [];
|
|
254
|
+
for (const doc of documents) {
|
|
255
|
+
const meta = doc.metadata ?? {};
|
|
256
|
+
const key = `${doc.kind}/${meta.name ?? ""}`;
|
|
257
|
+
if (!seen.has(key)) {
|
|
258
|
+
seen.add(key);
|
|
259
|
+
deduped.push(doc);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return deduped;
|
|
263
|
+
}
|
|
264
|
+
async _collectYaml(root, directory, skip, documents, readerMatched) {
|
|
265
|
+
const entries = (await readdir(directory)).sort();
|
|
266
|
+
for (const entry of entries) {
|
|
267
|
+
const full = join(directory, entry);
|
|
268
|
+
const relParts = full.slice(root.length + 1).split("/");
|
|
269
|
+
if (relParts.some((p) => skip.has(p)))
|
|
270
|
+
continue;
|
|
271
|
+
const st = await stat(full);
|
|
272
|
+
if (st.isDirectory()) {
|
|
273
|
+
await this._collectYaml(root, full, skip, documents, readerMatched);
|
|
274
|
+
}
|
|
275
|
+
else if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
|
|
276
|
+
// Dedup of reader-loaded bundles vs same-named YAML siblings is
|
|
277
|
+
// handled by step 3 ((kind, name) key) in _loadDir. Stem-only
|
|
278
|
+
// skip here would drop YAMLs of a DIFFERENT kind that happen to
|
|
279
|
+
// share a name with a bundle (e.g. Skill/create-initiative +
|
|
280
|
+
// UseCase/create-initiative — both valid, different kinds).
|
|
281
|
+
try {
|
|
282
|
+
const content = yaml.load(await readFile(full, "utf-8"));
|
|
283
|
+
if (content != null &&
|
|
284
|
+
typeof content === "object" &&
|
|
285
|
+
"kind" in content) {
|
|
286
|
+
documents.push(content);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
// Skip unparseable YAML
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async _readRecursive(directory, readers, documents, skip, readerMatched) {
|
|
296
|
+
// H3 — container-aware reader routing.
|
|
297
|
+
//
|
|
298
|
+
// The scanner walks subdirs of `directory`. Each subdir IS a
|
|
299
|
+
// bundle (or a non-bundle subdir). The PARENT directory's name
|
|
300
|
+
// equals the bundle's *container* (e.g.
|
|
301
|
+
// `pageindex-documents/frank-hutter-cv/` → container =
|
|
302
|
+
// `pageindex-documents`).
|
|
303
|
+
//
|
|
304
|
+
// Pre-H3, we tried every registered reader in order and stopped
|
|
305
|
+
// at first match. That broke when two readers detected the same
|
|
306
|
+
// marker file (PageIndex + Graphify both used MANIFEST.md): the
|
|
307
|
+
// alphabetically-first extension's reader silently captured the
|
|
308
|
+
// other's bundles.
|
|
309
|
+
//
|
|
310
|
+
// H3 fix: prefer readers whose `_ownerContainer` member matches the
|
|
311
|
+
// parent dir name. Unscoped readers (undefined — a formal ReaderPort
|
|
312
|
+
// member since s-dna-rw-roundtrip-suite, no longer duck-typed) are
|
|
313
|
+
// tried only as fallback.
|
|
314
|
+
const containerName = directory.split("/").filter(Boolean).pop() ?? "";
|
|
315
|
+
const ownedReaders = readers.filter((r) => r._ownerContainer === containerName);
|
|
316
|
+
const globalReaders = readers.filter((r) => r._ownerContainer == null);
|
|
317
|
+
const orderedReaders = [...ownedReaders, ...globalReaders];
|
|
318
|
+
const entries = (await readdir(directory)).sort();
|
|
319
|
+
for (const entry of entries) {
|
|
320
|
+
const full = join(directory, entry);
|
|
321
|
+
if (!(await stat(full)).isDirectory() || skip.has(entry))
|
|
322
|
+
continue;
|
|
323
|
+
let matched = false;
|
|
324
|
+
const handle = new FilesystemBundleHandle(full);
|
|
325
|
+
for (const reader of orderedReaders) {
|
|
326
|
+
try {
|
|
327
|
+
if (await reader.detect(handle)) {
|
|
328
|
+
const doc = await reader.read(handle);
|
|
329
|
+
if (doc != null &&
|
|
330
|
+
typeof doc === "object" &&
|
|
331
|
+
"kind" in doc) {
|
|
332
|
+
documents.push(doc);
|
|
333
|
+
}
|
|
334
|
+
readerMatched.add(entry);
|
|
335
|
+
matched = true;
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
// Skip reader errors
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (!matched) {
|
|
344
|
+
await this._readRecursive(full, readers, documents, skip, readerMatched);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// Two-planes F2 — RecordStorePort conformance BY COMPOSITION (compile-time;
|
|
350
|
+
// lives in src because tsconfig excludes tests/ from `tsc --noEmit`). The
|
|
351
|
+
// FS TS adapter provides the READ half (`query`|`count`) — it has no
|
|
352
|
+
// save/delete yet; full conformance arrives with the writable FS source
|
|
353
|
+
// (F2.5+). Function is never called — it exists for the typecheck.
|
|
354
|
+
function _fsRecordStoreReadHalfConformance(src) {
|
|
355
|
+
return src;
|
|
356
|
+
}
|
|
357
|
+
void _fsRecordStoreReadHalfConformance;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres schema migrations for the SDK adapter.
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity (subset) with python/dna/adapters/postgres/source.py.
|
|
5
|
+
* v1.0 cut ships migrations 1-3:
|
|
6
|
+
*
|
|
7
|
+
* v1 — initial: dna_documents, dna_bundle_entries
|
|
8
|
+
* v2 — dna_layer_documents (layer overlays)
|
|
9
|
+
* v3 — tenant column on documents + bundle_entries (Phase 2c)
|
|
10
|
+
*
|
|
11
|
+
* Future migrations (versions table, lockfile, eventbus) land here as
|
|
12
|
+
* the TS Postgres adapter grows toward full Python parity.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MIGRATIONS: Record<number, string[]>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres schema migrations for the SDK adapter.
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity (subset) with python/dna/adapters/postgres/source.py.
|
|
5
|
+
* v1.0 cut ships migrations 1-3:
|
|
6
|
+
*
|
|
7
|
+
* v1 — initial: dna_documents, dna_bundle_entries
|
|
8
|
+
* v2 — dna_layer_documents (layer overlays)
|
|
9
|
+
* v3 — tenant column on documents + bundle_entries (Phase 2c)
|
|
10
|
+
*
|
|
11
|
+
* Future migrations (versions table, lockfile, eventbus) land here as
|
|
12
|
+
* the TS Postgres adapter grows toward full Python parity.
|
|
13
|
+
*/
|
|
14
|
+
export const MIGRATIONS = {
|
|
15
|
+
1: [
|
|
16
|
+
`CREATE TABLE IF NOT EXISTS {schema}.dna_documents (
|
|
17
|
+
scope TEXT NOT NULL,
|
|
18
|
+
kind TEXT NOT NULL,
|
|
19
|
+
name TEXT NOT NULL,
|
|
20
|
+
content JSONB NOT NULL,
|
|
21
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
22
|
+
updated_at TEXT NOT NULL,
|
|
23
|
+
PRIMARY KEY (scope, kind, name)
|
|
24
|
+
)`,
|
|
25
|
+
`CREATE TABLE IF NOT EXISTS {schema}.dna_bundle_entries (
|
|
26
|
+
scope TEXT NOT NULL,
|
|
27
|
+
kind TEXT NOT NULL,
|
|
28
|
+
name TEXT NOT NULL,
|
|
29
|
+
entry_path TEXT NOT NULL,
|
|
30
|
+
content TEXT NOT NULL,
|
|
31
|
+
updated_at TEXT NOT NULL,
|
|
32
|
+
PRIMARY KEY (scope, kind, name, entry_path)
|
|
33
|
+
)`,
|
|
34
|
+
`CREATE INDEX IF NOT EXISTS dna_bundle_entries_scope_kind_idx
|
|
35
|
+
ON {schema}.dna_bundle_entries (scope, kind)`,
|
|
36
|
+
],
|
|
37
|
+
2: [
|
|
38
|
+
`CREATE TABLE IF NOT EXISTS {schema}.dna_layer_documents (
|
|
39
|
+
scope TEXT NOT NULL,
|
|
40
|
+
layer_id TEXT NOT NULL,
|
|
41
|
+
layer_value TEXT NOT NULL,
|
|
42
|
+
kind TEXT NOT NULL,
|
|
43
|
+
name TEXT NOT NULL,
|
|
44
|
+
content JSONB NOT NULL,
|
|
45
|
+
updated_at TEXT NOT NULL,
|
|
46
|
+
PRIMARY KEY (scope, layer_id, layer_value, kind, name)
|
|
47
|
+
)`,
|
|
48
|
+
],
|
|
49
|
+
3: [
|
|
50
|
+
`ALTER TABLE {schema}.dna_documents ADD COLUMN IF NOT EXISTS tenant TEXT NOT NULL DEFAULT ''`,
|
|
51
|
+
`ALTER TABLE {schema}.dna_bundle_entries ADD COLUMN IF NOT EXISTS tenant TEXT NOT NULL DEFAULT ''`,
|
|
52
|
+
`ALTER TABLE {schema}.dna_documents DROP CONSTRAINT IF EXISTS dna_documents_pkey`,
|
|
53
|
+
`ALTER TABLE {schema}.dna_documents ADD PRIMARY KEY (scope, kind, name, tenant)`,
|
|
54
|
+
`ALTER TABLE {schema}.dna_bundle_entries DROP CONSTRAINT IF EXISTS dna_bundle_entries_pkey`,
|
|
55
|
+
`ALTER TABLE {schema}.dna_bundle_entries ADD PRIMARY KEY (scope, kind, name, entry_path, tenant)`,
|
|
56
|
+
],
|
|
57
|
+
4: [
|
|
58
|
+
// v1.0 — Module catalog versioning (Phase 10 parity).
|
|
59
|
+
//
|
|
60
|
+
// `dna_versions` stores immutable per-version snapshots. Module
|
|
61
|
+
// catalog publish flow (publishModuleVersion) inserts here; list/
|
|
62
|
+
// deprecation queries read from here. Same semver pinning rules
|
|
63
|
+
// as the Python adapter — `(scope, kind, name, tenant, semver)`
|
|
64
|
+
// is unique when semver is non-NULL (immutable releases).
|
|
65
|
+
`CREATE TABLE IF NOT EXISTS {schema}.dna_versions (
|
|
66
|
+
id BIGSERIAL PRIMARY KEY,
|
|
67
|
+
scope TEXT NOT NULL,
|
|
68
|
+
kind TEXT NOT NULL,
|
|
69
|
+
name TEXT NOT NULL,
|
|
70
|
+
content JSONB NOT NULL,
|
|
71
|
+
version INTEGER NOT NULL,
|
|
72
|
+
semver TEXT,
|
|
73
|
+
deprecated BOOLEAN NOT NULL DEFAULT false,
|
|
74
|
+
deprecation_message TEXT,
|
|
75
|
+
author TEXT,
|
|
76
|
+
created_at TEXT NOT NULL,
|
|
77
|
+
tenant TEXT NOT NULL DEFAULT ''
|
|
78
|
+
)`,
|
|
79
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS dna_versions_semver_unique
|
|
80
|
+
ON {schema}.dna_versions (scope, kind, name, tenant, semver)
|
|
81
|
+
WHERE semver IS NOT NULL`,
|
|
82
|
+
`CREATE INDEX IF NOT EXISTS dna_versions_module_lookup
|
|
83
|
+
ON {schema}.dna_versions (kind, scope, tenant, semver)
|
|
84
|
+
WHERE kind = 'Module' AND semver IS NOT NULL`,
|
|
85
|
+
`CREATE INDEX IF NOT EXISTS dna_versions_scope_kind_name_idx
|
|
86
|
+
ON {schema}.dna_versions (scope, kind, name, tenant)`,
|
|
87
|
+
],
|
|
88
|
+
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgresSource — TypeScript adapter for the SDK's WritableSourcePort.
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity (MVP) with python/dna/adapters/postgres/source.py.
|
|
5
|
+
* Implements:
|
|
6
|
+
* - WritableSourcePort: loadBootstrapDocs, loadAll, resolveRef, loadLayer,
|
|
7
|
+
* saveDocument, deleteDocument, publish (no-op for MVP), close
|
|
8
|
+
* - BundleEntryReadable: fetchBundleEntry — port-aware byte fetch
|
|
9
|
+
* - KernelAttachable: attachKernel — uniform writer/reader wiring
|
|
10
|
+
* - Versionable: getVersion — basic single-version support
|
|
11
|
+
*
|
|
12
|
+
* Out of scope for v1.0 (lands in v1.1 follow-up):
|
|
13
|
+
* - Module versioning catalog (Phase 10 lockfile flow)
|
|
14
|
+
* - Tenant overlay routing (multi-tenant scopes endpoint)
|
|
15
|
+
* - LISTEN/NOTIFY event bus (Phase 15.1)
|
|
16
|
+
* - Draft → published two-step write flow
|
|
17
|
+
*
|
|
18
|
+
* The schema mirrors Python's `dna_documents` / `dna_bundle_entries` /
|
|
19
|
+
* `dna_layer_documents` — already-existing Python deployments are
|
|
20
|
+
* read-compatible with this adapter.
|
|
21
|
+
*/
|
|
22
|
+
import { Pool, type PoolConfig } from "pg";
|
|
23
|
+
import type { Kernel } from "../../kernel/index.js";
|
|
24
|
+
import type { ReaderPort, WritableSourcePort, WriterPort } from "../../kernel/protocols.js";
|
|
25
|
+
import type { SourceCapabilities } from "../../kernel/capabilities.js";
|
|
26
|
+
export interface PostgresSourceOptions {
|
|
27
|
+
/** Postgres connection string (postgresql://user:pass@host/db). */
|
|
28
|
+
connectionString?: string;
|
|
29
|
+
/** Schema to create tables in. Defaults to "public". */
|
|
30
|
+
schema?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Optional pre-built Pool. When provided the adapter uses it
|
|
33
|
+
* verbatim and does NOT close it on `close()`. Useful for sharing
|
|
34
|
+
* pools across adapters or test fixtures.
|
|
35
|
+
*/
|
|
36
|
+
pool?: Pool;
|
|
37
|
+
/** Initial writers (filled by attachKernel if empty). */
|
|
38
|
+
writers?: WriterPort[];
|
|
39
|
+
/** Initial readers (filled by attachKernel if empty). */
|
|
40
|
+
readers?: ReaderPort[];
|
|
41
|
+
/** Extra Pool options (max, idleTimeoutMillis, ...). */
|
|
42
|
+
poolOptions?: PoolConfig;
|
|
43
|
+
}
|
|
44
|
+
export declare class PostgresSource implements WritableSourcePort {
|
|
45
|
+
readonly supportsReaders = false;
|
|
46
|
+
private _pool;
|
|
47
|
+
private readonly _ownsPool;
|
|
48
|
+
private readonly _schema;
|
|
49
|
+
private _migrated;
|
|
50
|
+
_writers: WriterPort[];
|
|
51
|
+
_readers: ReaderPort[];
|
|
52
|
+
private _kernel;
|
|
53
|
+
constructor(opts: PostgresSourceOptions);
|
|
54
|
+
init(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Explicit contract declaration (s-sourceport-contract-cleanup) — kept
|
|
57
|
+
* honest by the adapter conformance test. The TS PG adapter has the
|
|
58
|
+
* write half + versions/bundle-read + the granular ref list
|
|
59
|
+
* (listDocRefs, s-dna-port-surface-parity); query/count push-down and
|
|
60
|
+
* loadOne are Py-only this phase (F2.5+ candidates).
|
|
61
|
+
*/
|
|
62
|
+
capabilities(): SourceCapabilities;
|
|
63
|
+
close(): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* KernelAttachable interface impl. Idempotent: copies the kernel's
|
|
66
|
+
* registered writers + readers into this source so bundle writes
|
|
67
|
+
* (which depend on `_writers` producing serialised entries via
|
|
68
|
+
* `WriterPort.serialize`) work even when this source was
|
|
69
|
+
* instantiated without explicit injection.
|
|
70
|
+
*/
|
|
71
|
+
attachKernel(kernel: Kernel): void;
|
|
72
|
+
private _runMigrations;
|
|
73
|
+
loadBootstrapDocs(scope: string, opts?: {
|
|
74
|
+
tenant?: string;
|
|
75
|
+
}): Promise<Record<string, unknown>[]>;
|
|
76
|
+
loadAll(scope: string, readers?: ReaderPort[]): Promise<Record<string, unknown>[]>;
|
|
77
|
+
resolveRef(_scope: string, ref: string): Promise<string>;
|
|
78
|
+
/**
|
|
79
|
+
* L1 granular access (s-dna-port-surface-parity — mirror of the Py
|
|
80
|
+
* `PostgresSource.list_doc_refs`): one indexed SELECT of `[kind, name]`
|
|
81
|
+
* refs, metadata only — no bundle entries, no reader rehydration. This
|
|
82
|
+
* is where the granular read actually pays off vs `loadAll` (which is
|
|
83
|
+
* an N+1 over bundle entries). Tenant: union of the base layer
|
|
84
|
+
* (`tenant=''`) with the overlay; refs are DISTINCT so an overlay
|
|
85
|
+
* shadow contributes a single entry.
|
|
86
|
+
*/
|
|
87
|
+
listDocRefs(scope: string, opts?: {
|
|
88
|
+
kind?: string | null;
|
|
89
|
+
tenant?: string | null;
|
|
90
|
+
}): Promise<Array<[string, string]>>;
|
|
91
|
+
loadLayer(scope: string, layerId: string, layerValue: string, _readers?: ReaderPort[]): Promise<Record<string, unknown>[]>;
|
|
92
|
+
private _loadBundleEntries;
|
|
93
|
+
/**
|
|
94
|
+
* Fetch a single bundle entry by name. Mirrors Python.
|
|
95
|
+
*
|
|
96
|
+
* Disambiguation: when the kernel supplies ``kind`` (e.g.
|
|
97
|
+
* ``"GraphifyArtifact"``), the WHERE clause includes ``kind=$N``
|
|
98
|
+
* so a Skill ``foo`` and a GraphifyArtifact ``foo`` in the same
|
|
99
|
+
* scope don't collide. Without ``kind`` we fall back to
|
|
100
|
+
* ``(scope, name, entry_path)`` and accept the rare collision
|
|
101
|
+
* risk — older callers that built the kernel before the
|
|
102
|
+
* protocol gained the kwarg fall through this path.
|
|
103
|
+
*/
|
|
104
|
+
fetchBundleEntry(scope: string, container: string, name: string, entry: string, options?: {
|
|
105
|
+
tenant?: string | null;
|
|
106
|
+
kind?: string | null;
|
|
107
|
+
}): Promise<Uint8Array>;
|
|
108
|
+
saveDocument(scope: string, kind: string, name: string, raw: Record<string, unknown>, options?: {
|
|
109
|
+
author?: string;
|
|
110
|
+
tenant?: string | null;
|
|
111
|
+
layer?: [string, string];
|
|
112
|
+
versionRetention?: number | null;
|
|
113
|
+
}): Promise<string>;
|
|
114
|
+
deleteDocument(scope: string, kind: string, name: string, options?: {
|
|
115
|
+
author?: string;
|
|
116
|
+
tenant?: string | null;
|
|
117
|
+
layer?: [string, string];
|
|
118
|
+
}): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* MVP no-op: PostgresSource saves directly to dna_documents
|
|
121
|
+
* (no draft state). Future v1.1 may introduce drafts → publish
|
|
122
|
+
* promotion; the method exists today so callers expecting it
|
|
123
|
+
* (e.g. cross-language migrations from Python) don't break.
|
|
124
|
+
*/
|
|
125
|
+
publish(_scope: string, _kind: string, _name: string): Promise<string>;
|
|
126
|
+
getVersion(scope: string, kind: string, name: string, versionId: string): Promise<Record<string, unknown>>;
|
|
127
|
+
/**
|
|
128
|
+
* Publish an immutable Module version with semver. Mirrors the Python
|
|
129
|
+
* Phase 10 `publishModuleVersion` flow:
|
|
130
|
+
*
|
|
131
|
+
* 1. Reject re-publish of an existing (scope, name, semver, tenant).
|
|
132
|
+
* 2. Insert the row into dna_versions with semver set.
|
|
133
|
+
* 3. Mirror the row to dna_documents (latest-stable view) so
|
|
134
|
+
* `loadAll` continues returning the freshest content.
|
|
135
|
+
*
|
|
136
|
+
* Throws `VersionAlreadyPublished` when the (scope, kind, name,
|
|
137
|
+
* tenant, semver) tuple already exists. This is the immutability
|
|
138
|
+
* guarantee — once published, that version is frozen.
|
|
139
|
+
*/
|
|
140
|
+
publishModuleVersion(scope: string, name: string, raw: Record<string, unknown>, semver: string, options?: {
|
|
141
|
+
tenant?: string | null;
|
|
142
|
+
author?: string;
|
|
143
|
+
}): Promise<{
|
|
144
|
+
id: number;
|
|
145
|
+
semver: string;
|
|
146
|
+
}>;
|
|
147
|
+
/**
|
|
148
|
+
* List published versions of a Module. Returns newest-first by
|
|
149
|
+
* semver lexicographic order (good enough for typical X.Y.Z;
|
|
150
|
+
* v1.1 may add proper semver comparison).
|
|
151
|
+
*/
|
|
152
|
+
listModuleVersions(scope: string, name: string, options?: {
|
|
153
|
+
tenant?: string | null;
|
|
154
|
+
includeDeprecated?: boolean;
|
|
155
|
+
}): Promise<Array<{
|
|
156
|
+
id: number;
|
|
157
|
+
semver: string;
|
|
158
|
+
version: number;
|
|
159
|
+
deprecated: boolean;
|
|
160
|
+
deprecationMessage: string | null;
|
|
161
|
+
author: string | null;
|
|
162
|
+
createdAt: string;
|
|
163
|
+
}>>;
|
|
164
|
+
/**
|
|
165
|
+
* Mark a published Module version as deprecated. The row stays
|
|
166
|
+
* (immutability), but listModuleVersions can filter it out via
|
|
167
|
+
* `includeDeprecated: false`.
|
|
168
|
+
*/
|
|
169
|
+
deprecateModuleVersion(scope: string, name: string, semver: string, message?: string, options?: {
|
|
170
|
+
tenant?: string | null;
|
|
171
|
+
}): Promise<void>;
|
|
172
|
+
}
|