@neutron-build/nucleus 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 +27 -0
- package/dist/blob/index.d.ts +42 -0
- package/dist/blob/index.d.ts.map +1 -0
- package/dist/blob/index.js +121 -0
- package/dist/blob/index.js.map +1 -0
- package/dist/cdc/index.d.ts +14 -0
- package/dist/cdc/index.d.ts.map +1 -0
- package/dist/cdc/index.js +41 -0
- package/dist/cdc/index.js.map +1 -0
- package/dist/client.d.ts +59 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +68 -0
- package/dist/client.js.map +1 -0
- package/dist/columnar/index.d.ts +20 -0
- package/dist/columnar/index.d.ts.map +1 -0
- package/dist/columnar/index.js +60 -0
- package/dist/columnar/index.js.map +1 -0
- package/dist/datalog/index.d.ts +20 -0
- package/dist/datalog/index.d.ts.map +1 -0
- package/dist/datalog/index.js +53 -0
- package/dist/datalog/index.js.map +1 -0
- package/dist/document/index.d.ts +42 -0
- package/dist/document/index.d.ts.map +1 -0
- package/dist/document/index.js +154 -0
- package/dist/document/index.js.map +1 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +64 -0
- package/dist/errors.js.map +1 -0
- package/dist/features.d.ts +14 -0
- package/dist/features.d.ts.map +1 -0
- package/dist/features.js +85 -0
- package/dist/features.js.map +1 -0
- package/dist/fts/index.d.ts +35 -0
- package/dist/fts/index.d.ts.map +1 -0
- package/dist/fts/index.js +76 -0
- package/dist/fts/index.js.map +1 -0
- package/dist/geo/index.d.ts +34 -0
- package/dist/geo/index.d.ts.map +1 -0
- package/dist/geo/index.js +121 -0
- package/dist/geo/index.js.map +1 -0
- package/dist/graph/index.d.ts +43 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +109 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/helpers.d.ts +11 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +47 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/kv/index.d.ts +93 -0
- package/dist/kv/index.d.ts.map +1 -0
- package/dist/kv/index.js +223 -0
- package/dist/kv/index.js.map +1 -0
- package/dist/migrate.d.ts +37 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +95 -0
- package/dist/migrate.js.map +1 -0
- package/dist/pubsub/index.d.ts +14 -0
- package/dist/pubsub/index.d.ts.map +1 -0
- package/dist/pubsub/index.js +44 -0
- package/dist/pubsub/index.js.map +1 -0
- package/dist/sql/index.d.ts +31 -0
- package/dist/sql/index.d.ts.map +1 -0
- package/dist/sql/index.js +65 -0
- package/dist/sql/index.js.map +1 -0
- package/dist/streams/index.d.ts +26 -0
- package/dist/streams/index.d.ts.map +1 -0
- package/dist/streams/index.js +83 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/timeseries/index.d.ts +46 -0
- package/dist/timeseries/index.d.ts.map +1 -0
- package/dist/timeseries/index.js +122 -0
- package/dist/timeseries/index.js.map +1 -0
- package/dist/transport.d.ts +97 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +477 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +58 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/vector/index.d.ts +34 -0
- package/dist/vector/index.d.ts.map +1 -0
- package/dist/vector/index.js +104 -0
- package/dist/vector/index.js.map +1 -0
- package/package.json +110 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tyler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @neutron-build/nucleus
|
|
2
|
+
|
|
3
|
+
TypeScript client for the Nucleus database.
|
|
4
|
+
|
|
5
|
+
14 data models over PostgreSQL wire protocol — SQL, KV, Vector, TimeSeries, Document, Graph, FTS, Geo, Blob, Streams, PubSub, Columnar, Datalog, CDC.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @neutron-build/nucleus
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createClient } from "@neutron-build/nucleus";
|
|
17
|
+
import { query } from "@neutron-build/nucleus/sql";
|
|
18
|
+
import { vectorSearch } from "@neutron-build/nucleus/vector";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Documentation
|
|
22
|
+
|
|
23
|
+
[neutron.build](https://neutron.build)
|
|
24
|
+
|
|
25
|
+
## License
|
|
26
|
+
|
|
27
|
+
MIT
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { NucleusPlugin } from '../types.js';
|
|
2
|
+
export interface BlobMeta {
|
|
3
|
+
key: string;
|
|
4
|
+
size: number;
|
|
5
|
+
contentType: string;
|
|
6
|
+
createdAt: Date;
|
|
7
|
+
metadata?: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
export interface BlobPutOptions {
|
|
10
|
+
/** MIME content type (default `application/octet-stream`). */
|
|
11
|
+
contentType?: string;
|
|
12
|
+
/** Custom key/value metadata tags. */
|
|
13
|
+
metadata?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
export interface BlobModel {
|
|
16
|
+
/** Store a blob. `data` is a Uint8Array or a hex-encoded string. */
|
|
17
|
+
put(bucket: string, key: string, data: Uint8Array | string, opts?: BlobPutOptions): Promise<void>;
|
|
18
|
+
/** Retrieve a blob. Returns the decoded bytes and metadata, or `null`. */
|
|
19
|
+
get(bucket: string, key: string): Promise<{
|
|
20
|
+
data: Uint8Array;
|
|
21
|
+
meta: BlobMeta | null;
|
|
22
|
+
} | null>;
|
|
23
|
+
/** Delete a blob. Returns `true` if it existed. */
|
|
24
|
+
delete(bucket: string, key: string): Promise<boolean>;
|
|
25
|
+
/** Get metadata for a blob. */
|
|
26
|
+
meta(bucket: string, key: string): Promise<BlobMeta | null>;
|
|
27
|
+
/** Tag a blob with a key/value pair. */
|
|
28
|
+
tag(bucket: string, key: string, tagKey: string, tagValue: string): Promise<boolean>;
|
|
29
|
+
/** List blobs matching a prefix. */
|
|
30
|
+
list(bucket: string, prefix: string): Promise<BlobMeta[]>;
|
|
31
|
+
/** Check if a blob exists. */
|
|
32
|
+
exists(bucket: string, key: string): Promise<boolean>;
|
|
33
|
+
/** Return the total number of stored blobs. */
|
|
34
|
+
blobCount(): Promise<number>;
|
|
35
|
+
/** Return the deduplication ratio. */
|
|
36
|
+
dedupRatio(): Promise<number>;
|
|
37
|
+
}
|
|
38
|
+
/** Plugin: adds `.blob` to the client. */
|
|
39
|
+
export declare const withBlob: NucleusPlugin<{
|
|
40
|
+
blob: BlobModel;
|
|
41
|
+
}>;
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blob/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,aAAa,EAAmB,MAAM,aAAa,CAAC;AAO7E,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAMD,MAAM,WAAW,SAAS;IACxB,oEAAoE;IACpE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElG,0EAA0E;IAC1E,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAE9F,mDAAmD;IACnD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEtD,+BAA+B;IAC/B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAE5D,wCAAwC;IACxC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAErF,oCAAoC;IACpC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAE1D,8BAA8B;IAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEtD,+CAA+C;IAC/C,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7B,sCAAsC;IACtC,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/B;AAyID,0CAA0C;AAC1C,eAAO,MAAM,QAAQ,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAKvD,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @neutron/nucleus/blob — Blob storage model plugin
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { requireNucleus } from '../helpers.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Hex helpers
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
function toHex(data) {
|
|
9
|
+
if (typeof data === 'string')
|
|
10
|
+
return data;
|
|
11
|
+
return Array.from(data)
|
|
12
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
13
|
+
.join('');
|
|
14
|
+
}
|
|
15
|
+
function fromHex(hex) {
|
|
16
|
+
if (hex.length % 2 !== 0) {
|
|
17
|
+
throw new Error('Invalid hex string: odd length');
|
|
18
|
+
}
|
|
19
|
+
if (!/^[0-9a-fA-F]*$/.test(hex)) {
|
|
20
|
+
throw new Error('Invalid hex string: contains non-hex characters');
|
|
21
|
+
}
|
|
22
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
23
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
24
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
25
|
+
}
|
|
26
|
+
return bytes;
|
|
27
|
+
}
|
|
28
|
+
function parseBlobMeta(raw) {
|
|
29
|
+
return {
|
|
30
|
+
key: raw.key,
|
|
31
|
+
size: raw.size,
|
|
32
|
+
contentType: raw.content_type,
|
|
33
|
+
createdAt: new Date(raw.created_at),
|
|
34
|
+
metadata: raw.metadata,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Implementation
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
class BlobModelImpl {
|
|
41
|
+
transport;
|
|
42
|
+
features;
|
|
43
|
+
constructor(transport, features) {
|
|
44
|
+
this.transport = transport;
|
|
45
|
+
this.features = features;
|
|
46
|
+
}
|
|
47
|
+
require() {
|
|
48
|
+
requireNucleus(this.features, 'Blob');
|
|
49
|
+
}
|
|
50
|
+
async put(bucket, key, data, opts = {}) {
|
|
51
|
+
this.require();
|
|
52
|
+
const fullKey = `${bucket}/${key}`;
|
|
53
|
+
const hexData = toHex(data);
|
|
54
|
+
const contentType = opts.contentType ?? 'application/octet-stream';
|
|
55
|
+
await this.transport.execute('SELECT BLOB_STORE($1, $2, $3)', [fullKey, hexData, contentType]);
|
|
56
|
+
if (opts.metadata) {
|
|
57
|
+
for (const [k, v] of Object.entries(opts.metadata)) {
|
|
58
|
+
await this.transport.execute('SELECT BLOB_TAG($1, $2, $3)', [fullKey, k, v]);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async get(bucket, key) {
|
|
63
|
+
this.require();
|
|
64
|
+
const fullKey = `${bucket}/${key}`;
|
|
65
|
+
const hexData = await this.transport.fetchval('SELECT BLOB_GET($1)', [fullKey]);
|
|
66
|
+
if (hexData === null)
|
|
67
|
+
return null;
|
|
68
|
+
const meta = await this.meta(bucket, key);
|
|
69
|
+
return { data: fromHex(hexData), meta };
|
|
70
|
+
}
|
|
71
|
+
async delete(bucket, key) {
|
|
72
|
+
this.require();
|
|
73
|
+
const fullKey = `${bucket}/${key}`;
|
|
74
|
+
return (await this.transport.fetchval('SELECT BLOB_DELETE($1)', [fullKey])) ?? false;
|
|
75
|
+
}
|
|
76
|
+
async meta(bucket, key) {
|
|
77
|
+
this.require();
|
|
78
|
+
const fullKey = `${bucket}/${key}`;
|
|
79
|
+
const raw = await this.transport.fetchval('SELECT BLOB_META($1)', [fullKey]);
|
|
80
|
+
if (!raw)
|
|
81
|
+
return null;
|
|
82
|
+
return parseBlobMeta(JSON.parse(raw));
|
|
83
|
+
}
|
|
84
|
+
async tag(bucket, key, tagKey, tagValue) {
|
|
85
|
+
this.require();
|
|
86
|
+
const fullKey = `${bucket}/${key}`;
|
|
87
|
+
return (await this.transport.fetchval('SELECT BLOB_TAG($1, $2, $3)', [fullKey, tagKey, tagValue])) ?? false;
|
|
88
|
+
}
|
|
89
|
+
async list(bucket, prefix) {
|
|
90
|
+
this.require();
|
|
91
|
+
const fullPrefix = `${bucket}/${prefix}`;
|
|
92
|
+
const raw = await this.transport.fetchval('SELECT BLOB_LIST($1)', [fullPrefix]);
|
|
93
|
+
if (!raw)
|
|
94
|
+
return [];
|
|
95
|
+
const items = JSON.parse(raw);
|
|
96
|
+
return items.map(parseBlobMeta);
|
|
97
|
+
}
|
|
98
|
+
async exists(bucket, key) {
|
|
99
|
+
const meta = await this.meta(bucket, key);
|
|
100
|
+
return meta !== null;
|
|
101
|
+
}
|
|
102
|
+
async blobCount() {
|
|
103
|
+
this.require();
|
|
104
|
+
return (await this.transport.fetchval('SELECT BLOB_COUNT()')) ?? 0;
|
|
105
|
+
}
|
|
106
|
+
async dedupRatio() {
|
|
107
|
+
this.require();
|
|
108
|
+
return (await this.transport.fetchval('SELECT BLOB_DEDUP_RATIO()')) ?? 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Plugin
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
/** Plugin: adds `.blob` to the client. */
|
|
115
|
+
export const withBlob = {
|
|
116
|
+
name: 'blob',
|
|
117
|
+
init(transport, features) {
|
|
118
|
+
return { blob: new BlobModelImpl(transport, features) };
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/blob/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NucleusPlugin } from '../types.js';
|
|
2
|
+
export interface CDCModel {
|
|
3
|
+
/** Read CDC events starting from the given offset. Returns raw JSON. */
|
|
4
|
+
read(offset: number): Promise<string>;
|
|
5
|
+
/** Return the total number of CDC events. */
|
|
6
|
+
count(): Promise<number>;
|
|
7
|
+
/** Read CDC events for a specific table starting from the given offset. */
|
|
8
|
+
tableRead(table: string, offset: number): Promise<string>;
|
|
9
|
+
}
|
|
10
|
+
/** Plugin: adds `.cdc` to the client. */
|
|
11
|
+
export declare const withCDC: NucleusPlugin<{
|
|
12
|
+
cdc: CDCModel;
|
|
13
|
+
}>;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cdc/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,aAAa,EAAmB,MAAM,aAAa,CAAC;AAO7E,MAAM,WAAW,QAAQ;IACvB,wEAAwE;IACxE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEtC,6CAA6C;IAC7C,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAEzB,2EAA2E;IAC3E,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3D;AAoCD,yCAAyC;AACzC,eAAO,MAAM,OAAO,EAAE,aAAa,CAAC;IAAE,GAAG,EAAE,QAAQ,CAAA;CAAE,CAKpD,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @neutron/nucleus/cdc — Change Data Capture model plugin
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { requireNucleus } from '../helpers.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Implementation
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
class CDCModelImpl {
|
|
9
|
+
transport;
|
|
10
|
+
features;
|
|
11
|
+
constructor(transport, features) {
|
|
12
|
+
this.transport = transport;
|
|
13
|
+
this.features = features;
|
|
14
|
+
}
|
|
15
|
+
require() {
|
|
16
|
+
requireNucleus(this.features, 'CDC');
|
|
17
|
+
}
|
|
18
|
+
async read(offset) {
|
|
19
|
+
this.require();
|
|
20
|
+
return (await this.transport.fetchval('SELECT CDC_READ($1)', [offset])) ?? '';
|
|
21
|
+
}
|
|
22
|
+
async count() {
|
|
23
|
+
this.require();
|
|
24
|
+
return (await this.transport.fetchval('SELECT CDC_COUNT()')) ?? 0;
|
|
25
|
+
}
|
|
26
|
+
async tableRead(table, offset) {
|
|
27
|
+
this.require();
|
|
28
|
+
return (await this.transport.fetchval('SELECT CDC_TABLE_READ($1, $2)', [table, offset])) ?? '';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Plugin
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
/** Plugin: adds `.cdc` to the client. */
|
|
35
|
+
export const withCDC = {
|
|
36
|
+
name: 'cdc',
|
|
37
|
+
init(transport, features) {
|
|
38
|
+
return { cdc: new CDCModelImpl(transport, features) };
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cdc/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,0DAA0D;AAC1D,8EAA8E;AAG9E,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAiB/C,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,YAAY;IAEG;IACA;IAFnB,YACmB,SAAoB,EACpB,QAAyB;QADzB,cAAS,GAAT,SAAS,CAAW;QACpB,aAAQ,GAAR,QAAQ,CAAiB;IACzC,CAAC;IAEI,OAAO;QACb,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc;QACvB,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAS,qBAAqB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACxF,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAS,oBAAoB,CAAC,CAAC,IAAI,CAAC,CAAC;IAC5E,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAa,EAAE,MAAc;QAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAS,+BAA+B,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACzG,CAAC;CACF;AAED,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E,yCAAyC;AACzC,MAAM,CAAC,MAAM,OAAO,GAAqC;IACvD,IAAI,EAAE,KAAK;IACX,IAAI,CAAC,SAAoB,EAAE,QAAyB;QAClD,OAAO,EAAE,GAAG,EAAE,IAAI,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;IACxD,CAAC;CACF,CAAC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Transport, NucleusFeatures, NucleusPlugin } from './types.js';
|
|
2
|
+
export interface NucleusClientConfig {
|
|
3
|
+
/** Base URL of the Nucleus (or PostgreSQL HTTP proxy) server. */
|
|
4
|
+
url: string;
|
|
5
|
+
/** Extra HTTP headers sent with every request. */
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
/** Request timeout in milliseconds (default 30000). */
|
|
8
|
+
timeout?: number;
|
|
9
|
+
/** Override the default transport (e.g. for testing or explicit platform choice). */
|
|
10
|
+
transport?: Transport;
|
|
11
|
+
/** Maximum retry attempts for transient failures (default 3). */
|
|
12
|
+
maxRetries?: number;
|
|
13
|
+
/** Base delay in ms between retries — uses exponential backoff (default 1000). */
|
|
14
|
+
retryDelay?: number;
|
|
15
|
+
/** Time-to-live for cached SELECT results in ms (default 60000). */
|
|
16
|
+
cacheTTL?: number;
|
|
17
|
+
/** Whether to cache SELECT queries (default true on mobile). */
|
|
18
|
+
cacheEnabled?: boolean;
|
|
19
|
+
/** Whether to queue writes when offline (default true on mobile). */
|
|
20
|
+
offlineQueueEnabled?: boolean;
|
|
21
|
+
/** Maximum number of queued offline operations (default 100). */
|
|
22
|
+
maxQueueSize?: number;
|
|
23
|
+
}
|
|
24
|
+
/** The minimal client surface available before any plugins are applied. */
|
|
25
|
+
export interface NucleusClientBase {
|
|
26
|
+
readonly transport: Transport;
|
|
27
|
+
readonly features: NucleusFeatures;
|
|
28
|
+
close(): Promise<void>;
|
|
29
|
+
ping(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A lazy builder that accumulates plugins via `.use()` then resolves them all
|
|
33
|
+
* at `.connect()` time. The return type of `.connect()` is the intersection of
|
|
34
|
+
* `NucleusClientBase` with every plugin's contribution `T`.
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* const db = await createClient({ url: '...' })
|
|
38
|
+
* .use(withSQL)
|
|
39
|
+
* .use(withKV)
|
|
40
|
+
* .connect();
|
|
41
|
+
*
|
|
42
|
+
* db.sql.query(...);
|
|
43
|
+
* db.kv.get(...);
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export interface NucleusClientBuilder<Acc> {
|
|
47
|
+
/** Register a plugin. Returns a new builder whose type includes `T`. */
|
|
48
|
+
use<T>(plugin: NucleusPlugin<T>): NucleusClientBuilder<Acc & T>;
|
|
49
|
+
/** Connect to the server, detect features, and initialise all plugins. */
|
|
50
|
+
connect(): Promise<NucleusClientBase & Acc>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create a new Nucleus client builder.
|
|
54
|
+
*
|
|
55
|
+
* Call `.use(plugin)` to add model support, then `.connect()` to establish the
|
|
56
|
+
* connection and resolve the fully-typed client.
|
|
57
|
+
*/
|
|
58
|
+
export declare function createClient(config: NucleusClientConfig): NucleusClientBuilder<Record<string, never>>;
|
|
59
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAQ5E,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,SAAS,CAAC,EAAE,SAAS,CAAC;IAItB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qEAAqE;IACrE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAMD;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,oBAAoB,CAAC,GAAG;IACvC,wEAAwE;IACxE,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,oBAAoB,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAEhE,0EAA0E;IAC1E,OAAO,IAAI,OAAO,CAAC,iBAAiB,GAAG,GAAG,CAAC,CAAC;CAC7C;AAgED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,mBAAmB,GAAG,oBAAoB,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAErG"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Nucleus client — builder + `.use()` plugin composition
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { createTransport } from './transport.js';
|
|
5
|
+
import { detectFeatures } from './features.js';
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Implementation
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
class ClientBuilder {
|
|
10
|
+
config;
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
plugins;
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
constructor(config, plugins = []) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.plugins = plugins;
|
|
17
|
+
}
|
|
18
|
+
use(plugin) {
|
|
19
|
+
return new ClientBuilder(this.config, [...this.plugins, plugin]);
|
|
20
|
+
}
|
|
21
|
+
async connect() {
|
|
22
|
+
const transport = this.config.transport ?? createTransport({
|
|
23
|
+
url: this.config.url,
|
|
24
|
+
headers: this.config.headers,
|
|
25
|
+
timeout: this.config.timeout,
|
|
26
|
+
maxRetries: this.config.maxRetries,
|
|
27
|
+
retryDelay: this.config.retryDelay,
|
|
28
|
+
cacheTTL: this.config.cacheTTL,
|
|
29
|
+
cacheEnabled: this.config.cacheEnabled,
|
|
30
|
+
offlineQueueEnabled: this.config.offlineQueueEnabled,
|
|
31
|
+
maxQueueSize: this.config.maxQueueSize,
|
|
32
|
+
});
|
|
33
|
+
const features = await detectFeatures(transport);
|
|
34
|
+
// Base client object
|
|
35
|
+
const base = {
|
|
36
|
+
transport,
|
|
37
|
+
features,
|
|
38
|
+
close: () => transport.close(),
|
|
39
|
+
ping: () => transport.ping(),
|
|
40
|
+
};
|
|
41
|
+
// Merge plugin contributions into the base object
|
|
42
|
+
const reserved = new Set(['transport', 'features', 'close', 'ping']);
|
|
43
|
+
const client = base;
|
|
44
|
+
for (const plugin of this.plugins) {
|
|
45
|
+
const contribution = plugin.init(transport, features);
|
|
46
|
+
for (const key of Object.keys(contribution)) {
|
|
47
|
+
if (reserved.has(key)) {
|
|
48
|
+
throw new Error(`Plugin "${plugin.name}" cannot override reserved property "${key}"`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
Object.assign(client, contribution);
|
|
52
|
+
}
|
|
53
|
+
return client;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Public factory
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
/**
|
|
60
|
+
* Create a new Nucleus client builder.
|
|
61
|
+
*
|
|
62
|
+
* Call `.use(plugin)` to add model support, then `.connect()` to establish the
|
|
63
|
+
* connection and resolve the fully-typed client.
|
|
64
|
+
*/
|
|
65
|
+
export function createClient(config) {
|
|
66
|
+
return new ClientBuilder(config);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,yDAAyD;AACzD,8EAA8E;AAG9E,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAuE/C,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,aAAa;IACA,MAAM,CAAsB;IAC7C,8DAA8D;IAC7C,OAAO,CAAuB;IAE/C,8DAA8D;IAC9D,YAAY,MAA2B,EAAE,UAAgC,EAAE;QACzE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,GAAG,CAAI,MAAwB;QAC7B,OAAO,IAAI,aAAa,CAAU,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,eAAe,CAAC;YACzD,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG;YACpB,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU;YAClC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU;YAClC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC9B,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;YACtC,mBAAmB,EAAE,IAAI,CAAC,MAAM,CAAC,mBAAmB;YACpD,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;SACvC,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;QAEjD,qBAAqB;QACrB,MAAM,IAAI,GAAsB;YAC9B,SAAS;YACT,QAAQ;YACR,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE;YAC9B,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE;SAC7B,CAAC;QAEF,kDAAkD;QAClD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,IAA+B,CAAC;QAC/C,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YACtD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,YAAsB,CAAC,EAAE,CAAC;gBACtD,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtB,MAAM,IAAI,KAAK,CAAC,WAAW,MAAM,CAAC,IAAI,wCAAwC,GAAG,GAAG,CAAC,CAAC;gBACxF,CAAC;YACH,CAAC;YACD,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,MAA2B;IACtD,OAAO,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { NucleusPlugin } from '../types.js';
|
|
2
|
+
export interface ColumnarModel {
|
|
3
|
+
/** Insert a row into a columnar table. Returns `true` on success. */
|
|
4
|
+
insert(table: string, values: Record<string, unknown>): Promise<boolean>;
|
|
5
|
+
/** Return the number of rows in a columnar table. */
|
|
6
|
+
count(table: string): Promise<number>;
|
|
7
|
+
/** Return the sum of a numeric column. */
|
|
8
|
+
sum(table: string, column: string): Promise<number>;
|
|
9
|
+
/** Return the average of a numeric column. */
|
|
10
|
+
avg(table: string, column: string): Promise<number>;
|
|
11
|
+
/** Return the minimum value of a column. */
|
|
12
|
+
min(table: string, column: string): Promise<unknown>;
|
|
13
|
+
/** Return the maximum value of a column. */
|
|
14
|
+
max(table: string, column: string): Promise<unknown>;
|
|
15
|
+
}
|
|
16
|
+
/** Plugin: adds `.columnar` to the client. */
|
|
17
|
+
export declare const withColumnar: NucleusPlugin<{
|
|
18
|
+
columnar: ColumnarModel;
|
|
19
|
+
}>;
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/columnar/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,aAAa,EAAmB,MAAM,aAAa,CAAC;AAO7E,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEzE,qDAAqD;IACrD,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEtC,0CAA0C;IAC1C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEpD,8CAA8C;IAC9C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEpD,4CAA4C;IAC5C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAErD,4CAA4C;IAC5C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtD;AA0DD,8CAA8C;AAC9C,eAAO,MAAM,YAAY,EAAE,aAAa,CAAC;IAAE,QAAQ,EAAE,aAAa,CAAA;CAAE,CAKnE,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @neutron/nucleus/columnar — Columnar analytics model plugin
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { requireNucleus, assertIdentifier } from '../helpers.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Implementation
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
class ColumnarModelImpl {
|
|
9
|
+
transport;
|
|
10
|
+
features;
|
|
11
|
+
constructor(transport, features) {
|
|
12
|
+
this.transport = transport;
|
|
13
|
+
this.features = features;
|
|
14
|
+
}
|
|
15
|
+
require() {
|
|
16
|
+
requireNucleus(this.features, 'Columnar');
|
|
17
|
+
}
|
|
18
|
+
async insert(table, values) {
|
|
19
|
+
this.require();
|
|
20
|
+
assertIdentifier(table, 'table name');
|
|
21
|
+
const valuesJson = JSON.stringify(values);
|
|
22
|
+
return (await this.transport.fetchval('SELECT COLUMNAR_INSERT($1, $2)', [table, valuesJson])) ?? false;
|
|
23
|
+
}
|
|
24
|
+
async count(table) {
|
|
25
|
+
this.require();
|
|
26
|
+
assertIdentifier(table, 'table name');
|
|
27
|
+
return (await this.transport.fetchval('SELECT COLUMNAR_COUNT($1)', [table])) ?? 0;
|
|
28
|
+
}
|
|
29
|
+
async sum(table, column) {
|
|
30
|
+
this.require();
|
|
31
|
+
assertIdentifier(table, 'table name');
|
|
32
|
+
return (await this.transport.fetchval('SELECT COLUMNAR_SUM($1, $2)', [table, column])) ?? 0;
|
|
33
|
+
}
|
|
34
|
+
async avg(table, column) {
|
|
35
|
+
this.require();
|
|
36
|
+
assertIdentifier(table, 'table name');
|
|
37
|
+
return (await this.transport.fetchval('SELECT COLUMNAR_AVG($1, $2)', [table, column])) ?? 0;
|
|
38
|
+
}
|
|
39
|
+
async min(table, column) {
|
|
40
|
+
this.require();
|
|
41
|
+
assertIdentifier(table, 'table name');
|
|
42
|
+
return this.transport.fetchval('SELECT COLUMNAR_MIN($1, $2)', [table, column]);
|
|
43
|
+
}
|
|
44
|
+
async max(table, column) {
|
|
45
|
+
this.require();
|
|
46
|
+
assertIdentifier(table, 'table name');
|
|
47
|
+
return this.transport.fetchval('SELECT COLUMNAR_MAX($1, $2)', [table, column]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Plugin
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
/** Plugin: adds `.columnar` to the client. */
|
|
54
|
+
export const withColumnar = {
|
|
55
|
+
name: 'columnar',
|
|
56
|
+
init(transport, features) {
|
|
57
|
+
return { columnar: new ColumnarModelImpl(transport, features) };
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/columnar/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,8DAA8D;AAC9D,8EAA8E;AAG9E,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AA0BjE,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,iBAAiB;IAEF;IACA;IAFnB,YACmB,SAAoB,EACpB,QAAyB;QADzB,cAAS,GAAT,SAAS,CAAW;QACpB,aAAQ,GAAR,QAAQ,CAAiB;IACzC,CAAC;IAEI,OAAO;QACb,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,MAA+B;QACzD,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,gBAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACtC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1C,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAU,gCAAgC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IAClH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAa;QACvB,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,gBAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACtC,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAS,2BAA2B,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC5F,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa,EAAE,MAAc;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,gBAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACtC,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAS,6BAA6B,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACtG,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa,EAAE,MAAc;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,gBAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACtC,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAS,6BAA6B,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACtG,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa,EAAE,MAAc;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,gBAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,6BAA6B,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa,EAAE,MAAc;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,gBAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,6BAA6B,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACjF,CAAC;CACF;AAED,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E,8CAA8C;AAC9C,MAAM,CAAC,MAAM,YAAY,GAA+C;IACtE,IAAI,EAAE,UAAU;IAChB,IAAI,CAAC,SAAoB,EAAE,QAAyB;QAClD,OAAO,EAAE,QAAQ,EAAE,IAAI,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;IAClE,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { NucleusPlugin } from '../types.js';
|
|
2
|
+
export interface DatalogModel {
|
|
3
|
+
/** Add a fact to the knowledge base. */
|
|
4
|
+
assert(fact: string): Promise<boolean>;
|
|
5
|
+
/** Remove a fact from the knowledge base. */
|
|
6
|
+
retract(fact: string): Promise<boolean>;
|
|
7
|
+
/** Define a rule with a head and body. */
|
|
8
|
+
rule(head: string, body: string): Promise<boolean>;
|
|
9
|
+
/** Evaluate a Datalog query pattern. Returns results as CSV text. */
|
|
10
|
+
query(pattern: string): Promise<string>;
|
|
11
|
+
/** Clear all facts and rules. */
|
|
12
|
+
clear(): Promise<boolean>;
|
|
13
|
+
/** Import the graph model into the Datalog knowledge base. Returns count of facts imported. */
|
|
14
|
+
importGraph(): Promise<number>;
|
|
15
|
+
}
|
|
16
|
+
/** Plugin: adds `.datalog` to the client. */
|
|
17
|
+
export declare const withDatalog: NucleusPlugin<{
|
|
18
|
+
datalog: DatalogModel;
|
|
19
|
+
}>;
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/datalog/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,aAAa,EAAmB,MAAM,aAAa,CAAC;AAO7E,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvC,6CAA6C;IAC7C,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAExC,0CAA0C;IAC1C,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEnD,qEAAqE;IACrE,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAExC,iCAAiC;IACjC,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAE1B,+FAA+F;IAC/F,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAChC;AAmDD,6CAA6C;AAC7C,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC;IAAE,OAAO,EAAE,YAAY,CAAA;CAAE,CAKhE,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @neutron/nucleus/datalog — Datalog reasoning model plugin
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { requireNucleus } from '../helpers.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Implementation
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
class DatalogModelImpl {
|
|
9
|
+
transport;
|
|
10
|
+
features;
|
|
11
|
+
constructor(transport, features) {
|
|
12
|
+
this.transport = transport;
|
|
13
|
+
this.features = features;
|
|
14
|
+
}
|
|
15
|
+
require() {
|
|
16
|
+
requireNucleus(this.features, 'Datalog');
|
|
17
|
+
}
|
|
18
|
+
async assert(fact) {
|
|
19
|
+
this.require();
|
|
20
|
+
return (await this.transport.fetchval('SELECT DATALOG_ASSERT($1)', [fact])) ?? false;
|
|
21
|
+
}
|
|
22
|
+
async retract(fact) {
|
|
23
|
+
this.require();
|
|
24
|
+
return (await this.transport.fetchval('SELECT DATALOG_RETRACT($1)', [fact])) ?? false;
|
|
25
|
+
}
|
|
26
|
+
async rule(head, body) {
|
|
27
|
+
this.require();
|
|
28
|
+
return (await this.transport.fetchval('SELECT DATALOG_RULE($1, $2)', [head, body])) ?? false;
|
|
29
|
+
}
|
|
30
|
+
async query(pattern) {
|
|
31
|
+
this.require();
|
|
32
|
+
return (await this.transport.fetchval('SELECT DATALOG_QUERY($1)', [pattern])) ?? '';
|
|
33
|
+
}
|
|
34
|
+
async clear() {
|
|
35
|
+
this.require();
|
|
36
|
+
return (await this.transport.fetchval('SELECT DATALOG_CLEAR()')) ?? false;
|
|
37
|
+
}
|
|
38
|
+
async importGraph() {
|
|
39
|
+
this.require();
|
|
40
|
+
return (await this.transport.fetchval('SELECT DATALOG_IMPORT_GRAPH()')) ?? 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Plugin
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
/** Plugin: adds `.datalog` to the client. */
|
|
47
|
+
export const withDatalog = {
|
|
48
|
+
name: 'datalog',
|
|
49
|
+
init(transport, features) {
|
|
50
|
+
return { datalog: new DatalogModelImpl(transport, features) };
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/datalog/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4DAA4D;AAC5D,8EAA8E;AAG9E,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA0B/C,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,gBAAgB;IAED;IACA;IAFnB,YACmB,SAAoB,EACpB,QAAyB;QADzB,cAAS,GAAT,SAAS,CAAW;QACpB,aAAQ,GAAR,QAAQ,CAAiB;IACzC,CAAC;IAEI,OAAO;QACb,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAU,2BAA2B,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IAChG,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAY;QACxB,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAU,4BAA4B,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IACjG,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,IAAY;QACnC,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAU,6BAA6B,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IACxG,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAAe;QACzB,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAS,0BAA0B,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9F,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAU,wBAAwB,CAAC,CAAC,IAAI,KAAK,CAAC;IACrF,CAAC;IAED,KAAK,CAAC,WAAW;QACf,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAS,+BAA+B,CAAC,CAAC,IAAI,CAAC,CAAC;IACvF,CAAC;CACF;AAED,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E,6CAA6C;AAC7C,MAAM,CAAC,MAAM,WAAW,GAA6C;IACnE,IAAI,EAAE,SAAS;IACf,IAAI,CAAC,SAAoB,EAAE,QAAyB;QAClD,OAAO,EAAE,OAAO,EAAE,IAAI,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;IAChE,CAAC;CACF,CAAC"}
|