@oxilite/d1 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 +20 -0
- package/bin/oxilite-d1.mjs +11 -0
- package/dist/driver.d.ts +102 -0
- package/dist/driver.js +161 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +35 -0
- package/dist/node.d.ts +9 -0
- package/dist/node.js +15 -0
- package/package.json +64 -0
- package/wasm/node/oxilite_wasm.d.ts +113 -0
- package/wasm/node/oxilite_wasm.js +536 -0
- package/wasm/node/oxilite_wasm_bg.wasm +0 -0
- package/wasm/node/oxilite_wasm_bg.wasm.d.ts +35 -0
- package/wasm/node/package.json +1 -0
- package/wasm/web/oxilite_wasm.d.ts +173 -0
- package/wasm/web/oxilite_wasm.js +632 -0
- package/wasm/web/oxilite_wasm_bg.wasm +0 -0
- package/wasm/web/oxilite_wasm_bg.wasm.d.ts +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# @oxilite/d1
|
|
2
|
+
|
|
3
|
+
An Oxigraph-compatible SPARQL 1.1 store on a Cloudflare D1 database. The oxilite core runs as WebAssembly: SPARQL is compiled to SQL and sent to your `env.DB` binding, so every query is one D1 call and every update is one atomic batch.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { D1Store } from "@oxilite/d1";
|
|
7
|
+
import wasm from "@oxilite/d1/oxilite.wasm";
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
async fetch(req: Request, env: { DB: D1Database }) {
|
|
11
|
+
const store = await D1Store.open(env.DB, { wasm, migrated: true });
|
|
12
|
+
const q = new URL(req.url).searchParams.get("query") ?? "SELECT * WHERE { ?s ?p ?o } LIMIT 10";
|
|
13
|
+
return new Response(await store.queryJson(q), { headers: { "content-type": "application/sparql-results+json" } });
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Create the schema as a migration with `npx oxilite-d1 schema > migrations/0001_oxilite.sql`. In Node (tests, Miniflare), import from `@oxilite/d1/node`.
|
|
19
|
+
|
|
20
|
+
The API mirrors Oxigraph's JavaScript `Store` (`query`, `update`, `load`, `dump`, `add`, `delete`, `has`, `match`, `size`), asynchronously, plus `explain`, `bulkLoad`, `optimize`, `materialize` (OWL 2 RL) and query options for RDFS / OWL reasoning and full-text search. See the [oxilite repository](https://github.com/Volland/oxilite).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Usage: npx oxilite-d1 schema [--no-graph-index] > migrations/0001_oxilite.sql
|
|
3
|
+
import { D1Store } from "../dist/node.js";
|
|
4
|
+
|
|
5
|
+
const [command, ...args] = process.argv.slice(2);
|
|
6
|
+
if (command === "schema") {
|
|
7
|
+
process.stdout.write(D1Store.schemaSql({ graphIndex: !args.includes("--no-graph-index") }));
|
|
8
|
+
} else {
|
|
9
|
+
console.error("usage: oxilite-d1 schema [--no-graph-index]");
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
package/dist/driver.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { type DumpOptions, type LoadData, type LoadOptions, type Quad, type QueryOptions, type QueryResult, type TermLike } from "@oxilite/common";
|
|
2
|
+
/** The subset of the Cloudflare D1 binding API used by oxilite. */
|
|
3
|
+
export interface D1PreparedStatementLike {
|
|
4
|
+
raw(): Promise<unknown[][]>;
|
|
5
|
+
}
|
|
6
|
+
export interface D1ResultLike {
|
|
7
|
+
results?: Record<string, unknown>[];
|
|
8
|
+
meta?: {
|
|
9
|
+
changes?: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export interface D1DatabaseLike {
|
|
13
|
+
prepare(sql: string): D1PreparedStatementLike;
|
|
14
|
+
batch(statements: D1PreparedStatementLike[]): Promise<D1ResultLike[]>;
|
|
15
|
+
}
|
|
16
|
+
/** The wasm engine API (generated by wasm-bindgen from crates/oxilite-wasm). */
|
|
17
|
+
export interface WasmJob {
|
|
18
|
+
step(response?: string | null): string;
|
|
19
|
+
free?(): void;
|
|
20
|
+
}
|
|
21
|
+
export interface WasmEngine {
|
|
22
|
+
open(): WasmJob;
|
|
23
|
+
openExisting(): WasmJob;
|
|
24
|
+
optimize(): WasmJob;
|
|
25
|
+
query(sparql: string, options?: string | null): WasmJob;
|
|
26
|
+
queryJson(sparql: string): WasmJob;
|
|
27
|
+
explain(sparql: string): string;
|
|
28
|
+
update(sparql: string, baseIri?: string | null): WasmJob;
|
|
29
|
+
explainUpdate(sparql: string): string;
|
|
30
|
+
load(data: string, format: string, base?: string | null, graph?: string | null): WasmJob;
|
|
31
|
+
bulkLoad(data: string, format: string, base?: string | null, graph?: string | null): WasmJob;
|
|
32
|
+
add(quads: string): WasmJob;
|
|
33
|
+
delete(quads: string): WasmJob;
|
|
34
|
+
has(quad: string): WasmJob;
|
|
35
|
+
match(s?: string | null, p?: string | null, o?: string | null, g?: string | null): WasmJob;
|
|
36
|
+
size(): WasmJob;
|
|
37
|
+
dump(format: string, graph?: string | null): WasmJob;
|
|
38
|
+
clear(): WasmJob;
|
|
39
|
+
materialize(): WasmJob;
|
|
40
|
+
clearInferences(): WasmJob;
|
|
41
|
+
schemaSql(): string;
|
|
42
|
+
}
|
|
43
|
+
export type EngineConstructor = new (capabilities?: string | null, options?: string | null) => WasmEngine;
|
|
44
|
+
/** A term hash collision (two different terms with the same 59-bit id). */
|
|
45
|
+
export declare class OxiliteCollisionError extends Error {
|
|
46
|
+
}
|
|
47
|
+
export interface D1StoreOptions {
|
|
48
|
+
/** Create the optional graph index (default true); only used when the schema is created. */
|
|
49
|
+
graphIndex?: boolean;
|
|
50
|
+
/** The schema was applied by a migration: open without DDL. */
|
|
51
|
+
migrated?: boolean;
|
|
52
|
+
/** Create the FTS5 full-text index over string literals (for `oxl:textMatch`). */
|
|
53
|
+
textIndex?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/** An oxilite RDF store on a Cloudflare D1 database. */
|
|
56
|
+
export declare class D1Store {
|
|
57
|
+
readonly db: D1DatabaseLike;
|
|
58
|
+
private readonly engine;
|
|
59
|
+
private constructor();
|
|
60
|
+
static openWith(Engine: EngineConstructor, db: D1DatabaseLike, options?: D1StoreOptions): Promise<D1Store>;
|
|
61
|
+
private execute;
|
|
62
|
+
private run;
|
|
63
|
+
/** SPARQL query: `Map[]` for SELECT, `boolean` for ASK, `Quad[]` for CONSTRUCT/DESCRIBE, or a string with `results_format`. */
|
|
64
|
+
query(query: string, options?: QueryOptions): Promise<QueryResult>;
|
|
65
|
+
/** SPARQL query returning SPARQL 1.1 JSON results. */
|
|
66
|
+
queryJson(query: string): Promise<string>;
|
|
67
|
+
/** The SQL a query compiles to, with join orders and warnings. */
|
|
68
|
+
explain(query: string): string;
|
|
69
|
+
/** SPARQL update, applied atomically in one D1 batch. */
|
|
70
|
+
update(update: string, options?: {
|
|
71
|
+
base_iri?: string;
|
|
72
|
+
}): Promise<void>;
|
|
73
|
+
/** How an update runs (SQL per operation). */
|
|
74
|
+
explainUpdate(update: string): string;
|
|
75
|
+
/** Loads RDF atomically (one batch); use `bulkLoad` for large documents. */
|
|
76
|
+
load(data: LoadData, options: LoadOptions): Promise<void>;
|
|
77
|
+
/** Loads RDF in several batches (not atomic) and refreshes planner statistics. */
|
|
78
|
+
bulkLoad(data: LoadData, options: LoadOptions): Promise<void>;
|
|
79
|
+
/** Serializes the dataset or one graph. */
|
|
80
|
+
dump(options: DumpOptions): Promise<string>;
|
|
81
|
+
add(quad: TermLike): Promise<void>;
|
|
82
|
+
/** Inserts many quads atomically. */
|
|
83
|
+
addAll(quads: TermLike[]): Promise<void>;
|
|
84
|
+
delete(quad: TermLike): Promise<void>;
|
|
85
|
+
has(quad: TermLike): Promise<boolean>;
|
|
86
|
+
match(subject?: TermLike | null, predicate?: TermLike | null, object?: TermLike | null, graph?: TermLike | null): Promise<Quad[]>;
|
|
87
|
+
/** Number of quads. */
|
|
88
|
+
size(): Promise<number>;
|
|
89
|
+
/**
|
|
90
|
+
* Computes the OWL 2 RL closure into a separate inference table with SQL rules (one D1
|
|
91
|
+
* batch per round; not atomic as a whole). Query it with `include_inferred: true`.
|
|
92
|
+
* Returns the number of inferred triples.
|
|
93
|
+
*/
|
|
94
|
+
materialize(): Promise<number>;
|
|
95
|
+
/** Removes every materialized inference. */
|
|
96
|
+
clearInferences(): Promise<void>;
|
|
97
|
+
/** Refreshes planner statistics (run after large imports). */
|
|
98
|
+
optimize(): Promise<void>;
|
|
99
|
+
clear(): Promise<void>;
|
|
100
|
+
/** The schema SQL of this store (for `wrangler d1 migrations`). */
|
|
101
|
+
schemaSql(): string;
|
|
102
|
+
}
|
package/dist/driver.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// The D1 driver: runs the oxilite core's SQL requests on a Cloudflare D1 binding.
|
|
2
|
+
//
|
|
3
|
+
// The core (WebAssembly) never touches the database. Each operation is a job: the driver
|
|
4
|
+
// feeds it the response of every request it asks for, until it is done. Atomic requests
|
|
5
|
+
// become one `db.batch()` — D1's only transaction.
|
|
6
|
+
import { loadDataToString, outputToResult, toJson, } from "@oxilite/common";
|
|
7
|
+
/** A term hash collision (two different terms with the same 59-bit id). */
|
|
8
|
+
export class OxiliteCollisionError extends Error {
|
|
9
|
+
}
|
|
10
|
+
function mapError(e) {
|
|
11
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
12
|
+
if (message.includes("oxilite: term hash collision"))
|
|
13
|
+
return new OxiliteCollisionError(message);
|
|
14
|
+
if (message.includes("graph_does_not_exist"))
|
|
15
|
+
return new Error("the graph does not exist");
|
|
16
|
+
if (message.includes("graph_already_exists"))
|
|
17
|
+
return new Error("the graph already exists");
|
|
18
|
+
if (message.includes("computed_value_not_storable")) {
|
|
19
|
+
return new Error("an update template stores a computed non-integer value, which cannot be done on D1");
|
|
20
|
+
}
|
|
21
|
+
return e instanceof Error ? e : new Error(message);
|
|
22
|
+
}
|
|
23
|
+
/** An oxilite RDF store on a Cloudflare D1 database. */
|
|
24
|
+
export class D1Store {
|
|
25
|
+
db;
|
|
26
|
+
engine;
|
|
27
|
+
constructor(db, engine) {
|
|
28
|
+
this.db = db;
|
|
29
|
+
this.engine = engine;
|
|
30
|
+
}
|
|
31
|
+
static async openWith(Engine, db, options = {}) {
|
|
32
|
+
const engine = new Engine(null, JSON.stringify({ graphIndex: options.graphIndex ?? true, textIndex: options.textIndex ?? false }));
|
|
33
|
+
const store = new D1Store(db, engine);
|
|
34
|
+
await store.run(options.migrated ? engine.openExisting() : engine.open());
|
|
35
|
+
return store;
|
|
36
|
+
}
|
|
37
|
+
async execute(req) {
|
|
38
|
+
try {
|
|
39
|
+
if (req.statements.length === 0)
|
|
40
|
+
return [];
|
|
41
|
+
if (req.mode === "read" && req.statements.length === 1) {
|
|
42
|
+
const rows = await this.db.prepare(req.statements[0].sql).raw();
|
|
43
|
+
return [{ rows, changes: 0 }];
|
|
44
|
+
}
|
|
45
|
+
const results = await this.db.batch(req.statements.map((s) => this.db.prepare(s.sql)));
|
|
46
|
+
return results.map((r) => ({
|
|
47
|
+
rows: (r.results ?? []).map((row) => Object.values(row)),
|
|
48
|
+
changes: r.meta?.changes ?? 0,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
throw mapError(e);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async run(job) {
|
|
56
|
+
try {
|
|
57
|
+
let response = null;
|
|
58
|
+
for (;;) {
|
|
59
|
+
const step = JSON.parse(job.step(response));
|
|
60
|
+
if (step.done !== undefined)
|
|
61
|
+
return step.done;
|
|
62
|
+
response = JSON.stringify(await this.execute(step.execute));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
job.free?.();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** SPARQL query: `Map[]` for SELECT, `boolean` for ASK, `Quad[]` for CONSTRUCT/DESCRIBE, or a string with `results_format`. */
|
|
70
|
+
async query(query, options = {}) {
|
|
71
|
+
const js = {
|
|
72
|
+
...options,
|
|
73
|
+
default_graph: options.default_graph
|
|
74
|
+
? Array.isArray(options.default_graph)
|
|
75
|
+
? options.default_graph.map(toJson)
|
|
76
|
+
: toJson(options.default_graph)
|
|
77
|
+
: undefined,
|
|
78
|
+
named_graphs: options.named_graphs?.map(toJson),
|
|
79
|
+
};
|
|
80
|
+
return outputToResult(await this.run(this.engine.query(query, JSON.stringify(js))));
|
|
81
|
+
}
|
|
82
|
+
/** SPARQL query returning SPARQL 1.1 JSON results. */
|
|
83
|
+
async queryJson(query) {
|
|
84
|
+
const out = await this.run(this.engine.queryJson(query));
|
|
85
|
+
return out.value;
|
|
86
|
+
}
|
|
87
|
+
/** The SQL a query compiles to, with join orders and warnings. */
|
|
88
|
+
explain(query) {
|
|
89
|
+
return this.engine.explain(query);
|
|
90
|
+
}
|
|
91
|
+
/** SPARQL update, applied atomically in one D1 batch. */
|
|
92
|
+
async update(update, options = {}) {
|
|
93
|
+
await this.run(this.engine.update(update, options.base_iri ?? null));
|
|
94
|
+
}
|
|
95
|
+
/** How an update runs (SQL per operation). */
|
|
96
|
+
explainUpdate(update) {
|
|
97
|
+
return this.engine.explainUpdate(update);
|
|
98
|
+
}
|
|
99
|
+
/** Loads RDF atomically (one batch); use `bulkLoad` for large documents. */
|
|
100
|
+
async load(data, options) {
|
|
101
|
+
const graph = options.to_graph_name ? JSON.stringify(toJson(options.to_graph_name)) : null;
|
|
102
|
+
const text = loadDataToString(data);
|
|
103
|
+
const job = options.no_transaction
|
|
104
|
+
? this.engine.bulkLoad(text, options.format, options.base_iri ?? null, graph)
|
|
105
|
+
: this.engine.load(text, options.format, options.base_iri ?? null, graph);
|
|
106
|
+
await this.run(job);
|
|
107
|
+
}
|
|
108
|
+
/** Loads RDF in several batches (not atomic) and refreshes planner statistics. */
|
|
109
|
+
async bulkLoad(data, options) {
|
|
110
|
+
await this.load(data, { ...options, no_transaction: true });
|
|
111
|
+
}
|
|
112
|
+
/** Serializes the dataset or one graph. */
|
|
113
|
+
async dump(options) {
|
|
114
|
+
const graph = options.from_graph_name ? JSON.stringify(toJson(options.from_graph_name)) : null;
|
|
115
|
+
return (await this.run(this.engine.dump(options.format, graph))).value;
|
|
116
|
+
}
|
|
117
|
+
async add(quad) {
|
|
118
|
+
await this.run(this.engine.add(JSON.stringify([toJson(quad)])));
|
|
119
|
+
}
|
|
120
|
+
/** Inserts many quads atomically. */
|
|
121
|
+
async addAll(quads) {
|
|
122
|
+
await this.run(this.engine.add(JSON.stringify(quads.map(toJson))));
|
|
123
|
+
}
|
|
124
|
+
async delete(quad) {
|
|
125
|
+
await this.run(this.engine.delete(JSON.stringify([toJson(quad)])));
|
|
126
|
+
}
|
|
127
|
+
async has(quad) {
|
|
128
|
+
return (await this.run(this.engine.has(JSON.stringify(toJson(quad))))).value;
|
|
129
|
+
}
|
|
130
|
+
async match(subject, predicate, object, graph) {
|
|
131
|
+
const j = (t) => (t ? JSON.stringify(toJson(t)) : null);
|
|
132
|
+
return outputToResult(await this.run(this.engine.match(j(subject), j(predicate), j(object), j(graph))));
|
|
133
|
+
}
|
|
134
|
+
/** Number of quads. */
|
|
135
|
+
async size() {
|
|
136
|
+
return (await this.run(this.engine.size())).value;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Computes the OWL 2 RL closure into a separate inference table with SQL rules (one D1
|
|
140
|
+
* batch per round; not atomic as a whole). Query it with `include_inferred: true`.
|
|
141
|
+
* Returns the number of inferred triples.
|
|
142
|
+
*/
|
|
143
|
+
async materialize() {
|
|
144
|
+
return (await this.run(this.engine.materialize())).value;
|
|
145
|
+
}
|
|
146
|
+
/** Removes every materialized inference. */
|
|
147
|
+
async clearInferences() {
|
|
148
|
+
await this.run(this.engine.clearInferences());
|
|
149
|
+
}
|
|
150
|
+
/** Refreshes planner statistics (run after large imports). */
|
|
151
|
+
async optimize() {
|
|
152
|
+
await this.run(this.engine.optimize());
|
|
153
|
+
}
|
|
154
|
+
async clear() {
|
|
155
|
+
await this.run(this.engine.clear());
|
|
156
|
+
}
|
|
157
|
+
/** The schema SQL of this store (for `wrangler d1 migrations`). */
|
|
158
|
+
schemaSql() {
|
|
159
|
+
return this.engine.schemaSql();
|
|
160
|
+
}
|
|
161
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { D1Store as Base, type D1DatabaseLike, type D1StoreOptions } from "./driver.js";
|
|
2
|
+
export * from "@oxilite/common";
|
|
3
|
+
export { OxiliteCollisionError, type D1DatabaseLike, type D1StoreOptions } from "./driver.js";
|
|
4
|
+
/** Initializes the WebAssembly core (a `WebAssembly.Module`, bytes, or a URL/Response). */
|
|
5
|
+
export declare function initOxilite(wasm?: WebAssembly.Module | BufferSource | Response | URL | string): Promise<void>;
|
|
6
|
+
export declare class D1Store {
|
|
7
|
+
/** Opens (and, unless `migrated`, creates) the oxilite schema on a D1 binding. */
|
|
8
|
+
static open(db: D1DatabaseLike, options?: D1StoreOptions & {
|
|
9
|
+
wasm?: WebAssembly.Module | BufferSource | Response | URL | string;
|
|
10
|
+
}): Promise<Base>;
|
|
11
|
+
/** The schema as SQL (for `wrangler d1 migrations`). */
|
|
12
|
+
static schemaSql(options?: {
|
|
13
|
+
graphIndex?: boolean;
|
|
14
|
+
wasm?: WebAssembly.Module | BufferSource;
|
|
15
|
+
}): Promise<string>;
|
|
16
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// @oxilite/d1 for Cloudflare Workers (and other WebAssembly hosts).
|
|
2
|
+
//
|
|
3
|
+
// In a Worker, import the wasm module and pass it once:
|
|
4
|
+
// import wasm from "@oxilite/d1/oxilite.wasm";
|
|
5
|
+
// const store = await D1Store.open(env.DB, { wasm });
|
|
6
|
+
import init, { Engine, initSync } from "../wasm/web/oxilite_wasm.js";
|
|
7
|
+
import { D1Store as Base } from "./driver.js";
|
|
8
|
+
export * from "@oxilite/common";
|
|
9
|
+
export { OxiliteCollisionError } from "./driver.js";
|
|
10
|
+
let ready = false;
|
|
11
|
+
/** Initializes the WebAssembly core (a `WebAssembly.Module`, bytes, or a URL/Response). */
|
|
12
|
+
export async function initOxilite(wasm) {
|
|
13
|
+
if (ready)
|
|
14
|
+
return;
|
|
15
|
+
if (wasm instanceof WebAssembly.Module || ArrayBuffer.isView(wasm) || wasm instanceof ArrayBuffer) {
|
|
16
|
+
initSync({ module: wasm });
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
await init(wasm === undefined ? undefined : { module_or_path: wasm });
|
|
20
|
+
}
|
|
21
|
+
ready = true;
|
|
22
|
+
}
|
|
23
|
+
export class D1Store {
|
|
24
|
+
/** Opens (and, unless `migrated`, creates) the oxilite schema on a D1 binding. */
|
|
25
|
+
static async open(db, options = {}) {
|
|
26
|
+
await initOxilite(options.wasm);
|
|
27
|
+
return Base.openWith(Engine, db, options);
|
|
28
|
+
}
|
|
29
|
+
/** The schema as SQL (for `wrangler d1 migrations`). */
|
|
30
|
+
static async schemaSql(options = {}) {
|
|
31
|
+
await initOxilite(options.wasm);
|
|
32
|
+
const e = new Engine(null, JSON.stringify({ graphIndex: options.graphIndex ?? true }));
|
|
33
|
+
return e.schemaSql();
|
|
34
|
+
}
|
|
35
|
+
}
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { D1Store as Base, type D1DatabaseLike, type D1StoreOptions } from "./driver.js";
|
|
2
|
+
export * from "@oxilite/common";
|
|
3
|
+
export { OxiliteCollisionError, type D1DatabaseLike, type D1StoreOptions } from "./driver.js";
|
|
4
|
+
export declare class D1Store {
|
|
5
|
+
static open(db: D1DatabaseLike, options?: D1StoreOptions): Promise<Base>;
|
|
6
|
+
static schemaSql(options?: {
|
|
7
|
+
graphIndex?: boolean;
|
|
8
|
+
}): string;
|
|
9
|
+
}
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// @oxilite/d1 for Node.js (tests, scripts, Miniflare): the WebAssembly core loads itself.
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { D1Store as Base } from "./driver.js";
|
|
4
|
+
export * from "@oxilite/common";
|
|
5
|
+
export { OxiliteCollisionError } from "./driver.js";
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const { Engine } = require("../wasm/node/oxilite_wasm.js");
|
|
8
|
+
export class D1Store {
|
|
9
|
+
static async open(db, options = {}) {
|
|
10
|
+
return Base.openWith(Engine, db, options);
|
|
11
|
+
}
|
|
12
|
+
static schemaSql(options = {}) {
|
|
13
|
+
return new Engine(null, JSON.stringify({ graphIndex: options.graphIndex ?? true })).schemaSql();
|
|
14
|
+
}
|
|
15
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@oxilite/d1",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "oxilite on Cloudflare D1: an Oxigraph-compatible SPARQL store running on a D1 binding",
|
|
5
|
+
"license": "MIT OR Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"node": "./dist/node.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./node": {
|
|
16
|
+
"types": "./dist/node.d.ts",
|
|
17
|
+
"default": "./dist/node.js"
|
|
18
|
+
},
|
|
19
|
+
"./oxilite.wasm": "./wasm/web/oxilite_wasm_bg.wasm"
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"oxilite-d1": "bin/oxilite-d1.mjs"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"wasm",
|
|
27
|
+
"bin"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build:wasm": "cargo build -p oxilite-wasm --target wasm32-unknown-unknown --profile wasm-release && wasm-bindgen --target nodejs --out-dir wasm/node ../../target/wasm32-unknown-unknown/wasm-release/oxilite_wasm.wasm && wasm-bindgen --target web --out-dir wasm/web ../../target/wasm32-unknown-unknown/wasm-release/oxilite_wasm.wasm && echo {\\\"type\\\":\\\"commonjs\\\"} > wasm/node/package.json",
|
|
31
|
+
"build": "tsc -p .",
|
|
32
|
+
"test": "vitest run"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@oxilite/common": "0.1.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"miniflare": "^4"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/Volland/oxilite.git",
|
|
43
|
+
"directory": "packages/d1"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/Volland/oxilite#readme",
|
|
46
|
+
"bugs": "https://github.com/Volland/oxilite/issues",
|
|
47
|
+
"author": "Volodymyr Pavlyshyn",
|
|
48
|
+
"keywords": [
|
|
49
|
+
"rdf",
|
|
50
|
+
"sparql",
|
|
51
|
+
"sqlite",
|
|
52
|
+
"oxigraph",
|
|
53
|
+
"rdfjs",
|
|
54
|
+
"cloudflare",
|
|
55
|
+
"d1",
|
|
56
|
+
"workers"
|
|
57
|
+
],
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=18"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The oxilite engine for one database.
|
|
6
|
+
*/
|
|
7
|
+
export class Engine {
|
|
8
|
+
free(): void;
|
|
9
|
+
[Symbol.dispose](): void;
|
|
10
|
+
/**
|
|
11
|
+
* Inserts quads (JSON array of RDF/JS quads) atomically.
|
|
12
|
+
*/
|
|
13
|
+
add(quads: string): Job;
|
|
14
|
+
/**
|
|
15
|
+
* Loads a document in several batches (not atomic), then refreshes statistics.
|
|
16
|
+
*/
|
|
17
|
+
bulkLoad(data: string, format_name: string, base?: string | null, graph?: string | null): Job;
|
|
18
|
+
/**
|
|
19
|
+
* Removes every materialized inference.
|
|
20
|
+
*/
|
|
21
|
+
clearInferences(): Job;
|
|
22
|
+
/**
|
|
23
|
+
* Removes everything.
|
|
24
|
+
*/
|
|
25
|
+
clear(): Job;
|
|
26
|
+
/**
|
|
27
|
+
* Removes quads (JSON array of RDF/JS quads) atomically.
|
|
28
|
+
*/
|
|
29
|
+
delete(quads: string): Job;
|
|
30
|
+
/**
|
|
31
|
+
* Serializes the dataset (or one graph, JSON graph term) in an RDF format.
|
|
32
|
+
*/
|
|
33
|
+
dump(format_name: string, graph?: string | null): Job;
|
|
34
|
+
/**
|
|
35
|
+
* How an update would run (SQL per operation).
|
|
36
|
+
*/
|
|
37
|
+
explainUpdate(sparql: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* The SQL a query compiles to, with the planner's notes.
|
|
40
|
+
*/
|
|
41
|
+
explain(sparql: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Does the store contain a quad (JSON RDF/JS quad)?
|
|
44
|
+
*/
|
|
45
|
+
has(quad: string): Job;
|
|
46
|
+
/**
|
|
47
|
+
* Loads a document atomically (one batch). `graph` is a JSON graph term.
|
|
48
|
+
*/
|
|
49
|
+
load(data: string, format_name: string, base?: string | null, graph?: string | null): Job;
|
|
50
|
+
/**
|
|
51
|
+
* Quads matching a pattern; each argument is a JSON term or null.
|
|
52
|
+
*/
|
|
53
|
+
match(subject?: string | null, predicate?: string | null, object?: string | null, graph?: string | null): Job;
|
|
54
|
+
/**
|
|
55
|
+
* Computes the OWL 2 RL closure into the inference table (one batch per rule round);
|
|
56
|
+
* the result is `{"kind": "number"}`, the number of inferred triples.
|
|
57
|
+
*/
|
|
58
|
+
materialize(): Job;
|
|
59
|
+
/**
|
|
60
|
+
* `capabilities` (JSON, optional) defaults to Cloudflare D1's; `options` (JSON,
|
|
61
|
+
* optional) are the store options, e.g. `{"graphIndex": false}`.
|
|
62
|
+
*/
|
|
63
|
+
constructor(capabilities?: string | null, options?: string | null);
|
|
64
|
+
/**
|
|
65
|
+
* Loads planner statistics only (schema applied by a migration).
|
|
66
|
+
*/
|
|
67
|
+
openExisting(): Job;
|
|
68
|
+
/**
|
|
69
|
+
* Creates the schema if needed and loads planner statistics.
|
|
70
|
+
*/
|
|
71
|
+
open(): Job;
|
|
72
|
+
/**
|
|
73
|
+
* Recomputes planner statistics.
|
|
74
|
+
*/
|
|
75
|
+
optimize(): Job;
|
|
76
|
+
/**
|
|
77
|
+
* A SPARQL query whose result is SPARQL 1.1 JSON results (`{"kind": "text", "value"}`).
|
|
78
|
+
*/
|
|
79
|
+
queryJson(sparql: string): Job;
|
|
80
|
+
/**
|
|
81
|
+
* A SPARQL query. `options` (JSON) uses Oxigraph's JS names: `base_iri`,
|
|
82
|
+
* `use_default_graph_as_union`, `default_graph`, `named_graphs`, `results_format`. The
|
|
83
|
+
* result is `oxilite_core::json::output_to_json`, or `{"kind": "text"}` when a
|
|
84
|
+
* `results_format` is given.
|
|
85
|
+
*/
|
|
86
|
+
query(sparql: string, options?: string | null): Job;
|
|
87
|
+
/**
|
|
88
|
+
* The schema as a SQL script (for `wrangler d1 migrations`).
|
|
89
|
+
*/
|
|
90
|
+
schemaSql(): string;
|
|
91
|
+
/**
|
|
92
|
+
* Number of quads.
|
|
93
|
+
*/
|
|
94
|
+
size(): Job;
|
|
95
|
+
/**
|
|
96
|
+
* A SPARQL update, applied as one atomic request (one D1 batch).
|
|
97
|
+
*/
|
|
98
|
+
update(sparql: string, base_iri?: string | null): Job;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A resumable operation (see the crate documentation).
|
|
103
|
+
*/
|
|
104
|
+
export class Job {
|
|
105
|
+
private constructor();
|
|
106
|
+
free(): void;
|
|
107
|
+
[Symbol.dispose](): void;
|
|
108
|
+
/**
|
|
109
|
+
* Advances the job: `response` is the JSON response to the previous request (or null).
|
|
110
|
+
* Returns `{"execute": request}` or `{"done": value}` as JSON.
|
|
111
|
+
*/
|
|
112
|
+
step(response?: string | null): string;
|
|
113
|
+
}
|