marcidb-client 0.3.1 → 0.6.1

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 CHANGED
@@ -1,85 +1,85 @@
1
- # marci-db
2
-
3
- TypeScript client for [MarciDB](https://github.com/den59k/marci-db.git) — a NoSQL database with a schema-first approach.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install marcidb-client
9
- # or
10
- bun add marcidb-client
11
- ```
12
-
13
- ## Setup
14
-
15
- 1. Create a `schema.marci` file in your project root:
16
-
17
- ```
18
- model User {
19
- name String
20
- posts Post[] @bind(Post.author)
21
- }
22
-
23
- model Post {
24
- title String
25
- author User?
26
- }
27
- ```
28
-
29
- 2. Generate the client:
30
-
31
- ```bash
32
- marcidb generate
33
- # or with custom paths
34
- marcidb generate schema.marci node_modules/.marcidb/client
35
- ```
36
-
37
- 3. Start your MarciDB server and connect:
38
-
39
- ```typescript
40
- import { marcidb } from "marcidb-client";
41
-
42
- const db = marcidb("http://localhost:3000");
43
- ```
44
-
45
- ## Usage
46
-
47
- ```typescript
48
- import { marcidb } from "marcidb-client";
49
-
50
- const db = marcidb("http://localhost:3000");
51
-
52
- // Find
53
- const users = await db.user.findMany({
54
- id: true,
55
- name: true,
56
- });
57
-
58
- // Insert
59
- const id = await db.user.insert({
60
- name: "Alice",
61
- });
62
-
63
- // Update
64
- await db.user.update({ id: 1 }, {
65
- name: "Bob",
66
- });
67
-
68
- // Delete
69
- await db.user.delete({ id: 1 });
70
- ```
71
-
72
- ## Requirements
73
-
74
- - Node.js 18+ or Bun
75
- - MarciDB server running
76
-
77
- ## Re-generating types
78
-
79
- After every change to `schema.marci`, re-run:
80
-
81
- ```bash
82
- marcidb generate
83
- ```
84
-
1
+ # marci-db
2
+
3
+ TypeScript client for [MarciDB](https://github.com/den59k/marci-db.git) — a NoSQL database with a schema-first approach.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install marcidb-client
9
+ # or
10
+ bun add marcidb-client
11
+ ```
12
+
13
+ ## Setup
14
+
15
+ 1. Create a `schema.marci` file in your project root:
16
+
17
+ ```
18
+ model User {
19
+ name String
20
+ posts Post[] @bind(Post.author)
21
+ }
22
+
23
+ model Post {
24
+ title String
25
+ author User?
26
+ }
27
+ ```
28
+
29
+ 2. Generate the client:
30
+
31
+ ```bash
32
+ marcidb generate
33
+ # or with custom paths
34
+ marcidb generate schema.marci node_modules/.marcidb/client
35
+ ```
36
+
37
+ 3. Start your MarciDB server and connect:
38
+
39
+ ```typescript
40
+ import { marcidb } from "marcidb-client";
41
+
42
+ const db = marcidb("http://localhost:3000");
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ ```typescript
48
+ import { marcidb } from "marcidb-client";
49
+
50
+ const db = marcidb("http://localhost:3000");
51
+
52
+ // Find
53
+ const users = await db.user.findMany({
54
+ id: true,
55
+ name: true,
56
+ });
57
+
58
+ // Insert
59
+ const id = await db.user.insert({
60
+ name: "Alice",
61
+ });
62
+
63
+ // Update
64
+ await db.user.update({ id: 1 }, {
65
+ name: "Bob",
66
+ });
67
+
68
+ // Delete
69
+ await db.user.delete({ id: 1 });
70
+ ```
71
+
72
+ ## Requirements
73
+
74
+ - Node.js 18+ or Bun
75
+ - MarciDB server running
76
+
77
+ ## Re-generating types
78
+
79
+ After every change to `schema.marci`, re-run:
80
+
81
+ ```bash
82
+ marcidb generate
83
+ ```
84
+
85
85
  If types don't update in VSCode, run **TypeScript: Restart TS Server** (`Ctrl+Shift+P`).
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "marcidb-client",
3
3
  "type": "module",
4
- "version": "0.3.1",
4
+ "version": "0.6.1",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "den59k"
@@ -1,3 +1,18 @@
1
- declare function request(method: string, url: string, body?: any);
2
- declare function encodeId(id: any);
3
- export { request, encodeId };
1
+ declare function request(method: string, url: string, body?: any): Promise<any>;
2
+ type FieldDesc = {
3
+ n: string;
4
+ k: "key" | "body" | "one" | "many";
5
+ t?: string | null;
6
+ m?: string;
7
+ };
8
+ /**
9
+ * Builds the binary decoder registry for a set of models. `MODELS` maps each model name to its ordered
10
+ * field descriptors (emitted by codegen). Returns `getDecoder` (shape → cached decoder, or `null` when the
11
+ * shape isn't binary-decodable → caller falls back to JSON) and `decodeBuffer` (run a decoder over a buffer).
12
+ */
13
+ declare function createDecoderRegistry(MODELS: Record<string, FieldDesc[]>): {
14
+ getDecoder(model: string, select: Record<string, any> | undefined): ((reader: any) => any) | null;
15
+ decodeBuffer(decoder: (reader: any) => any, bytes: Uint8Array, many: boolean): any;
16
+ };
17
+ declare function encodeId(id: any): string;
18
+ export { request, encodeId, createDecoderRegistry };
package/runtime/index.js CHANGED
@@ -11,6 +11,180 @@ async function request(method, url, body) {
11
11
  }
12
12
  return res.json();
13
13
  }
14
+ var BINARY_VERSION = 1;
15
+ var AGG_KEYS = ["$count", "$sum", "$avg", "$min", "$max"];
16
+
17
+ class BinReader {
18
+ o = 0;
19
+ dv;
20
+ bytes;
21
+ static decoder = new TextDecoder;
22
+ constructor(bytes) {
23
+ this.bytes = bytes;
24
+ this.dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
25
+ }
26
+ u8() {
27
+ return this.dv.getUint8(this.o++);
28
+ }
29
+ u32() {
30
+ const v = this.dv.getUint32(this.o, true);
31
+ this.o += 4;
32
+ return v;
33
+ }
34
+ i64() {
35
+ const v = Number(this.dv.getBigInt64(this.o, true));
36
+ this.o += 8;
37
+ return v;
38
+ }
39
+ u64() {
40
+ const v = Number(this.dv.getBigUint64(this.o, true));
41
+ this.o += 8;
42
+ return v;
43
+ }
44
+ f64() {
45
+ const v = this.dv.getFloat64(this.o, true);
46
+ this.o += 8;
47
+ return v;
48
+ }
49
+ str() {
50
+ const len = this.u32();
51
+ const s = BinReader.decoder.decode(this.bytes.subarray(this.o, this.o + len));
52
+ this.o += len;
53
+ return s;
54
+ }
55
+ }
56
+ function readScalar(r, t) {
57
+ switch (t) {
58
+ case "str":
59
+ return r.str();
60
+ case "i64":
61
+ return r.i64();
62
+ case "u64":
63
+ return r.u64();
64
+ case "f64":
65
+ return r.f64();
66
+ case "bool":
67
+ return r.u8() !== 0;
68
+ case "u8":
69
+ return r.u8();
70
+ default:
71
+ throw new Error(`marcidb: unknown binary type code '${t}'`);
72
+ }
73
+ }
74
+ function createDecoderRegistry(MODELS) {
75
+ const cache = new Map;
76
+ const isSel = (v) => v !== undefined && v !== false;
77
+ function allSelect(model) {
78
+ const out = {};
79
+ for (const f of MODELS[model] ?? []) {
80
+ if (f.k === "key" || f.k === "body")
81
+ out[f.n] = true;
82
+ }
83
+ return out;
84
+ }
85
+ function projectionKey(model, select) {
86
+ const fields = MODELS[model];
87
+ if (!fields)
88
+ return "?";
89
+ const known = new Set(fields.map((f) => f.n));
90
+ let key = model + "(";
91
+ for (const f of fields) {
92
+ const sel = select[f.n];
93
+ if (!isSel(sel))
94
+ continue;
95
+ if (f.k === "one" || f.k === "many") {
96
+ const sub = sel === true ? allSelect(f.m) : sel;
97
+ key += f.n + "{" + projectionKey(f.m, sub) + "}";
98
+ } else {
99
+ key += f.n + ",";
100
+ }
101
+ }
102
+ const unknown = Object.keys(select).filter((k) => k[0] !== "$" && !known.has(k) && isSel(select[k])).sort();
103
+ if (unknown.length)
104
+ key += "!" + unknown.join("!");
105
+ return key + ")";
106
+ }
107
+ function build(model, select) {
108
+ const fields = MODELS[model];
109
+ if (!fields)
110
+ return null;
111
+ const known = new Set(fields.map((f) => f.n));
112
+ for (const k in select) {
113
+ if (k[0] === "$")
114
+ continue;
115
+ if (!known.has(k) && isSel(select[k]))
116
+ return null;
117
+ }
118
+ const steps = [];
119
+ const rels = [];
120
+ for (const f of fields) {
121
+ const sel = select[f.n];
122
+ if (!isSel(sel))
123
+ continue;
124
+ if (f.k === "one" || f.k === "many") {
125
+ if (f.k === "many" && sel && typeof sel === "object" && AGG_KEYS.some((k) => (k in sel)))
126
+ return null;
127
+ const sub = sel === true ? allSelect(f.m) : sel;
128
+ const fn = build(f.m, sub);
129
+ if (!fn)
130
+ return null;
131
+ rels.push({ name: f.n, many: f.k === "many", fn });
132
+ } else {
133
+ if (f.t == null)
134
+ return null;
135
+ steps.push({ name: f.n, key: f.k === "key", t: f.t });
136
+ }
137
+ }
138
+ return (r) => {
139
+ const obj = {};
140
+ for (let i = 0;i < steps.length; i++) {
141
+ const s = steps[i];
142
+ if (s.key) {
143
+ obj[s.name] = readScalar(r, s.t);
144
+ } else {
145
+ obj[s.name] = r.u8() === 0 ? null : readScalar(r, s.t);
146
+ }
147
+ }
148
+ for (let i = 0;i < rels.length; i++) {
149
+ const rel = rels[i];
150
+ if (rel.many) {
151
+ const n = r.u32();
152
+ const arr = new Array(n);
153
+ for (let j = 0;j < n; j++)
154
+ arr[j] = rel.fn(r);
155
+ obj[rel.name] = arr;
156
+ } else {
157
+ obj[rel.name] = r.u8() === 0 ? null : rel.fn(r);
158
+ }
159
+ }
160
+ return obj;
161
+ };
162
+ }
163
+ function getDecoder(model, select) {
164
+ const sel = select ?? {};
165
+ const key = projectionKey(model, sel);
166
+ if (cache.has(key))
167
+ return cache.get(key);
168
+ const fn = build(model, sel);
169
+ cache.set(key, fn);
170
+ return fn;
171
+ }
172
+ function decodeBuffer(decoder, bytes, many) {
173
+ const r = new BinReader(bytes);
174
+ const version = r.u8();
175
+ if (version !== BINARY_VERSION)
176
+ throw new Error(`marcidb: unsupported binary result version ${version}`);
177
+ const count = r.u32();
178
+ if (many) {
179
+ const out = new Array(count);
180
+ for (let i = 0;i < count; i++)
181
+ out[i] = decoder(r);
182
+ return out;
183
+ }
184
+ return count === 0 ? null : decoder(r);
185
+ }
186
+ return { getDecoder, decodeBuffer };
187
+ }
14
188
  function encodeId(id) {
15
189
  if (typeof id === "object" && id !== null) {
16
190
  const parts = [];
@@ -31,5 +205,6 @@ function encodeId(id) {
31
205
  }
32
206
  export {
33
207
  request,
34
- encodeId
208
+ encodeId,
209
+ createDecoderRegistry
35
210
  };
package/client/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- // Types for the generated client (see client/index.js for why this is a relative path, not a bare specifier).
2
- export * from '../../.marcidb/client'
package/client/index.js DELETED
@@ -1,5 +0,0 @@
1
- // Re-export the generated client (default location: node_modules/.marcidb/client). A relative path is
2
- // used deliberately: the bare specifier `.marcidb/client` is an INVALID ESM specifier on Node
3
- // (ERR_INVALID_MODULE_SPECIFIER — it starts with "." but not "./"). A `../` relative path is valid on
4
- // Node and Bun, and resolves the runtime-generated dir without needing it installed as a package.
5
- export * from '../../.marcidb/client/index.js'