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 +84 -84
- package/bin/marci-generate-darwin-arm64 +0 -0
- package/bin/marci-generate-linux-x64 +0 -0
- package/bin/marci-generate-win32-x64.exe +0 -0
- package/bin/marci-migrate-darwin-arm64 +0 -0
- package/bin/marci-migrate-linux-x64 +0 -0
- package/bin/marci-migrate-win32-x64.exe +0 -0
- package/package.json +1 -1
- package/runtime/index.d.ts +18 -3
- package/runtime/index.js +176 -1
- package/client/index.d.ts +0 -2
- package/client/index.js +0 -5
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
package/runtime/index.d.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
declare function request(method: string, url: string, body?: any)
|
|
2
|
-
|
|
3
|
-
|
|
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
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'
|