@gitsheets/core-napi 0.2.0 → 0.4.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/binding.cjs CHANGED
@@ -70,6 +70,15 @@ class NotFoundError extends GitsheetsError {
70
70
  }
71
71
  }
72
72
 
73
+ class ContractError extends GitsheetsError {
74
+ constructor(message, opts = {}) {
75
+ super(message, opts);
76
+ this.name = 'ContractError';
77
+ if (opts.contract !== undefined) this.contract = opts.contract;
78
+ if (opts.issues !== undefined) this.issues = opts.issues;
79
+ }
80
+ }
81
+
73
82
  const CLASS_BY_NAME = {
74
83
  ConfigError,
75
84
  ValidationError,
@@ -78,6 +87,7 @@ const CLASS_BY_NAME = {
78
87
  RefError,
79
88
  PathTemplateError,
80
89
  NotFoundError,
90
+ ContractError,
81
91
  };
82
92
 
83
93
  // Map a structured error raised by the addon onto its typed class. The mapping
@@ -87,6 +97,7 @@ function mapCoreError(raw) {
87
97
  const opts = { code: raw.code, status: raw.status, cause: raw };
88
98
  if (raw.issues !== undefined) opts.issues = raw.issues;
89
99
  if (raw.conflictingPaths !== undefined) opts.conflictingPaths = raw.conflictingPaths;
100
+ if (raw.contract !== undefined) opts.contract = raw.contract;
90
101
  return new Cls(raw.message, opts);
91
102
  }
92
103
 
@@ -113,6 +124,10 @@ module.exports = {
113
124
  // PathTemplateError; schema compilation raises a typed ConfigError.
114
125
  renderPathsBatch: wrap(addon.renderPathsBatch),
115
126
  validateBatch: wrap(addon.validateBatch),
127
+ // The contract identity primitive (specs/behaviors/contracts.md "Canonical
128
+ // form" / "Contract identity"): canonicalize → SHA-256 hex. A string input
129
+ // with no/invalid `format` raises a typed ConfigError.
130
+ canonicalContractHash: wrap(addon.canonicalContractHash),
116
131
  runComparator: wrap(addon.runComparator),
117
132
  // Native locale array sort (declarative `sort = true`) — the ICU collator
118
133
  // matching V8 `localeCompare`. Pure (no structured errors), so unwrapped.
@@ -152,6 +167,11 @@ module.exports = {
152
167
  CoreTransaction: addon.CoreTransaction,
153
168
  coreDiscoverSheets: wrap(addon.coreDiscoverSheets),
154
169
  coreCheckValidators: wrap(addon.coreCheckValidators),
170
+ // Consumer-side contract verification — the two-rung ladder behind
171
+ // `openSheet(name, { contract })` (specs/behaviors/contracts.md "Consumer
172
+ // verification"). A verification failure raises a typed
173
+ // ContractError(contract_unsatisfied) carrying the conformance report.
174
+ verifySheetContract: wrap(addon.verifySheetContract),
155
175
  // Markdown / mdx content-type codec. serialize/parse can raise a typed
156
176
  // ValidationError/ConfigError; the H1 + normalize helpers are pure. Body
157
177
  // NORMALIZATION is native — `markdownSerialize` runs the embedded
@@ -175,4 +195,5 @@ module.exports = {
175
195
  RefError,
176
196
  PathTemplateError,
177
197
  NotFoundError,
198
+ ContractError,
178
199
  };
package/index.d.ts CHANGED
@@ -33,6 +33,17 @@ export interface JsValidationIssue {
33
33
  source: string
34
34
  schemaPath?: string
35
35
  code?: string
36
+ /**
37
+ * The contract name, when the failing branch is a declared contract
38
+ * composed via `allOf` (specs/behaviors/contracts.md).
39
+ */
40
+ contract?: string
41
+ /**
42
+ * The record's sheet-relative path, in a multi-record conformance
43
+ * report (`ContractError.issues` from consumer-side contract
44
+ * verification — specs/behaviors/contracts.md "Consumer verification").
45
+ */
46
+ record?: string
36
47
  }
37
48
  /**
38
49
  * Render a path template against a **batch** of records, returning one path per
@@ -53,6 +64,87 @@ export declare function renderPathsBatch(template: string, records: Array<JsValu
53
64
  * compile surfaces as a structured, typed `ConfigError` (`config_invalid`).
54
65
  */
55
66
  export declare function validateBatch(schema: JsValue, records: Array<JsValue>): Array<Array<JsValidationIssue>>
67
+ /**
68
+ * The contract identity primitive (specs/behaviors/contracts.md "Canonical
69
+ * form" / "Contract identity"): canonicalize `input` → SHA-256 hex of the
70
+ * canonical TOML bytes. `input` is either already-parsed data (a JS
71
+ * object/array/etc.) or a string, in which case `format` says how to parse
72
+ * it (`"json"` or `"toml"` — required for a string input; a
73
+ * `ConfigError(config_invalid)` names the omission). The minimal JS surface
74
+ * over [`gitsheets_core::contract::canonical_contract_hash`].
75
+ */
76
+ export declare function canonicalContractHash(input: string | JsValue, format?: string | undefined | null): string
77
+ /**
78
+ * Validate a contract name (`specs/behaviors/contracts.md` "Contract names
79
+ * and the derived path") — the same check a sheet's `implements` entries run
80
+ * through at sheet-open. `contracts adopt` runs it on the name derived from a
81
+ * candidate document's `$id` before vendoring. Throws
82
+ * `ConfigError(config_invalid)` naming the violated rule.
83
+ */
84
+ export declare function validateContractName(name: string): void
85
+ /**
86
+ * The derived vendored path for a contract name — mechanical, no manifest:
87
+ * `.gitsheets/contracts/<name>.toml`. The minimal JS surface over
88
+ * [`gitsheets_core::contract_path`].
89
+ */
90
+ export declare function contractPath(name: string): string
91
+ /**
92
+ * Enforce the contract document requirements
93
+ * (`specs/behaviors/contracts.md` "Contract document requirements") against a
94
+ * NOT-yet-vendored candidate document — `contracts adopt`'s pre-vendor gate.
95
+ *
96
+ * `input` is either already-parsed data (a JS object) or a string, in which
97
+ * case `format` says how to parse it — mirrors `canonicalContractHash`'s
98
+ * shape exactly, for the same reason: a **JSON-text** input is parsed with
99
+ * `serde_json::from_str` directly (preserving a literal JSON `null`),
100
+ * deliberately NOT via the `JsValue`/core-`Value` marshalling boundary,
101
+ * which silently *drops* null-valued keys per the type-fidelity rules at the
102
+ * top of this file. Requirement 4 (no null-bearing keywords — `default:
103
+ * null`, `const: null`, a null in `enum`) can only ever be violated by a
104
+ * JSON-sourced document (TOML has no null literal at all), so routing
105
+ * through the null-dropping path would make that check silently unreachable
106
+ * for exactly the input shape it exists to catch. A TOML-text or
107
+ * already-parsed-data input has no such hazard (nothing they carry can be a
108
+ * TOML/core `Value` null in the first place), so those still go through the
109
+ * shared `value_to_json` conversion.
110
+ * Throws `ContractError(contract_invalid)` naming the violated rule.
111
+ */
112
+ export declare function checkContractDocument(name: string, input: string | JsValue, format?: string | undefined | null): void
113
+ /**
114
+ * Load a vendored contract document `name` from the committed tree at
115
+ * `treeRef` (scoped under `openRoot`), returning it as JSON text on success.
116
+ * The minimal JS surface over [`gitsheets_core::contract::load_contract`] —
117
+ * runs the exact compile-time check (byte-canonical, document requirements,
118
+ * `$id`↔path) `Sheet::open` composition relies on, so `contracts verify` /
119
+ * `contracts adopt --sheet` get the identical guarantee. Throws
120
+ * `ContractError('contract_missing' | 'contract_invalid')` on failure.
121
+ */
122
+ export declare function contractLoad(gitDir: string, treeRef: string, openRoot: string, name: string): string
123
+ /**
124
+ * The result of a successful [`verify_sheet_contract`] call — the wire shape
125
+ * for `sheet.contractVerification` minus `tree` (the JS binding attaches that
126
+ * from the read snapshot it already resolved to open the sheet).
127
+ */
128
+ export interface JsConformanceReport {
129
+ name: string
130
+ /** `"declared"` or `"structural"` — which rung passed. */
131
+ rung: string
132
+ conforming: boolean
133
+ issues: Array<JsValidationIssue>
134
+ }
135
+ /**
136
+ * Consumer-side contract verification — the two-rung ladder behind
137
+ * `openSheet(name, { contract })` (specs/behaviors/contracts.md "Consumer
138
+ * verification", specs/api/repository.md `opts.contract`). Opens `sheetName`
139
+ * read-only against `treeRef` (config read + effective-schema compile, same
140
+ * as any other read), then verifies it against `schema` — parsed data, or
141
+ * text with `format` naming which text form (mirrors
142
+ * `canonicalContractHash`'s input handling: no format auto-detection). A
143
+ * verification failure (both rungs missed, or the attempted rung missed in
144
+ * `declared`/`structural` mode) surfaces as `ContractError(contract_unsatisfied)`
145
+ * carrying the conformance report in `issues`.
146
+ */
147
+ export declare function verifySheetContract(gitDir: string, treeRef: string, sheetName: string, configPath: string, openRoot: string, prefix: string, schema: string | JsValue, format?: string | undefined | null, mode?: string | undefined | null): JsConformanceReport
56
148
  /**
57
149
  * Compile a raw-JS sort comparator (`rule`, the body of `(a, b) => { … }`) and
58
150
  * run it once against `a`/`b`, returning its numeric result. The direct
@@ -384,6 +476,14 @@ export declare class CoreTransaction {
384
476
  * else. `path` is repo-root-relative. Returns the written blob hash.
385
477
  */
386
478
  writeFile(path: string, content: string): string
479
+ /**
480
+ * Delete a raw file at `path` (repo-root-relative) in this transaction's
481
+ * private tree *(mutating)* — `write_file`'s inverse. Returns whether a
482
+ * blob existed at `path` before the delete. Used by `contracts prune` to
483
+ * remove undeclared vendored contract documents; deep (slash-separated)
484
+ * paths are supported directly via `MutableTree::delete_child_deep`.
485
+ */
486
+ deleteFile(path: string): boolean
387
487
  /**
388
488
  * The blob-hash map of a record's attachments (`name → hash`, sorted by
389
489
  * name), or `null` when the record has no attachment directory. Read-only.
package/index.js CHANGED
@@ -310,13 +310,19 @@ if (!nativeBinding) {
310
310
  throw new Error(`Failed to load native binding`)
311
311
  }
312
312
 
313
- const { roundtrip, parseRecords, serializeRecords, renderPathsBatch, validateBatch, runComparator, collatorSort, CompiledDefinition, recordRead, recordWrite, recordDelete, recordList, writeBlob, substrateStats, substrateReset, recordQuery, recordQueryCandidates, templateFieldNames, recordIndexUnique, recordIndexMulti, createPatch, applyMergePatch, diffRecords, simulateCoreError, CoreTransaction, coreDiscoverSheets, coreCheckValidators, markdownSerialize, markdownNormalizeBody, markdownParse, markdownParseHeaderOnly, markdownExtractH1, markdownRewriteH1 } = nativeBinding
313
+ const { roundtrip, parseRecords, serializeRecords, renderPathsBatch, validateBatch, canonicalContractHash, validateContractName, contractPath, checkContractDocument, contractLoad, verifySheetContract, runComparator, collatorSort, CompiledDefinition, recordRead, recordWrite, recordDelete, recordList, writeBlob, substrateStats, substrateReset, recordQuery, recordQueryCandidates, templateFieldNames, recordIndexUnique, recordIndexMulti, createPatch, applyMergePatch, diffRecords, simulateCoreError, CoreTransaction, coreDiscoverSheets, coreCheckValidators, markdownSerialize, markdownNormalizeBody, markdownParse, markdownParseHeaderOnly, markdownExtractH1, markdownRewriteH1 } = nativeBinding
314
314
 
315
315
  module.exports.roundtrip = roundtrip
316
316
  module.exports.parseRecords = parseRecords
317
317
  module.exports.serializeRecords = serializeRecords
318
318
  module.exports.renderPathsBatch = renderPathsBatch
319
319
  module.exports.validateBatch = validateBatch
320
+ module.exports.canonicalContractHash = canonicalContractHash
321
+ module.exports.validateContractName = validateContractName
322
+ module.exports.contractPath = contractPath
323
+ module.exports.checkContractDocument = checkContractDocument
324
+ module.exports.contractLoad = contractLoad
325
+ module.exports.verifySheetContract = verifySheetContract
320
326
  module.exports.runComparator = runComparator
321
327
  module.exports.collatorSort = collatorSort
322
328
  module.exports.CompiledDefinition = CompiledDefinition
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitsheets/core-napi",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Node.js native binding for gitsheets-core — the FFI marshalling boundary.",
5
5
  "main": "binding.cjs",
6
6
  "types": "index.d.ts",
@@ -33,19 +33,19 @@
33
33
  "index.d.ts"
34
34
  ],
35
35
  "optionalDependencies": {
36
- "@gitsheets/core-napi-linux-x64-gnu": "0.2.0",
37
- "@gitsheets/core-napi-linux-arm64-gnu": "0.2.0",
38
- "@gitsheets/core-napi-linux-x64-musl": "0.2.0",
39
- "@gitsheets/core-napi-darwin-arm64": "0.2.0",
40
- "@gitsheets/core-napi-darwin-x64": "0.2.0",
41
- "@gitsheets/core-napi-win32-x64-msvc": "0.2.0"
36
+ "@gitsheets/core-napi-linux-x64-gnu": "0.4.0",
37
+ "@gitsheets/core-napi-linux-arm64-gnu": "0.4.0",
38
+ "@gitsheets/core-napi-linux-x64-musl": "0.4.0",
39
+ "@gitsheets/core-napi-darwin-arm64": "0.4.0",
40
+ "@gitsheets/core-napi-darwin-x64": "0.4.0",
41
+ "@gitsheets/core-napi-win32-x64-msvc": "0.4.0"
42
42
  },
43
43
  "scripts": {
44
44
  "artifacts": "napi artifacts",
45
45
  "build": "napi build --platform --release",
46
46
  "build:debug": "napi build --platform",
47
47
  "prepublishOnly": "napi prepublish -t npm --skip-gh-release",
48
- "test": "node --test test/roundtrip.mjs test/errors.mjs test/canonical.mjs test/path-template.mjs test/validation-parity.mjs test/engine-parity.mjs test/collator-parity.mjs test/record-crud.mjs test/record-query.mjs test/record-index.mjs test/sheet-store.mjs test/sheet-markdown.mjs test/sheet-attachments.mjs",
48
+ "test": "node --test test/roundtrip.mjs test/errors.mjs test/canonical.mjs test/path-template.mjs test/validation-parity.mjs test/engine-parity.mjs test/collator-parity.mjs test/record-crud.mjs test/record-query.mjs test/record-index.mjs test/sheet-store.mjs test/sheet-markdown.mjs test/sheet-attachments.mjs test/contracts.mjs",
49
49
  "bench": "node bench/query-bench.mjs",
50
50
  "version": "napi version"
51
51
  },