@gitsheets/core-napi 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/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # @gitsheets/core-napi
2
+
3
+ Node.js native binding for [`gitsheets-core`](../gitsheets-core) — the FFI
4
+ marshalling boundary between the published `gitsheets` npm package (a thin
5
+ TypeScript shell) and the Rust engine that owns the bytes: TOML parse/serialize,
6
+ canonical normalization, path templates, JSON-Schema validation, the embedded
7
+ JS engine, ICU collation, record CRUD / query / index, diff/patch, attachments,
8
+ and the `Sheet`/`Transaction`/`Store` state machine. Tree/blob/commit ops run on
9
+ `holo-tree` (gitoxide) inside the core.
10
+
11
+ This package exists so that a consumer can `npm install gitsheets` and get a
12
+ working native addon **without a Rust toolchain** — the matching prebuilt binary
13
+ is pulled in as an `optionalDependency` platform package.
14
+
15
+ ## Building (in-repo development)
16
+
17
+ Requires a Rust toolchain and `@napi-rs/cli` (a devDependency):
18
+
19
+ ```sh
20
+ npm install
21
+ npm run build:debug # or: npm run build (release)
22
+ npm test # node --test against scratch git repos
23
+ ```
24
+
25
+ `napi build` emits `gitsheets-core.<triple>.node`. The generated `index.js`
26
+ loader, `index.d.ts` types, and `binding.cjs` wrapper **are committed**; only the
27
+ `.node` binaries are git-ignored (built per-platform in CI). The `npm/<triple>/`
28
+ platform-package manifests are committed too; their `.node` payloads are dropped
29
+ in at publish time.
30
+
31
+ In-repo, the JS package resolves this binding through the npm-workspace symlink
32
+ (`node_modules/@gitsheets/core-napi → rust/gitsheets-napi`) and loads the locally
33
+ built `.node` next to `index.js`; the `optionalDependencies` platform packages
34
+ are only used by external consumers.
35
+
36
+ ## Publishing / prebuilds
37
+
38
+ Published as the scoped package **`@gitsheets/core-napi`** with per-platform
39
+ prebuilt binaries shipped as `optionalDependencies`:
40
+
41
+ | Platform package | Triple | Built on | Smoke-tested |
42
+ | --- | --- | --- | --- |
43
+ | `@gitsheets/core-napi-linux-x64-gnu` | `x86_64-unknown-linux-gnu` | ubuntu-latest | ✓ native |
44
+ | `@gitsheets/core-napi-linux-arm64-gnu` | `aarch64-unknown-linux-gnu` | ubuntu-24.04-arm | ✓ native |
45
+ | `@gitsheets/core-napi-linux-x64-musl` | `x86_64-unknown-linux-musl` | ubuntu-latest (zig cross) | build-only |
46
+ | `@gitsheets/core-napi-darwin-arm64` | `aarch64-apple-darwin` | macos-latest | ✓ native |
47
+ | `@gitsheets/core-napi-darwin-x64` | `x86_64-apple-darwin` | macos-latest (cross) | build-only |
48
+ | `@gitsheets/core-napi-win32-x64-msvc` | `x86_64-pc-windows-msvc` | windows-latest | ✓ native |
49
+
50
+ Native targets build + smoke-test on a matching runner; cross targets (musl,
51
+ darwin-x64) build only, since their `.node` can't run on the host arch/libc (the
52
+ logic is covered by the native runs). The `.github/workflows/core-napi.yml`
53
+ workflow builds all six on every PR touching `rust/**`, and on a `core-napi-v*`
54
+ tag it builds then publishes.
55
+
56
+ Auth is **npm trusted publishing (OIDC)** — no tokens, matching the repo's
57
+ `publish-npm.yml`. Trusted publishing is configured *per package*, and a package
58
+ can't get a trusted publisher until it exists — so the seven packages
59
+ (the main package + six platform packages) need a **one-time manual bootstrap**
60
+ before automated releases work.
61
+
62
+ ### Prerequisite: the `@gitsheets` npm scope
63
+
64
+ The `@gitsheets` org/scope must exist on npmjs.com and the person running the
65
+ bootstrap must be a member with publish rights. (One-time human setup.)
66
+
67
+ ### One-time bootstrap (manual first publish, then configure trusted publishing)
68
+
69
+ The seven packages all start at an early version (currently `0.1.0`). They must
70
+ exist on npm before trusted publishing can be turned on.
71
+
72
+ 1. **Get the prebuilt binaries.** Run the `core-napi` workflow (open a PR
73
+ touching `rust/**`, or trigger `workflow_dispatch`) and download its six
74
+ `bindings-*` artifacts — they hold the `.node` for each platform. A single
75
+ machine can't build all six natively, so use the CI artifacts.
76
+
77
+ 2. **Publish all seven manually**, logged in as a `@gitsheets` org member
78
+ (`npm login`):
79
+
80
+ ```sh
81
+ cd rust/gitsheets-napi
82
+ npm install
83
+ npx napi artifacts --dir <downloaded-artifacts-dir> # → npm/<triple>/*.node
84
+ # platform packages first, then the main package:
85
+ for d in npm/*/ ; do ( cd "$d" && npm publish --access public ); done
86
+ npm publish --access public --ignore-scripts # main; skip the napi
87
+ # prepublish hook
88
+ ```
89
+
90
+ 3. **Turn on trusted publishing** on npmjs.com for **each** of the seven packages
91
+ → package Settings → Trusted Publisher → GitHub Actions, repo
92
+ `JarvusInnovations/gitsheets`, workflow `core-napi.yml`.
93
+
94
+ ### Releases (after bootstrap — fully automated, tokenless)
95
+
96
+ ```sh
97
+ git tag core-napi-v0.1.1 && git push origin core-napi-v0.1.1
98
+ ```
99
+
100
+ The tag drives the published version; CI builds all six platforms, then publishes
101
+ via OIDC (provenance). No secret needed. The `core-napi-v*` tag is the release
102
+ marker — napi runs with `--skip-gh-release` so it does **not** create a bare
103
+ `v<version>` GitHub release/tag (which would collide with the `gitsheets`
104
+ JS-package `v*` release namespace owned by `publish-npm.yml`).
105
+
106
+ **Version scheme.** `@gitsheets/core-napi` carries its own semver in its own
107
+ `core-napi-v*` tag namespace, decoupled from the `gitsheets` package's `v*` tags.
108
+ `packages/gitsheets` depends on it via a caret range (`^0.1.0`) so a compatible
109
+ addon release is picked up without a lockstep bump; widen the range deliberately
110
+ when the addon takes a breaking major.
111
+
112
+ To add or drop a platform later, edit `napi.triples.additional` +
113
+ `optionalDependencies` in `package.json`, run `npx napi create-npm-dir -t .`,
114
+ add the matching matrix entry in the workflow, and (since it's a new package)
115
+ bootstrap + trust that one package too.
package/binding.cjs ADDED
@@ -0,0 +1,178 @@
1
+ 'use strict';
2
+
3
+ // Thin JS surface over the napi addon. The addon (./index.js) marshals records
4
+ // with full type fidelity and throws *structured* errors (own `code`, `status`,
5
+ // `gitsheetsClass`, and `issues`/`conflictingPaths` payloads). This wrapper owns
6
+ // the one genuinely host-specific concern the Rust side can't: constructing real
7
+ // instances of the typed `GitsheetsError` subclasses from `specs/api/errors.md`
8
+ // so consumers can `instanceof` and switch on `err.code`.
9
+
10
+ const addon = require('./index.js');
11
+
12
+ class GitsheetsError extends Error {
13
+ constructor(message, { code, status, cause } = {}) {
14
+ super(message);
15
+ this.name = 'GitsheetsError';
16
+ this.code = code;
17
+ this.status = status;
18
+ if (cause !== undefined) this.cause = cause;
19
+ }
20
+ }
21
+
22
+ class ConfigError extends GitsheetsError {
23
+ constructor(message, opts) {
24
+ super(message, opts);
25
+ this.name = 'ConfigError';
26
+ }
27
+ }
28
+
29
+ class ValidationError extends GitsheetsError {
30
+ constructor(message, opts = {}) {
31
+ super(message, opts);
32
+ this.name = 'ValidationError';
33
+ this.issues = opts.issues ?? [];
34
+ }
35
+ }
36
+
37
+ class TransactionError extends GitsheetsError {
38
+ constructor(message, opts) {
39
+ super(message, opts);
40
+ this.name = 'TransactionError';
41
+ }
42
+ }
43
+
44
+ class IndexError extends GitsheetsError {
45
+ constructor(message, opts = {}) {
46
+ super(message, opts);
47
+ this.name = 'IndexError';
48
+ if (opts.conflictingPaths !== undefined) this.conflictingPaths = opts.conflictingPaths;
49
+ }
50
+ }
51
+
52
+ class RefError extends GitsheetsError {
53
+ constructor(message, opts) {
54
+ super(message, opts);
55
+ this.name = 'RefError';
56
+ }
57
+ }
58
+
59
+ class PathTemplateError extends GitsheetsError {
60
+ constructor(message, opts) {
61
+ super(message, opts);
62
+ this.name = 'PathTemplateError';
63
+ }
64
+ }
65
+
66
+ class NotFoundError extends GitsheetsError {
67
+ constructor(message, opts) {
68
+ super(message, opts);
69
+ this.name = 'NotFoundError';
70
+ }
71
+ }
72
+
73
+ const CLASS_BY_NAME = {
74
+ ConfigError,
75
+ ValidationError,
76
+ TransactionError,
77
+ IndexError,
78
+ RefError,
79
+ PathTemplateError,
80
+ NotFoundError,
81
+ };
82
+
83
+ // Map a structured error raised by the addon onto its typed class. The mapping
84
+ // keys off the `gitsheetsClass` discriminant the core sets — never the message.
85
+ function mapCoreError(raw) {
86
+ const Cls = CLASS_BY_NAME[raw && raw.gitsheetsClass] ?? GitsheetsError;
87
+ const opts = { code: raw.code, status: raw.status, cause: raw };
88
+ if (raw.issues !== undefined) opts.issues = raw.issues;
89
+ if (raw.conflictingPaths !== undefined) opts.conflictingPaths = raw.conflictingPaths;
90
+ return new Cls(raw.message, opts);
91
+ }
92
+
93
+ // Wrap an addon call so any structured core error surfaces as its typed class.
94
+ function wrap(fn) {
95
+ return (...args) => {
96
+ try {
97
+ return fn(...args);
98
+ } catch (raw) {
99
+ if (raw && typeof raw.gitsheetsClass === 'string') throw mapCoreError(raw);
100
+ throw raw;
101
+ }
102
+ };
103
+ }
104
+
105
+ module.exports = {
106
+ // Marshalling entry points (batch-first).
107
+ roundtrip: addon.roundtrip,
108
+ // Canonical TOML bytes-authority (batch-first). Parse/serialize can raise a
109
+ // structured core error (`config_invalid`), surfaced as its typed class.
110
+ parseRecords: wrap(addon.parseRecords),
111
+ serializeRecords: wrap(addon.serializeRecords),
112
+ // Definition logic (batch-first). Path rendering raises a typed
113
+ // PathTemplateError; schema compilation raises a typed ConfigError.
114
+ renderPathsBatch: wrap(addon.renderPathsBatch),
115
+ validateBatch: wrap(addon.validateBatch),
116
+ runComparator: wrap(addon.runComparator),
117
+ // Native locale array sort (declarative `sort = true`) — the ICU collator
118
+ // matching V8 `localeCompare`. Pure (no structured errors), so unwrapped.
119
+ collatorSort: addon.collatorSort,
120
+ // Stateful compiled definition (compile-once / reuse). Raw class — its
121
+ // structured errors carry `gitsheetsClass`; map with `mapCoreError` if needed.
122
+ CompiledDefinition: addon.CompiledDefinition,
123
+ // Record CRUD over the holo-tree substrate (batch-first). Substrate / record
124
+ // parse failures surface as typed core errors.
125
+ recordRead: wrap(addon.recordRead),
126
+ recordWrite: wrap(addon.recordWrite),
127
+ recordDelete: wrap(addon.recordDelete),
128
+ recordList: wrap(addon.recordList),
129
+ diffRecords: wrap(addon.diffRecords),
130
+ // Blob-write primitive: hash raw attachment bytes into the ODB. A substrate
131
+ // failure surfaces as a typed core error.
132
+ writeBlob: wrap(addon.writeBlob),
133
+ // Query traversal + filtering (batch-first): the template prunes the walk and
134
+ // the filter (equality / nested / `$pred` engine snippets) runs in the core.
135
+ recordQuery: wrap(addon.recordQuery),
136
+ recordQueryCandidates: wrap(addon.recordQueryCandidates),
137
+ templateFieldNames: wrap(addon.templateFieldNames),
138
+ // Secondary indexing (lazy, in-memory). A unique conflict surfaces as a typed
139
+ // IndexError(index_unique_conflict).
140
+ recordIndexUnique: wrap(addon.recordIndexUnique),
141
+ recordIndexMulti: wrap(addon.recordIndexMulti),
142
+ // Substrate (holo-tree) read/write counters — bulk benchmark instrumentation.
143
+ substrateStats: addon.substrateStats,
144
+ substrateReset: addon.substrateReset,
145
+ // Diff / patch primitives (RFC 6902 createPatch, RFC 7396 mergePatch).
146
+ createPatch: wrap(addon.createPatch),
147
+ applyMergePatch: wrap(addon.applyMergePatch),
148
+ // Orchestration: Sheet / Transaction / Store state machine (sheet-store-core).
149
+ // CoreTransaction is the stateful two-phase-protocol driver. Raw class — its
150
+ // methods throw structured errors carrying `gitsheetsClass`/`code`; map with
151
+ // `mapCoreError` where a typed instance is wanted.
152
+ CoreTransaction: addon.CoreTransaction,
153
+ coreDiscoverSheets: wrap(addon.coreDiscoverSheets),
154
+ coreCheckValidators: wrap(addon.coreCheckValidators),
155
+ // Markdown / mdx content-type codec. serialize/parse can raise a typed
156
+ // ValidationError/ConfigError; the H1 + normalize helpers are pure. Body
157
+ // NORMALIZATION is native — `markdownSerialize` runs the embedded
158
+ // `dprint-plugin-markdown` formatter (unless `normalize: false` is passed), and
159
+ // `markdownNormalizeBody` exposes it directly (see gitsheets_core::codec docs).
160
+ markdownSerialize: wrap(addon.markdownSerialize),
161
+ markdownParse: wrap(addon.markdownParse),
162
+ markdownParseHeaderOnly: wrap(addon.markdownParseHeaderOnly),
163
+ markdownExtractH1: addon.markdownExtractH1,
164
+ markdownRewriteH1: addon.markdownRewriteH1,
165
+ markdownNormalizeBody: addon.markdownNormalizeBody,
166
+ // Boundary-test entry: throws the typed class for a given stable code.
167
+ simulateCoreError: wrap(addon.simulateCoreError),
168
+ // Error machinery.
169
+ mapCoreError,
170
+ GitsheetsError,
171
+ ConfigError,
172
+ ValidationError,
173
+ TransactionError,
174
+ IndexError,
175
+ RefError,
176
+ PathTemplateError,
177
+ NotFoundError,
178
+ };
package/index.d.ts ADDED
@@ -0,0 +1,422 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ /**
7
+ * Round-trip a **batch** of records through the core, preserving full type
8
+ * fidelity. The whole array crosses the FFI once. This is the foundation's
9
+ * stand-in for the real batch engine entry points (`upsertMany`, `queryAll`,
10
+ * …), which keep this same batch-first signature.
11
+ */
12
+ export declare function roundtrip(records: Array<JsValue>): Array<JsValue>
13
+ /**
14
+ * Parse a **batch** of TOML documents into records, marshalled to JS with full
15
+ * type fidelity. The whole array crosses the FFI once (batch-first). A
16
+ * malformed document surfaces as a structured, typed core error (`config_invalid`).
17
+ */
18
+ export declare function parseRecords(documents: Array<string>): Array<JsValue>
19
+ /**
20
+ * Serialize a **batch** of records to their canonical TOML bytes in one call
21
+ * (deep key sort + `toml`-crate default formatting; see `gitsheets_core::canonical`).
22
+ * A value TOML can't represent surfaces as a structured, typed core error.
23
+ */
24
+ export declare function serializeRecords(records: Array<JsValue>): Array<string>
25
+ /**
26
+ * One JSON-Schema validation failure, marshalled to the `ValidationIssue`
27
+ * shape the host surface uses (`source`/`schemaPath`/`code`, with snake fields
28
+ * rendered camelCase by napi). Returned by [`validate_batch`].
29
+ */
30
+ export interface JsValidationIssue {
31
+ path: Array<string>
32
+ message: string
33
+ source: string
34
+ schemaPath?: string
35
+ code?: string
36
+ }
37
+ /**
38
+ * Render a path template against a **batch** of records, returning one path per
39
+ * record (no file extension; the caller appends `.toml`/`.md`). The template is
40
+ * parsed and its expression components compiled into the embedded engine
41
+ * **once**, then reused across the whole batch — the bulk path crosses the FFI
42
+ * a single time. A render failure surfaces as a structured, typed
43
+ * `PathTemplateError` (`path_render_failed` / `path_invalid_chars`). This is the
44
+ * path-template half of the `node:vm` parity gate.
45
+ */
46
+ export declare function renderPathsBatch(template: string, records: Array<JsValue>): Array<string>
47
+ /**
48
+ * Validate a **batch** of records against a JSON Schema, returning the issues
49
+ * for each record (an empty inner array ⇒ that record is valid). The schema is
50
+ * compiled **once**, then reused across the batch. Unlike the host's throwing
51
+ * `validateRecord`, this returns issues per record so a parity harness can diff
52
+ * the full pass/fail + path/keyword picture against `ajv`. A schema that won't
53
+ * compile surfaces as a structured, typed `ConfigError` (`config_invalid`).
54
+ */
55
+ export declare function validateBatch(schema: JsValue, records: Array<JsValue>): Array<Array<JsValidationIssue>>
56
+ /**
57
+ * Compile a raw-JS sort comparator (`rule`, the body of `(a, b) => { … }`) and
58
+ * run it once against `a`/`b`, returning its numeric result. The direct
59
+ * comparator-parity entry point: a harness asserts this equals the same rule
60
+ * run through `node:vm` for identical inputs.
61
+ */
62
+ export declare function runComparator(rule: string, a: JsValue, b: JsValue): number
63
+ /**
64
+ * Native locale array sort — the declarative `sort = true` path. Sorts the
65
+ * strings with the ICU collator that matches V8's `localeCompare(b, undefined,
66
+ * { sensitivity: 'base', ignorePunctuation: true, numeric: true })`. This is the
67
+ * exact function `Sheet::normalize_record` applies for a `sort = true` field, and
68
+ * it does **NOT** route through the boa engine (boa lacks `Intl`, so its
69
+ * `localeCompare` falls back to code-unit order and diverges from `node:vm` on
70
+ * non-ASCII / mixed-case input). Exposed so a `node --test` harness asserts
71
+ * byte-exact parity against `node`'s `localeCompare` — see
72
+ * `test/collator-parity.mjs`.
73
+ */
74
+ export declare function collatorSort(strings: Array<string>): Array<string>
75
+ /**
76
+ * Read a batch of records by path. Each result is the record (a plain object
77
+ * with full TOML type fidelity) or `null` when no blob lives at that path.
78
+ */
79
+ export declare function recordRead(gitDir: string, treeRef: string, base: string, paths: Array<string>, extension?: string | undefined | null): Array<JsValue | undefined | null>
80
+ /**
81
+ * The result of a `record_write`/`record_delete`: the new root tree hash plus
82
+ * per-record output (`blobHashes` for writes, `existed` for deletes).
83
+ */
84
+ export interface JsWriteOutcome {
85
+ treeHash: string
86
+ blobHashes: Array<string>
87
+ }
88
+ /** The result of a `record_delete`. */
89
+ export interface JsDeleteOutcome {
90
+ treeHash: string
91
+ existed: Array<boolean>
92
+ }
93
+ /**
94
+ * Write a batch of records (canonical TOML) under `base`, starting from the
95
+ * tree `baseRef`. `paths[i]` is written from `records[i]`. Returns the new root
96
+ * tree hash + each written blob hash. The bytes are produced by the core's
97
+ * bytes-authority, so two bindings writing the same record agree byte-for-byte.
98
+ */
99
+ export declare function recordWrite(gitDir: string, baseRef: string, base: string, paths: Array<string>, records: Array<JsValue>, extension?: string | undefined | null): JsWriteOutcome
100
+ /**
101
+ * Delete a batch of records by path under `base`, starting from `baseRef`.
102
+ * `existed[i]` reports whether `paths[i]` was actually present.
103
+ */
104
+ export declare function recordDelete(gitDir: string, baseRef: string, base: string, paths: Array<string>, extension?: string | undefined | null): JsDeleteOutcome
105
+ /**
106
+ * One record from `record_list`: its path (relative to `base`, no extension)
107
+ * and parsed value.
108
+ */
109
+ export interface JsRecordEntry {
110
+ path: string
111
+ record: JsValue
112
+ }
113
+ /**
114
+ * List every record under `base` in the tree `treeRef`, in sorted
115
+ * (git-canonical) path order.
116
+ */
117
+ export declare function recordList(gitDir: string, treeRef: string, base: string, extension?: string | undefined | null): Array<JsRecordEntry>
118
+ /**
119
+ * Hash raw bytes into the object database as a loose blob, returning its git
120
+ * blob hash — the core's blob-write primitive (replaces the JS package's direct
121
+ * `@hologit/holo-tree` `writeBlob`). Used by the CLI/host to hash a binary
122
+ * attachment before staging it into a record's attachment tree with
123
+ * `CoreTransaction.setAttachment(s)`. The hash is a pure function of the bytes,
124
+ * so Node and Python hashing the same content produce the same object.
125
+ */
126
+ export declare function writeBlob(gitDir: string, content: Buffer): string
127
+ /**
128
+ * A snapshot of holo-tree's process-wide tree/blob counters — read-side
129
+ * instrumentation for the bulk benchmark and the hologit#464 perf finding.
130
+ */
131
+ export interface JsSubstrateStats {
132
+ treesRead: number
133
+ treesWritten: number
134
+ treesSkippedClean: number
135
+ cacheHits: number
136
+ cacheMisses: number
137
+ blobsRead: number
138
+ }
139
+ /** Snapshot the substrate (holo-tree) counters. */
140
+ export declare function substrateStats(): JsSubstrateStats
141
+ /** Reset the substrate counters + the thread-local tree cache. */
142
+ export declare function substrateReset(): void
143
+ /**
144
+ * Query records under `base` in the tree `treeRef`, returning each matched
145
+ * `{ path, record }` in sorted path order. The template prunes the walk; the
146
+ * filter (equality / nested / `$pred` snippets) is applied to each candidate.
147
+ * Batch-first: the whole query crosses the FFI once.
148
+ */
149
+ export declare function recordQuery(gitDir: string, treeRef: string, base: string, template: string, filter: object, extension?: string | undefined | null): Array<JsRecordEntry>
150
+ /**
151
+ * The pruning candidate set alone (no content filter applied) — the direct
152
+ * parity target for the host `Template.queryTree`. `query` is a (partial)
153
+ * record of the path-template input fields. Returns candidate record paths
154
+ * (relative to `base`, no extension) in sorted order.
155
+ */
156
+ export declare function recordQueryCandidates(gitDir: string, treeRef: string, base: string, template: string, query: JsValue, extension?: string | undefined | null): Array<string>
157
+ /**
158
+ * The record fields that contribute to rendering `template` — the query
159
+ * auto-derivation set (`Template.getFieldNames` parity). Insertion-ordered,
160
+ * de-duplicated; expression components contribute a best-effort identifier
161
+ * scan minus JS keywords/globals.
162
+ */
163
+ export declare function templateFieldNames(template: string): Array<string>
164
+ /**
165
+ * Build a **unique** index over the records under `base` and look up each key.
166
+ * `results[i]` is the record for `keys[i]`, or `null` when no record carries
167
+ * it. A duplicate key throws `IndexError(index_unique_conflict)` naming both
168
+ * paths.
169
+ */
170
+ export declare function recordIndexUnique(gitDir: string, treeRef: string, base: string, keySnippet: string, keys: Array<string>, extension?: string | undefined | null): Array<JsValue | undefined | null>
171
+ /**
172
+ * Build a **non-unique** index over the records under `base` and look up each
173
+ * key. `results[i]` is every record carrying `keys[i]` (an empty array when
174
+ * none).
175
+ */
176
+ export declare function recordIndexMulti(gitDir: string, treeRef: string, base: string, keySnippet: string, keys: Array<string>, extension?: string | undefined | null): Array<Array<JsValue>>
177
+ /**
178
+ * Generate an RFC 6902 JSON Patch transforming `src` into `dst` — the core's
179
+ * `createPatch`, matching the `rfc6902` package op-for-op. `null`/`undefined`
180
+ * on either side is the JSON null `Sheet.diffFrom` passes for an added
181
+ * (`src=null`) or deleted (`dst=null`) record.
182
+ */
183
+ export declare function createPatch(src?: JsValue | undefined | null, dst?: JsValue | undefined | null): object
184
+ /**
185
+ * Apply an RFC 7396 JSON Merge Patch to `target` (`null` ⇒ absent), returning
186
+ * the merged record — the core's `mergePatch` behind `Sheet.patch`. A `null`
187
+ * in the patch deletes that key; an object merges recursively; anything else
188
+ * replaces wholesale. Returns `null` only when the patch deletes the record
189
+ * outright.
190
+ */
191
+ export declare function applyMergePatch(target: JsValue | undefined | null, patch: JsMergePatch): JsValue | null
192
+ /**
193
+ * Diff records between two trees (`srcRef` → `dstRef`) under `base`, returning
194
+ * one entry per change with status, src/dst blob hashes, the parsed src/dst
195
+ * records, and the RFC 6902 patch — the full `Sheet.diffFrom({records,
196
+ * patches})` payload, computed in the core. `srcRef` may be the empty-tree hash
197
+ * for a from-scratch diff.
198
+ */
199
+ export declare function diffRecords(gitDir: string, srcRef: string, dstRef: string, base: string, extension?: string | undefined | null): object
200
+ /**
201
+ * Throw the core error for a given stable `code`, surfaced as a **structured,
202
+ * matchable** JS error (own `code`, `status`, `gitsheetsClass`, and any
203
+ * `issues`/`conflictingPaths`). Boundary-test entry point: it exercises the
204
+ * error-variant → typed-class mapping without standing up real engine paths.
205
+ */
206
+ export declare function simulateCoreError(code: string): void
207
+ /** Commit identity for the JS surface. */
208
+ export interface JsAuthor {
209
+ name: string
210
+ email: string
211
+ }
212
+ /** One ordered commit trailer (`Key: value`). */
213
+ export interface JsTrailer {
214
+ key: string
215
+ value: string
216
+ }
217
+ /**
218
+ * Options for opening a [`CoreTransaction`]. `timeSeconds` / `offsetMinutes`
219
+ * carry the host clock + local-timezone offset (a host concern), exactly as the
220
+ * JS `commitTreeWithRepo` computes them today.
221
+ */
222
+ export interface JsTransactionOptions {
223
+ parent?: string
224
+ branch?: string
225
+ author?: JsAuthor
226
+ committer?: JsAuthor
227
+ message: string
228
+ trailers?: Array<JsTrailer>
229
+ timeSeconds: number
230
+ offsetMinutes: number
231
+ }
232
+ /**
233
+ * The outcome of [`CoreTransaction::finalize`]. A no-op (no mutation, or the
234
+ * resulting tree equals the parent's) returns `commitHash = null`.
235
+ */
236
+ export interface JsTransactionResult {
237
+ commitHash?: string
238
+ treeHash?: string
239
+ refName?: string
240
+ parentCommitHash?: string
241
+ }
242
+ /** The outcome of [`CoreTransaction::stage_upsert`]. */
243
+ export interface JsStageOutcome {
244
+ blobHash: string
245
+ path: string
246
+ }
247
+ /**
248
+ * One attachment entry from `CoreTransaction.getAttachments`: its filename and
249
+ * blob hash (name → hash).
250
+ */
251
+ export interface JsAttachmentEntry {
252
+ name: string
253
+ hash: string
254
+ }
255
+ /**
256
+ * Discover every sheet declared in `<openRoot>/.gitsheets/*.toml` in the tree
257
+ * `treeRef`. Sorted bare names. The `Store` discovery half (`openStore`).
258
+ */
259
+ export declare function coreDiscoverSheets(gitDir: string, treeRef: string, openRoot: string): Array<string>
260
+ /**
261
+ * The `openStore` `config_missing` check: every validator must name a declared
262
+ * sheet. Throws `ConfigError(config_missing)` otherwise.
263
+ */
264
+ export declare function coreCheckValidators(declared: Array<string>, validatorNames: Array<string>): void
265
+ /**
266
+ * Serialize a record to its on-disk markdown bytes (`+++` frontmatter + body),
267
+ * enforcing the title-from-H1 invariant when `titleField` is set. The body is
268
+ * normalized natively by the embedded `dprint-plugin-markdown` formatter unless
269
+ * `normalize` is `false` (then it is framed verbatim). A non-string body or a
270
+ * title that disagrees with the body's (normalized) H1 throws a typed
271
+ * `ValidationError`.
272
+ */
273
+ export declare function markdownSerialize(record: JsValue, bodyField: string, titleField?: string | undefined | null, normalize?: boolean | undefined | null): string
274
+ /**
275
+ * Normalize a markdown body with the native `dprint-plugin-markdown` formatter
276
+ * (the pinned aggressive `textWrap: never` config). Deterministic + idempotent.
277
+ * Exposed so the boundary suite can assert the normalizer directly.
278
+ */
279
+ export declare function markdownNormalizeBody(body: string): string
280
+ /**
281
+ * Parse on-disk markdown bytes into a full record (frontmatter fields + the
282
+ * body under `bodyField`). Mirrors `markdownFormat.parse`.
283
+ */
284
+ export declare function markdownParse(text: string, bodyField: string, titleField?: string | undefined | null): JsValue
285
+ /**
286
+ * Parse only the frontmatter — the lazy-body path. The body field is absent in
287
+ * the returned record. Mirrors `markdownFormat.parseHeaderOnly`.
288
+ */
289
+ export declare function markdownParseHeaderOnly(text: string, bodyField: string): JsValue
290
+ /**
291
+ * Extract the first ATX-style H1 from a markdown body, or `null` if absent.
292
+ * Mirrors `extractFirstH1`.
293
+ */
294
+ export declare function markdownExtractH1(body: string): string | null
295
+ /**
296
+ * Rewrite (or prepend) the first ATX H1 of a markdown body to `title`. Mirrors
297
+ * `rewriteLeadingH1` — the `Sheet.patch` title-reconciliation helper.
298
+ */
299
+ export declare function markdownRewriteH1(body: string, title: string): string
300
+ /**
301
+ * A compiled sheet definition: the embedded engine plus the definition's
302
+ * path template (and optional raw-JS sort comparator), **compiled once** at
303
+ * construction and reused across every method call. Holds a `!Send` boa
304
+ * context, so it is constructed and used on its owning JS thread — the
305
+ * thread-confinement the spec requires. Exists to demonstrate the
306
+ * compile-once-per-open / reuse-across-operations contract from JS.
307
+ */
308
+ export declare class CompiledDefinition {
309
+ /**
310
+ * Compile a definition once: parse the path template (compiling its
311
+ * expression snippets into the engine) and, if given, compile a raw-JS sort
312
+ * comparator. All snippet compilation happens here — never per operation.
313
+ */
314
+ constructor(pathTemplate: string, sortRule?: string | undefined | null)
315
+ /** Render one record's path using the already-compiled template. */
316
+ renderPath(record: JsValue): string
317
+ /**
318
+ * Run the compiled sort comparator on two values, returning its numeric
319
+ * result. Errors if the definition was built without a sort rule.
320
+ */
321
+ compare(a: JsValue, b: JsValue): number
322
+ /**
323
+ * How many snippets were compiled into this definition's engine. Constant
324
+ * across operations — proof that compilation happened once at construction,
325
+ * not per `render_path` / `compare` call.
326
+ */
327
+ snippetCount(): number
328
+ }
329
+ /**
330
+ * A live transaction the JS boundary suite drives. Holds a `!Send`
331
+ * `Transaction` (repo + private tree) and the opened `Sheet`s, so it is
332
+ * constructed and used on its owning JS thread — the thread-confinement the
333
+ * spec requires.
334
+ */
335
+ export declare class CoreTransaction {
336
+ /**
337
+ * Open a transaction against `gitDir`. Resolves the parent/branch, builds
338
+ * the private tree, and acquires the single-writer slot. A concurrent open
339
+ * on the same repo throws `TransactionError(transaction_in_progress)`.
340
+ */
341
+ static begin(gitDir: string, opts: JsTransactionOptions): CoreTransaction
342
+ /**
343
+ * Open a sheet against this transaction's tree (config read + template /
344
+ * schema / sort comparators compiled once).
345
+ */
346
+ openSheet(name: string, configPath: string, openRoot: string, prefix: string): void
347
+ /**
348
+ * Phase 1 of the two-phase protocol *(non-mutating)*. Returns the candidate
349
+ * (`path`, `nextText`, and the normalized `record`) for the host validator;
350
+ * the candidate is stashed for a subsequent `stageUpsert`. A JSON-Schema
351
+ * rejection throws `ValidationError` here, before any bytes are written.
352
+ */
353
+ prepareUpsert(name: string, record: JsValue, previousPath?: string | undefined | null, allowMissingBody?: boolean | undefined | null): object
354
+ /**
355
+ * Phase 3 *(mutating)*: write the stashed candidate from the last
356
+ * `prepareUpsert` for `name`. Marks the transaction mutated.
357
+ */
358
+ stageUpsert(name: string): JsStageOutcome
359
+ /**
360
+ * Pre-flight idempotency check (`Sheet.willChange`) — same phase-1 pipeline,
361
+ * then a byte comparison to the existing blob. Non-mutating.
362
+ */
363
+ willChange(name: string, record: JsValue, previousPath?: string | undefined | null, allowMissingBody?: boolean | undefined | null): object
364
+ /**
365
+ * Delete a record by its sheet-relative path *(mutating)*. Throws
366
+ * `NotFoundError(record_not_found)` when absent.
367
+ */
368
+ delete(name: string, recordPath: string): void
369
+ /** `Sheet.clear` *(mutating)* — empties the sheet's data subtree. */
370
+ clear(name: string): void
371
+ /**
372
+ * Stage attachments for a record *(mutating)*: place each `name → blobHash`
373
+ * at `<recordPath>/<name>` in this transaction's live tree, so the record
374
+ * and its attachments commit atomically. Write the blob first with the
375
+ * top-level `writeBlob`. Marks the transaction mutated.
376
+ */
377
+ setAttachments(name: string, recordPath: string, attachments: Record<string, string>): void
378
+ /** Stage a single attachment *(mutating)* — sugar over [`Self::set_attachments`]. */
379
+ setAttachment(name: string, recordPath: string, attachmentName: string, blobHash: string): void
380
+ /**
381
+ * Stage a raw text file at `path` in this transaction's private tree
382
+ * *(mutating)* — the generic file write the CLI uses to commit sheet-config
383
+ * edits (`init` / `infer` / `migrate-config`) atomically alongside nothing
384
+ * else. `path` is repo-root-relative. Returns the written blob hash.
385
+ */
386
+ writeFile(path: string, content: string): string
387
+ /**
388
+ * The blob-hash map of a record's attachments (`name → hash`, sorted by
389
+ * name), or `null` when the record has no attachment directory. Read-only.
390
+ */
391
+ getAttachments(name: string, recordPath: string): Array<JsAttachmentEntry> | null
392
+ /** The blob hash of a single named attachment, or `null` if absent. Read-only. */
393
+ getAttachment(name: string, recordPath: string, attachmentName: string): string | null
394
+ /**
395
+ * Remove a single named attachment *(mutating)*. Throws
396
+ * `NotFoundError(record_not_found)` when it doesn't exist. Marks mutated.
397
+ */
398
+ deleteAttachment(name: string, recordPath: string, attachmentName: string): void
399
+ /**
400
+ * Remove all attachments for a record *(mutating)*. A no-op when the record
401
+ * has no attachment directory — in that case the transaction is NOT marked
402
+ * mutated (so an otherwise-empty transaction still finalizes to no commit).
403
+ * Returns whether anything was removed.
404
+ */
405
+ deleteAttachments(name: string, recordPath: string): boolean
406
+ /**
407
+ * List every record under the sheet's base, decoded through the format
408
+ * codec, in sorted path order. `withBody` is the lazy-body switch for
409
+ * markdown sheets (`false` omits the body field); a no-op for TOML sheets.
410
+ * Read-only — does not mark the transaction mutated.
411
+ */
412
+ list(name: string, withBody: boolean): Array<JsRecordEntry>
413
+ /** The parent commit hash captured at open (null on a fresh repo). */
414
+ parentCommitHash(): string | null
415
+ /**
416
+ * Finalize: commit-on-success-only with no-op detection + `parent_moved`
417
+ * re-check + CAS ref movement. Consumes the transaction.
418
+ */
419
+ finalize(): JsTransactionResult
420
+ /** Discard without committing (handler threw). Releases the writer slot. */
421
+ discard(): void
422
+ }
package/index.js ADDED
@@ -0,0 +1,347 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /* prettier-ignore */
4
+
5
+ /* auto-generated by NAPI-RS */
6
+
7
+ const { existsSync, readFileSync } = require('fs')
8
+ const { join } = require('path')
9
+
10
+ const { platform, arch } = process
11
+
12
+ let nativeBinding = null
13
+ let localFileExisted = false
14
+ let loadError = null
15
+
16
+ function isMusl() {
17
+ // For Node 10
18
+ if (!process.report || typeof process.report.getReport !== 'function') {
19
+ try {
20
+ const lddPath = require('child_process').execSync('which ldd').toString().trim()
21
+ return readFileSync(lddPath, 'utf8').includes('musl')
22
+ } catch (e) {
23
+ return true
24
+ }
25
+ } else {
26
+ const { glibcVersionRuntime } = process.report.getReport().header
27
+ return !glibcVersionRuntime
28
+ }
29
+ }
30
+
31
+ switch (platform) {
32
+ case 'android':
33
+ switch (arch) {
34
+ case 'arm64':
35
+ localFileExisted = existsSync(join(__dirname, 'gitsheets-core.android-arm64.node'))
36
+ try {
37
+ if (localFileExisted) {
38
+ nativeBinding = require('./gitsheets-core.android-arm64.node')
39
+ } else {
40
+ nativeBinding = require('@gitsheets/core-napi-android-arm64')
41
+ }
42
+ } catch (e) {
43
+ loadError = e
44
+ }
45
+ break
46
+ case 'arm':
47
+ localFileExisted = existsSync(join(__dirname, 'gitsheets-core.android-arm-eabi.node'))
48
+ try {
49
+ if (localFileExisted) {
50
+ nativeBinding = require('./gitsheets-core.android-arm-eabi.node')
51
+ } else {
52
+ nativeBinding = require('@gitsheets/core-napi-android-arm-eabi')
53
+ }
54
+ } catch (e) {
55
+ loadError = e
56
+ }
57
+ break
58
+ default:
59
+ throw new Error(`Unsupported architecture on Android ${arch}`)
60
+ }
61
+ break
62
+ case 'win32':
63
+ switch (arch) {
64
+ case 'x64':
65
+ localFileExisted = existsSync(
66
+ join(__dirname, 'gitsheets-core.win32-x64-msvc.node')
67
+ )
68
+ try {
69
+ if (localFileExisted) {
70
+ nativeBinding = require('./gitsheets-core.win32-x64-msvc.node')
71
+ } else {
72
+ nativeBinding = require('@gitsheets/core-napi-win32-x64-msvc')
73
+ }
74
+ } catch (e) {
75
+ loadError = e
76
+ }
77
+ break
78
+ case 'ia32':
79
+ localFileExisted = existsSync(
80
+ join(__dirname, 'gitsheets-core.win32-ia32-msvc.node')
81
+ )
82
+ try {
83
+ if (localFileExisted) {
84
+ nativeBinding = require('./gitsheets-core.win32-ia32-msvc.node')
85
+ } else {
86
+ nativeBinding = require('@gitsheets/core-napi-win32-ia32-msvc')
87
+ }
88
+ } catch (e) {
89
+ loadError = e
90
+ }
91
+ break
92
+ case 'arm64':
93
+ localFileExisted = existsSync(
94
+ join(__dirname, 'gitsheets-core.win32-arm64-msvc.node')
95
+ )
96
+ try {
97
+ if (localFileExisted) {
98
+ nativeBinding = require('./gitsheets-core.win32-arm64-msvc.node')
99
+ } else {
100
+ nativeBinding = require('@gitsheets/core-napi-win32-arm64-msvc')
101
+ }
102
+ } catch (e) {
103
+ loadError = e
104
+ }
105
+ break
106
+ default:
107
+ throw new Error(`Unsupported architecture on Windows: ${arch}`)
108
+ }
109
+ break
110
+ case 'darwin':
111
+ localFileExisted = existsSync(join(__dirname, 'gitsheets-core.darwin-universal.node'))
112
+ try {
113
+ if (localFileExisted) {
114
+ nativeBinding = require('./gitsheets-core.darwin-universal.node')
115
+ } else {
116
+ nativeBinding = require('@gitsheets/core-napi-darwin-universal')
117
+ }
118
+ break
119
+ } catch {}
120
+ switch (arch) {
121
+ case 'x64':
122
+ localFileExisted = existsSync(join(__dirname, 'gitsheets-core.darwin-x64.node'))
123
+ try {
124
+ if (localFileExisted) {
125
+ nativeBinding = require('./gitsheets-core.darwin-x64.node')
126
+ } else {
127
+ nativeBinding = require('@gitsheets/core-napi-darwin-x64')
128
+ }
129
+ } catch (e) {
130
+ loadError = e
131
+ }
132
+ break
133
+ case 'arm64':
134
+ localFileExisted = existsSync(
135
+ join(__dirname, 'gitsheets-core.darwin-arm64.node')
136
+ )
137
+ try {
138
+ if (localFileExisted) {
139
+ nativeBinding = require('./gitsheets-core.darwin-arm64.node')
140
+ } else {
141
+ nativeBinding = require('@gitsheets/core-napi-darwin-arm64')
142
+ }
143
+ } catch (e) {
144
+ loadError = e
145
+ }
146
+ break
147
+ default:
148
+ throw new Error(`Unsupported architecture on macOS: ${arch}`)
149
+ }
150
+ break
151
+ case 'freebsd':
152
+ if (arch !== 'x64') {
153
+ throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
154
+ }
155
+ localFileExisted = existsSync(join(__dirname, 'gitsheets-core.freebsd-x64.node'))
156
+ try {
157
+ if (localFileExisted) {
158
+ nativeBinding = require('./gitsheets-core.freebsd-x64.node')
159
+ } else {
160
+ nativeBinding = require('@gitsheets/core-napi-freebsd-x64')
161
+ }
162
+ } catch (e) {
163
+ loadError = e
164
+ }
165
+ break
166
+ case 'linux':
167
+ switch (arch) {
168
+ case 'x64':
169
+ if (isMusl()) {
170
+ localFileExisted = existsSync(
171
+ join(__dirname, 'gitsheets-core.linux-x64-musl.node')
172
+ )
173
+ try {
174
+ if (localFileExisted) {
175
+ nativeBinding = require('./gitsheets-core.linux-x64-musl.node')
176
+ } else {
177
+ nativeBinding = require('@gitsheets/core-napi-linux-x64-musl')
178
+ }
179
+ } catch (e) {
180
+ loadError = e
181
+ }
182
+ } else {
183
+ localFileExisted = existsSync(
184
+ join(__dirname, 'gitsheets-core.linux-x64-gnu.node')
185
+ )
186
+ try {
187
+ if (localFileExisted) {
188
+ nativeBinding = require('./gitsheets-core.linux-x64-gnu.node')
189
+ } else {
190
+ nativeBinding = require('@gitsheets/core-napi-linux-x64-gnu')
191
+ }
192
+ } catch (e) {
193
+ loadError = e
194
+ }
195
+ }
196
+ break
197
+ case 'arm64':
198
+ if (isMusl()) {
199
+ localFileExisted = existsSync(
200
+ join(__dirname, 'gitsheets-core.linux-arm64-musl.node')
201
+ )
202
+ try {
203
+ if (localFileExisted) {
204
+ nativeBinding = require('./gitsheets-core.linux-arm64-musl.node')
205
+ } else {
206
+ nativeBinding = require('@gitsheets/core-napi-linux-arm64-musl')
207
+ }
208
+ } catch (e) {
209
+ loadError = e
210
+ }
211
+ } else {
212
+ localFileExisted = existsSync(
213
+ join(__dirname, 'gitsheets-core.linux-arm64-gnu.node')
214
+ )
215
+ try {
216
+ if (localFileExisted) {
217
+ nativeBinding = require('./gitsheets-core.linux-arm64-gnu.node')
218
+ } else {
219
+ nativeBinding = require('@gitsheets/core-napi-linux-arm64-gnu')
220
+ }
221
+ } catch (e) {
222
+ loadError = e
223
+ }
224
+ }
225
+ break
226
+ case 'arm':
227
+ if (isMusl()) {
228
+ localFileExisted = existsSync(
229
+ join(__dirname, 'gitsheets-core.linux-arm-musleabihf.node')
230
+ )
231
+ try {
232
+ if (localFileExisted) {
233
+ nativeBinding = require('./gitsheets-core.linux-arm-musleabihf.node')
234
+ } else {
235
+ nativeBinding = require('@gitsheets/core-napi-linux-arm-musleabihf')
236
+ }
237
+ } catch (e) {
238
+ loadError = e
239
+ }
240
+ } else {
241
+ localFileExisted = existsSync(
242
+ join(__dirname, 'gitsheets-core.linux-arm-gnueabihf.node')
243
+ )
244
+ try {
245
+ if (localFileExisted) {
246
+ nativeBinding = require('./gitsheets-core.linux-arm-gnueabihf.node')
247
+ } else {
248
+ nativeBinding = require('@gitsheets/core-napi-linux-arm-gnueabihf')
249
+ }
250
+ } catch (e) {
251
+ loadError = e
252
+ }
253
+ }
254
+ break
255
+ case 'riscv64':
256
+ if (isMusl()) {
257
+ localFileExisted = existsSync(
258
+ join(__dirname, 'gitsheets-core.linux-riscv64-musl.node')
259
+ )
260
+ try {
261
+ if (localFileExisted) {
262
+ nativeBinding = require('./gitsheets-core.linux-riscv64-musl.node')
263
+ } else {
264
+ nativeBinding = require('@gitsheets/core-napi-linux-riscv64-musl')
265
+ }
266
+ } catch (e) {
267
+ loadError = e
268
+ }
269
+ } else {
270
+ localFileExisted = existsSync(
271
+ join(__dirname, 'gitsheets-core.linux-riscv64-gnu.node')
272
+ )
273
+ try {
274
+ if (localFileExisted) {
275
+ nativeBinding = require('./gitsheets-core.linux-riscv64-gnu.node')
276
+ } else {
277
+ nativeBinding = require('@gitsheets/core-napi-linux-riscv64-gnu')
278
+ }
279
+ } catch (e) {
280
+ loadError = e
281
+ }
282
+ }
283
+ break
284
+ case 's390x':
285
+ localFileExisted = existsSync(
286
+ join(__dirname, 'gitsheets-core.linux-s390x-gnu.node')
287
+ )
288
+ try {
289
+ if (localFileExisted) {
290
+ nativeBinding = require('./gitsheets-core.linux-s390x-gnu.node')
291
+ } else {
292
+ nativeBinding = require('@gitsheets/core-napi-linux-s390x-gnu')
293
+ }
294
+ } catch (e) {
295
+ loadError = e
296
+ }
297
+ break
298
+ default:
299
+ throw new Error(`Unsupported architecture on Linux: ${arch}`)
300
+ }
301
+ break
302
+ default:
303
+ throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
304
+ }
305
+
306
+ if (!nativeBinding) {
307
+ if (loadError) {
308
+ throw loadError
309
+ }
310
+ throw new Error(`Failed to load native binding`)
311
+ }
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
314
+
315
+ module.exports.roundtrip = roundtrip
316
+ module.exports.parseRecords = parseRecords
317
+ module.exports.serializeRecords = serializeRecords
318
+ module.exports.renderPathsBatch = renderPathsBatch
319
+ module.exports.validateBatch = validateBatch
320
+ module.exports.runComparator = runComparator
321
+ module.exports.collatorSort = collatorSort
322
+ module.exports.CompiledDefinition = CompiledDefinition
323
+ module.exports.recordRead = recordRead
324
+ module.exports.recordWrite = recordWrite
325
+ module.exports.recordDelete = recordDelete
326
+ module.exports.recordList = recordList
327
+ module.exports.writeBlob = writeBlob
328
+ module.exports.substrateStats = substrateStats
329
+ module.exports.substrateReset = substrateReset
330
+ module.exports.recordQuery = recordQuery
331
+ module.exports.recordQueryCandidates = recordQueryCandidates
332
+ module.exports.templateFieldNames = templateFieldNames
333
+ module.exports.recordIndexUnique = recordIndexUnique
334
+ module.exports.recordIndexMulti = recordIndexMulti
335
+ module.exports.createPatch = createPatch
336
+ module.exports.applyMergePatch = applyMergePatch
337
+ module.exports.diffRecords = diffRecords
338
+ module.exports.simulateCoreError = simulateCoreError
339
+ module.exports.CoreTransaction = CoreTransaction
340
+ module.exports.coreDiscoverSheets = coreDiscoverSheets
341
+ module.exports.coreCheckValidators = coreCheckValidators
342
+ module.exports.markdownSerialize = markdownSerialize
343
+ module.exports.markdownNormalizeBody = markdownNormalizeBody
344
+ module.exports.markdownParse = markdownParse
345
+ module.exports.markdownParseHeaderOnly = markdownParseHeaderOnly
346
+ module.exports.markdownExtractH1 = markdownExtractH1
347
+ module.exports.markdownRewriteH1 = markdownRewriteH1
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@gitsheets/core-napi",
3
+ "version": "0.1.0",
4
+ "description": "Node.js native binding for gitsheets-core — the FFI marshalling boundary.",
5
+ "main": "binding.cjs",
6
+ "types": "index.d.ts",
7
+ "license": "Apache-2.0",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/JarvusInnovations/gitsheets.git",
11
+ "directory": "rust/gitsheets-napi"
12
+ },
13
+ "engines": {
14
+ "node": ">=20"
15
+ },
16
+ "napi": {
17
+ "name": "gitsheets-core",
18
+ "triples": {
19
+ "defaults": false,
20
+ "additional": [
21
+ "x86_64-unknown-linux-gnu",
22
+ "aarch64-unknown-linux-gnu",
23
+ "x86_64-unknown-linux-musl",
24
+ "aarch64-apple-darwin",
25
+ "x86_64-apple-darwin",
26
+ "x86_64-pc-windows-msvc"
27
+ ]
28
+ }
29
+ },
30
+ "files": [
31
+ "binding.cjs",
32
+ "index.js",
33
+ "index.d.ts"
34
+ ],
35
+ "optionalDependencies": {
36
+ "@gitsheets/core-napi-linux-x64-gnu": "0.1.0",
37
+ "@gitsheets/core-napi-linux-arm64-gnu": "0.1.0",
38
+ "@gitsheets/core-napi-linux-x64-musl": "0.1.0",
39
+ "@gitsheets/core-napi-darwin-arm64": "0.1.0",
40
+ "@gitsheets/core-napi-darwin-x64": "0.1.0",
41
+ "@gitsheets/core-napi-win32-x64-msvc": "0.1.0"
42
+ },
43
+ "scripts": {
44
+ "artifacts": "napi artifacts",
45
+ "build": "napi build --platform --release",
46
+ "build:debug": "napi build --platform",
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",
49
+ "bench": "node bench/query-bench.mjs",
50
+ "version": "napi version"
51
+ },
52
+ "devDependencies": {
53
+ "@napi-rs/cli": "^2.18.4",
54
+ "ajv": "^8.20.0",
55
+ "ajv-formats": "^3.0.1",
56
+ "rfc6902": "^5.2.0",
57
+ "smol-toml": "^1.7.0"
58
+ }
59
+ }