@myphp-plugins/sqlite 0.0.0-stage → 1.28.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.
Files changed (3) hide show
  1. package/index.ts +118 -0
  2. package/package.json +19 -4
  3. package/README.md +0 -3
package/index.ts ADDED
@@ -0,0 +1,118 @@
1
+ // The page side of MyPHP's SQLite plugin (mygo's @mygo-plugins/sqlite,
2
+ // over MyPHP's bridge): the PHP service App::use(Sqlite::plugin()) binds as
3
+ // "plugin:sqlite". Import this file into the page's TypeScript.
4
+
5
+ import { call } from "myphp-runtime";
6
+
7
+ /** SQLite values: integers beyond Number's safe range are returned as bigint. */
8
+ export type Value = null | number | bigint | string | Uint8Array;
9
+ /** Booleans bind as SQLite integers 0 and 1. */
10
+ export type Parameter = Value | boolean;
11
+ export interface OpenOptions { readOnly?: boolean }
12
+ export interface Result {
13
+ changes: number;
14
+ /** The last inserted rowid on this connection, without precision loss. */
15
+ lastInsertId: bigint;
16
+ }
17
+ /** Ordered columns and cells preserve duplicate column names. */
18
+ export interface Rows { columns: string[]; rows: Value[][] }
19
+ export interface Statement { sql: string; params?: readonly Parameter[] }
20
+
21
+ interface WireValue { type: string; integer?: string; real?: number; special?: "Infinity" | "-Infinity" | "NaN"; text?: string; blob?: string }
22
+ interface WireResult { changes: number; lastInsertId: string }
23
+ interface WireRows { columns: string[]; rows: WireValue[][] }
24
+
25
+ /** Opens a file name in the PHP plugin's directory, or a private :memory: database. */
26
+ export async function open(name: string, options: OpenOptions = {}): Promise<Database> {
27
+ return Database.open(name, options);
28
+ }
29
+
30
+ /** A page-owned connection. It closes when the page navigates or the app quits. */
31
+ export class Database {
32
+ private closing?: Promise<void>;
33
+ private closed = false;
34
+ private constructor(private readonly id: string) {}
35
+
36
+ static async open(name: string, options: OpenOptions = {}): Promise<Database> {
37
+ const id = await call<string>("plugin:sqlite.open", name, { readOnly: options.readOnly ?? false });
38
+ return new Database(id);
39
+ }
40
+
41
+ /** Executes exactly one statement. Use placeholders, never interpolate values. */
42
+ async execute(sql: string, params: readonly Parameter[] = []): Promise<Result> {
43
+ this.assertOpen();
44
+ return result(await call<WireResult>("plugin:sqlite.execute", this.id, sql, params.map(encode)));
45
+ }
46
+
47
+ /** Queries one statement, including RETURNING; select a bounded page for large results. */
48
+ async query(sql: string, params: readonly Parameter[] = []): Promise<Rows> {
49
+ this.assertOpen();
50
+ const value = await call<WireRows>("plugin:sqlite.query", this.id, sql, params.map(encode));
51
+ return { columns: value.columns, rows: value.rows.map((row) => row.map(decode)) };
52
+ }
53
+
54
+ /** Runs an atomic BEGIN IMMEDIATE batch. A failure rolls back every statement. */
55
+ async transaction(statements: readonly Statement[]): Promise<Result[]> {
56
+ this.assertOpen();
57
+ const batch = statements.map((stmt) => ({ sql: stmt.sql, args: (stmt.params ?? []).map(encode) }));
58
+ return (await call<WireResult[]>("plugin:sqlite.transaction", this.id, batch)).map(result);
59
+ }
60
+
61
+ /** Closes this connection. Concurrent and repeated closes share the same call. */
62
+ close(): Promise<void> {
63
+ if (this.closed) return Promise.resolve();
64
+ if (!this.closing) {
65
+ this.closing = call<void>("plugin:sqlite.close", this.id).then(
66
+ () => { this.closed = true; },
67
+ (error: unknown) => { this.closing = undefined; throw error; },
68
+ );
69
+ }
70
+ return this.closing;
71
+ }
72
+
73
+ private assertOpen(): void {
74
+ if (this.closed || this.closing) throw new Error("sqlite: database is closed");
75
+ }
76
+ }
77
+
78
+ function result(value: WireResult): Result {
79
+ return { changes: value.changes, lastInsertId: BigInt(value.lastInsertId) };
80
+ }
81
+
82
+ function encode(value: Parameter): WireValue {
83
+ if (value === null) return { type: "null" };
84
+ if (typeof value === "boolean") return { type: "integer", integer: value ? "1" : "0" };
85
+ if (typeof value === "bigint") {
86
+ if (value < -(1n << 63n) || value >= (1n << 63n)) throw new RangeError("sqlite: integer exceeds signed 64-bit range");
87
+ return { type: "integer", integer: String(value) };
88
+ }
89
+ if (typeof value === "number") {
90
+ if (!Number.isFinite(value)) throw new TypeError("sqlite: number must be finite");
91
+ if (Number.isInteger(value)) {
92
+ if (!Number.isSafeInteger(value)) throw new RangeError("sqlite: use bigint for integers outside Number's safe range");
93
+ return { type: "integer", integer: String(value) };
94
+ }
95
+ return { type: "real", real: value };
96
+ }
97
+ if (typeof value === "string") return { type: "text", text: value };
98
+ if (value instanceof Uint8Array) {
99
+ let text = "";
100
+ for (let i = 0; i < value.length; i += 8192) text += String.fromCharCode(...value.subarray(i, i + 8192));
101
+ return { type: "blob", blob: btoa(text) };
102
+ }
103
+ throw new TypeError("sqlite: unsupported parameter");
104
+ }
105
+
106
+ function decode(value: WireValue): Value {
107
+ switch (value.type) {
108
+ case "null": return null;
109
+ case "integer": {
110
+ const n = BigInt(value.integer!);
111
+ return n >= BigInt(Number.MIN_SAFE_INTEGER) && n <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(n) : n;
112
+ }
113
+ case "real": return Number(value.special ?? value.real ?? 0);
114
+ case "text": return value.text ?? "";
115
+ case "blob": return Uint8Array.from(atob(value.blob ?? ""), (c) => c.charCodeAt(0));
116
+ default: throw new TypeError(`sqlite: unknown value type ${value.type}`);
117
+ }
118
+ }
package/package.json CHANGED
@@ -1,6 +1,21 @@
1
1
  {
2
2
  "name": "@myphp-plugins/sqlite",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.28.0",
4
+ "description": "The page side of MyPHP's SQLite plugin, over myphp-runtime",
5
+ "type": "module",
6
+ "exports": "./index.ts",
7
+ "types": "./index.ts",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/owner888/myphp.git",
11
+ "directory": "src/Plugins/Sqlite/client"
12
+ },
13
+ "license": "MIT",
14
+ "files": ["index.ts"],
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "peerDependencies": {
19
+ "myphp-runtime": "^1.28.0"
20
+ }
21
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.