@lunora/hyperdrive 1.0.0-alpha.7 → 1.0.0-alpha.71
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.md +6 -0
- package/dist/global.d.mts +66 -45
- package/dist/global.d.ts +66 -45
- package/dist/global.mjs +1 -9
- package/dist/index.d.mts +89 -154
- package/dist/index.d.ts +89 -154
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/buildMysqlExec-zneG1aUr.mjs +1 -0
- package/dist/packem_shared/createHyperdrive-DCe1WINF.mjs +1 -0
- package/dist/packem_shared/mysqlDialect-BIV-y3MW.mjs +1 -0
- package/dist/packem_shared/projectSourceRow-Dw1DLzyv.mjs +1 -0
- package/dist/packem_shared/types.d-DE1NYxyA.d.mts +113 -0
- package/dist/packem_shared/types.d-DE1NYxyA.d.ts +113 -0
- package/package.json +4 -3
- package/dist/packem_shared/buildMysqlExec-DBbCjyq3.mjs +0 -23
- package/dist/packem_shared/createHyperdrive-DD8GoDZo.mjs +0 -36
- package/dist/packem_shared/mysqlDialect-oNhZ58s8.mjs +0 -102
package/LICENSE.md
CHANGED
|
@@ -103,3 +103,9 @@ Unless required by applicable law or agreed to in writing, software distributed
|
|
|
103
103
|
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
104
104
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
105
105
|
specific language governing permissions and limitations under the License.
|
|
106
|
+
|
|
107
|
+
<!-- DEPENDENCIES -->
|
|
108
|
+
<!-- /DEPENDENCIES -->
|
|
109
|
+
|
|
110
|
+
<!-- TYPE_DEPENDENCIES -->
|
|
111
|
+
<!-- /TYPE_DEPENDENCIES -->
|
package/dist/global.d.mts
CHANGED
|
@@ -1,46 +1,71 @@
|
|
|
1
|
-
import { DatabaseWriterLike } from '@lunora/
|
|
1
|
+
import { DatabaseWriterLike } from '@lunora/shard-engine';
|
|
2
2
|
import { SqlExec, SqlDialect, SqlCtxDbOptions } from '@lunora/sql-store';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
import { M as Mysql2Like, S as SqlClient } from "./packem_shared/types.d-DE1NYxyA.mjs";
|
|
4
|
+
/**
|
|
5
|
+
* Minimal row-returning client (e.g. `@lunora/hyperdrive`'s `fromPostgresJs`/`fromNodePg`
|
|
6
|
+
* result). Aliases {@link SqlClient} — the exec-facing name is kept so call sites read
|
|
7
|
+
* intent, but the shape is the single source of truth in `./types` (no drift).
|
|
8
|
+
*/
|
|
9
|
+
type RowClient = SqlClient;
|
|
10
|
+
/**
|
|
11
|
+
* Minimal `mysql2/promise` connection/pool surface — `execute` resolves to
|
|
12
|
+
* `[rows | ResultSetHeader, fields]`. Aliases {@link Mysql2Like} so the /global entry's
|
|
13
|
+
* driver surface can never drift from the main entry's.
|
|
14
|
+
*/
|
|
15
|
+
type Mysql2Execute = Mysql2Like;
|
|
11
16
|
/**
|
|
12
|
-
* Wrap a Postgres row-client (from `@lunora/hyperdrive`'s `fromPostgresJs` /
|
|
13
|
-
* `fromNodePg`) as a {@link SqlExec}. The core already renders `$N` placeholders
|
|
14
|
-
* for Postgres, so `all`/`run` forward verbatim. Postgres uses `RETURNING` for
|
|
15
|
-
* OCC (read via `all`), so `run` reports no affected-row count.
|
|
16
|
-
|
|
17
|
+
* Wrap a Postgres row-client (from `@lunora/hyperdrive`'s `fromPostgresJs` /
|
|
18
|
+
* `fromNodePg`) as a {@link SqlExec}. The core already renders `$N` placeholders
|
|
19
|
+
* for Postgres, so `all`/`run` forward verbatim. Postgres uses `RETURNING` for
|
|
20
|
+
* OCC (read via `all`), so `run` reports no affected-row count.
|
|
21
|
+
*
|
|
22
|
+
* `batch` dispatches every statement concurrently (`Promise.all`) over `client`
|
|
23
|
+
* rather than awaiting each `query` call in turn — `RowClient` only exposes a
|
|
24
|
+
* single-statement `query`, so there is no wire-level multi-statement command
|
|
25
|
+
* to reach for. When `client` is backed by a pool (the common production
|
|
26
|
+
* shape), this genuinely spreads the statements across multiple physical
|
|
27
|
+
* connections instead of serializing one full round trip at a time; against a
|
|
28
|
+
* single connection it still removes the sequential *await*, though the
|
|
29
|
+
* underlying driver may itself queue the sends. Either way it stays
|
|
30
|
+
* non-atomic, at-least-once, and unordered between elements, same as the
|
|
31
|
+
* sequential fallback minus the ordering — safe only for statements whose
|
|
32
|
+
* effects don't depend on each other, which is what every current caller
|
|
33
|
+
* batches (distinct-keyed companion rows).
|
|
34
|
+
*/
|
|
17
35
|
declare const buildPgExec: (client: RowClient) => SqlExec;
|
|
18
36
|
/**
|
|
19
|
-
* Wrap a `mysql2/promise` connection/pool as a {@link SqlExec}. The core already
|
|
20
|
-
* renders backtick identifiers and `?` placeholders for MySQL, so `all`/`run`
|
|
21
|
-
* forward verbatim. MySQL has no `RETURNING`, so `run` surfaces `affectedRows`
|
|
22
|
-
* for the store's affected-rows OCC guard.
|
|
23
|
-
*
|
|
24
|
-
* **The connection MUST be created with the `CLIENT_FOUND_ROWS` flag**
|
|
25
|
-
* (mysql2: `createPool({ flags: ["FOUND_ROWS"] })`). Without it, `affectedRows`
|
|
26
|
-
* counts *changed* rows, so an idempotent `patch`/`replace` that re-writes the
|
|
27
|
-
* same values reports 0 and the OCC guard raises a spurious conflict.
|
|
28
|
-
|
|
37
|
+
* Wrap a `mysql2/promise` connection/pool as a {@link SqlExec}. The core already
|
|
38
|
+
* renders backtick identifiers and `?` placeholders for MySQL, so `all`/`run`
|
|
39
|
+
* forward verbatim. MySQL has no `RETURNING`, so `run` surfaces `affectedRows`
|
|
40
|
+
* for the store's affected-rows OCC guard.
|
|
41
|
+
*
|
|
42
|
+
* **The connection MUST be created with the `CLIENT_FOUND_ROWS` flag**
|
|
43
|
+
* (mysql2: `createPool({ flags: ["FOUND_ROWS"] })`). Without it, `affectedRows`
|
|
44
|
+
* counts *changed* rows, so an idempotent `patch`/`replace` that re-writes the
|
|
45
|
+
* same values reports 0 and the OCC guard raises a spurious conflict.
|
|
46
|
+
*
|
|
47
|
+
* `batch` dispatches every statement concurrently (`Promise.all`), same
|
|
48
|
+
* rationale as {@link buildPgExec}'s `batch` — `Mysql2Like` only exposes a
|
|
49
|
+
* single-statement `execute`, so this is "spread across a pool's connections"
|
|
50
|
+
* rather than one wire-level multi-statement command; still non-atomic,
|
|
51
|
+
* at-least-once, and unordered between elements, safe only for statements
|
|
52
|
+
* with no cross-effect (what every current caller batches).
|
|
53
|
+
*/
|
|
29
54
|
declare const buildMysqlExec: (connection: Mysql2Execute) => SqlExec;
|
|
30
55
|
/**
|
|
31
|
-
* **Postgres** dialect. Differs from SQLite only in column types
|
|
32
|
-
* (`DOUBLE PRECISION`/`BYTEA`/`BIGSERIAL`), the `information_schema` catalog
|
|
33
|
-
* probe, and unique-violation detection (SQLSTATE `23505`).
|
|
34
|
-
*/
|
|
56
|
+
* **Postgres** dialect. Differs from SQLite only in column types
|
|
57
|
+
* (`DOUBLE PRECISION`/`BYTEA`/`BIGSERIAL`), the `information_schema` catalog
|
|
58
|
+
* probe, and unique-violation detection (SQLSTATE `23505`).
|
|
59
|
+
*/
|
|
35
60
|
declare const postgresDialect: SqlDialect;
|
|
36
61
|
/**
|
|
37
|
-
* **MySQL** dialect. Diverges in: **no `RETURNING`** (the store's OCC falls back
|
|
38
|
-
* to affected-rows — which requires the connection's `CLIENT_FOUND_ROWS` flag so
|
|
39
|
-
* a no-op update still reports a matched row, see `buildMysqlExec`); bounded
|
|
40
|
-
* `VARCHAR` keys (TEXT can't be a primary key / unindexed); a TEXT/BLOB index key
|
|
41
|
-
* prefix; and `ER_DUP_ENTRY` (errno 1062) unique violations. (Drizzle's MySQL
|
|
42
|
-
* dialect supplies the backtick identifiers + `ON DUPLICATE KEY` upserts.)
|
|
43
|
-
*/
|
|
62
|
+
* **MySQL** dialect. Diverges in: **no `RETURNING`** (the store's OCC falls back
|
|
63
|
+
* to affected-rows — which requires the connection's `CLIENT_FOUND_ROWS` flag so
|
|
64
|
+
* a no-op update still reports a matched row, see `buildMysqlExec`); bounded
|
|
65
|
+
* `VARCHAR` keys (TEXT can't be a primary key / unindexed); a TEXT/BLOB index key
|
|
66
|
+
* prefix; and `ER_DUP_ENTRY` (errno 1062) unique violations. (Drizzle's MySQL
|
|
67
|
+
* dialect supplies the backtick identifiers + `ON DUPLICATE KEY` upserts.)
|
|
68
|
+
*/
|
|
44
69
|
declare const mysqlDialect: SqlDialect;
|
|
45
70
|
/** Which engine a Hyperdrive-backed `.global()` store targets. */
|
|
46
71
|
type HyperdriveEngine = "mysql" | "postgres";
|
|
@@ -52,16 +77,12 @@ interface CreateHyperdriveGlobalCtxDbOptions extends Omit<SqlCtxDbOptions, "dial
|
|
|
52
77
|
exec: SqlExec;
|
|
53
78
|
}
|
|
54
79
|
/**
|
|
55
|
-
* Build a reactive `.global()` writer backed by a Hyperdrive-reachable
|
|
56
|
-
* Postgres/MySQL database. Pass a built {@link SqlExec} (via {@link buildPgExec}/
|
|
57
|
-
* {@link buildMysqlExec}) and the matching `engine`; everything else mirrors the
|
|
58
|
-
* D1 store options.
|
|
59
|
-
*/
|
|
60
|
-
declare const createHyperdriveGlobalCtxDb: ({
|
|
61
|
-
engine,
|
|
62
|
-
exec,
|
|
63
|
-
...rest
|
|
64
|
-
}: CreateHyperdriveGlobalCtxDbOptions) => DatabaseWriterLike;
|
|
80
|
+
* Build a reactive `.global()` writer backed by a Hyperdrive-reachable
|
|
81
|
+
* Postgres/MySQL database. Pass a built {@link SqlExec} (via {@link buildPgExec}/
|
|
82
|
+
* {@link buildMysqlExec}) and the matching `engine`; everything else mirrors the
|
|
83
|
+
* D1 store options.
|
|
84
|
+
*/
|
|
85
|
+
declare const createHyperdriveGlobalCtxDb: ({ engine, exec, ...rest }: CreateHyperdriveGlobalCtxDbOptions) => DatabaseWriterLike;
|
|
65
86
|
/** Convenience: a **Postgres** `.global()` writer from a row-client (postgres.js/pg over Hyperdrive). */
|
|
66
87
|
declare const createPostgresGlobalCtxDb: (client: RowClient, options: Omit<CreateHyperdriveGlobalCtxDbOptions, "engine" | "exec">) => DatabaseWriterLike;
|
|
67
88
|
/** Convenience: a **MySQL** `.global()` writer from a `mysql2/promise` connection/pool (created with `flags: ["FOUND_ROWS"]`). */
|
package/dist/global.d.ts
CHANGED
|
@@ -1,46 +1,71 @@
|
|
|
1
|
-
import { DatabaseWriterLike } from '@lunora/
|
|
1
|
+
import { DatabaseWriterLike } from '@lunora/shard-engine';
|
|
2
2
|
import { SqlExec, SqlDialect, SqlCtxDbOptions } from '@lunora/sql-store';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
import { M as Mysql2Like, S as SqlClient } from "./packem_shared/types.d-DE1NYxyA.js";
|
|
4
|
+
/**
|
|
5
|
+
* Minimal row-returning client (e.g. `@lunora/hyperdrive`'s `fromPostgresJs`/`fromNodePg`
|
|
6
|
+
* result). Aliases {@link SqlClient} — the exec-facing name is kept so call sites read
|
|
7
|
+
* intent, but the shape is the single source of truth in `./types` (no drift).
|
|
8
|
+
*/
|
|
9
|
+
type RowClient = SqlClient;
|
|
10
|
+
/**
|
|
11
|
+
* Minimal `mysql2/promise` connection/pool surface — `execute` resolves to
|
|
12
|
+
* `[rows | ResultSetHeader, fields]`. Aliases {@link Mysql2Like} so the /global entry's
|
|
13
|
+
* driver surface can never drift from the main entry's.
|
|
14
|
+
*/
|
|
15
|
+
type Mysql2Execute = Mysql2Like;
|
|
11
16
|
/**
|
|
12
|
-
* Wrap a Postgres row-client (from `@lunora/hyperdrive`'s `fromPostgresJs` /
|
|
13
|
-
* `fromNodePg`) as a {@link SqlExec}. The core already renders `$N` placeholders
|
|
14
|
-
* for Postgres, so `all`/`run` forward verbatim. Postgres uses `RETURNING` for
|
|
15
|
-
* OCC (read via `all`), so `run` reports no affected-row count.
|
|
16
|
-
|
|
17
|
+
* Wrap a Postgres row-client (from `@lunora/hyperdrive`'s `fromPostgresJs` /
|
|
18
|
+
* `fromNodePg`) as a {@link SqlExec}. The core already renders `$N` placeholders
|
|
19
|
+
* for Postgres, so `all`/`run` forward verbatim. Postgres uses `RETURNING` for
|
|
20
|
+
* OCC (read via `all`), so `run` reports no affected-row count.
|
|
21
|
+
*
|
|
22
|
+
* `batch` dispatches every statement concurrently (`Promise.all`) over `client`
|
|
23
|
+
* rather than awaiting each `query` call in turn — `RowClient` only exposes a
|
|
24
|
+
* single-statement `query`, so there is no wire-level multi-statement command
|
|
25
|
+
* to reach for. When `client` is backed by a pool (the common production
|
|
26
|
+
* shape), this genuinely spreads the statements across multiple physical
|
|
27
|
+
* connections instead of serializing one full round trip at a time; against a
|
|
28
|
+
* single connection it still removes the sequential *await*, though the
|
|
29
|
+
* underlying driver may itself queue the sends. Either way it stays
|
|
30
|
+
* non-atomic, at-least-once, and unordered between elements, same as the
|
|
31
|
+
* sequential fallback minus the ordering — safe only for statements whose
|
|
32
|
+
* effects don't depend on each other, which is what every current caller
|
|
33
|
+
* batches (distinct-keyed companion rows).
|
|
34
|
+
*/
|
|
17
35
|
declare const buildPgExec: (client: RowClient) => SqlExec;
|
|
18
36
|
/**
|
|
19
|
-
* Wrap a `mysql2/promise` connection/pool as a {@link SqlExec}. The core already
|
|
20
|
-
* renders backtick identifiers and `?` placeholders for MySQL, so `all`/`run`
|
|
21
|
-
* forward verbatim. MySQL has no `RETURNING`, so `run` surfaces `affectedRows`
|
|
22
|
-
* for the store's affected-rows OCC guard.
|
|
23
|
-
*
|
|
24
|
-
* **The connection MUST be created with the `CLIENT_FOUND_ROWS` flag**
|
|
25
|
-
* (mysql2: `createPool({ flags: ["FOUND_ROWS"] })`). Without it, `affectedRows`
|
|
26
|
-
* counts *changed* rows, so an idempotent `patch`/`replace` that re-writes the
|
|
27
|
-
* same values reports 0 and the OCC guard raises a spurious conflict.
|
|
28
|
-
|
|
37
|
+
* Wrap a `mysql2/promise` connection/pool as a {@link SqlExec}. The core already
|
|
38
|
+
* renders backtick identifiers and `?` placeholders for MySQL, so `all`/`run`
|
|
39
|
+
* forward verbatim. MySQL has no `RETURNING`, so `run` surfaces `affectedRows`
|
|
40
|
+
* for the store's affected-rows OCC guard.
|
|
41
|
+
*
|
|
42
|
+
* **The connection MUST be created with the `CLIENT_FOUND_ROWS` flag**
|
|
43
|
+
* (mysql2: `createPool({ flags: ["FOUND_ROWS"] })`). Without it, `affectedRows`
|
|
44
|
+
* counts *changed* rows, so an idempotent `patch`/`replace` that re-writes the
|
|
45
|
+
* same values reports 0 and the OCC guard raises a spurious conflict.
|
|
46
|
+
*
|
|
47
|
+
* `batch` dispatches every statement concurrently (`Promise.all`), same
|
|
48
|
+
* rationale as {@link buildPgExec}'s `batch` — `Mysql2Like` only exposes a
|
|
49
|
+
* single-statement `execute`, so this is "spread across a pool's connections"
|
|
50
|
+
* rather than one wire-level multi-statement command; still non-atomic,
|
|
51
|
+
* at-least-once, and unordered between elements, safe only for statements
|
|
52
|
+
* with no cross-effect (what every current caller batches).
|
|
53
|
+
*/
|
|
29
54
|
declare const buildMysqlExec: (connection: Mysql2Execute) => SqlExec;
|
|
30
55
|
/**
|
|
31
|
-
* **Postgres** dialect. Differs from SQLite only in column types
|
|
32
|
-
* (`DOUBLE PRECISION`/`BYTEA`/`BIGSERIAL`), the `information_schema` catalog
|
|
33
|
-
* probe, and unique-violation detection (SQLSTATE `23505`).
|
|
34
|
-
*/
|
|
56
|
+
* **Postgres** dialect. Differs from SQLite only in column types
|
|
57
|
+
* (`DOUBLE PRECISION`/`BYTEA`/`BIGSERIAL`), the `information_schema` catalog
|
|
58
|
+
* probe, and unique-violation detection (SQLSTATE `23505`).
|
|
59
|
+
*/
|
|
35
60
|
declare const postgresDialect: SqlDialect;
|
|
36
61
|
/**
|
|
37
|
-
* **MySQL** dialect. Diverges in: **no `RETURNING`** (the store's OCC falls back
|
|
38
|
-
* to affected-rows — which requires the connection's `CLIENT_FOUND_ROWS` flag so
|
|
39
|
-
* a no-op update still reports a matched row, see `buildMysqlExec`); bounded
|
|
40
|
-
* `VARCHAR` keys (TEXT can't be a primary key / unindexed); a TEXT/BLOB index key
|
|
41
|
-
* prefix; and `ER_DUP_ENTRY` (errno 1062) unique violations. (Drizzle's MySQL
|
|
42
|
-
* dialect supplies the backtick identifiers + `ON DUPLICATE KEY` upserts.)
|
|
43
|
-
*/
|
|
62
|
+
* **MySQL** dialect. Diverges in: **no `RETURNING`** (the store's OCC falls back
|
|
63
|
+
* to affected-rows — which requires the connection's `CLIENT_FOUND_ROWS` flag so
|
|
64
|
+
* a no-op update still reports a matched row, see `buildMysqlExec`); bounded
|
|
65
|
+
* `VARCHAR` keys (TEXT can't be a primary key / unindexed); a TEXT/BLOB index key
|
|
66
|
+
* prefix; and `ER_DUP_ENTRY` (errno 1062) unique violations. (Drizzle's MySQL
|
|
67
|
+
* dialect supplies the backtick identifiers + `ON DUPLICATE KEY` upserts.)
|
|
68
|
+
*/
|
|
44
69
|
declare const mysqlDialect: SqlDialect;
|
|
45
70
|
/** Which engine a Hyperdrive-backed `.global()` store targets. */
|
|
46
71
|
type HyperdriveEngine = "mysql" | "postgres";
|
|
@@ -52,16 +77,12 @@ interface CreateHyperdriveGlobalCtxDbOptions extends Omit<SqlCtxDbOptions, "dial
|
|
|
52
77
|
exec: SqlExec;
|
|
53
78
|
}
|
|
54
79
|
/**
|
|
55
|
-
* Build a reactive `.global()` writer backed by a Hyperdrive-reachable
|
|
56
|
-
* Postgres/MySQL database. Pass a built {@link SqlExec} (via {@link buildPgExec}/
|
|
57
|
-
* {@link buildMysqlExec}) and the matching `engine`; everything else mirrors the
|
|
58
|
-
* D1 store options.
|
|
59
|
-
*/
|
|
60
|
-
declare const createHyperdriveGlobalCtxDb: ({
|
|
61
|
-
engine,
|
|
62
|
-
exec,
|
|
63
|
-
...rest
|
|
64
|
-
}: CreateHyperdriveGlobalCtxDbOptions) => DatabaseWriterLike;
|
|
80
|
+
* Build a reactive `.global()` writer backed by a Hyperdrive-reachable
|
|
81
|
+
* Postgres/MySQL database. Pass a built {@link SqlExec} (via {@link buildPgExec}/
|
|
82
|
+
* {@link buildMysqlExec}) and the matching `engine`; everything else mirrors the
|
|
83
|
+
* D1 store options.
|
|
84
|
+
*/
|
|
85
|
+
declare const createHyperdriveGlobalCtxDb: ({ engine, exec, ...rest }: CreateHyperdriveGlobalCtxDbOptions) => DatabaseWriterLike;
|
|
65
86
|
/** Convenience: a **Postgres** `.global()` writer from a row-client (postgres.js/pg over Hyperdrive). */
|
|
66
87
|
declare const createPostgresGlobalCtxDb: (client: RowClient, options: Omit<CreateHyperdriveGlobalCtxDbOptions, "engine" | "exec">) => DatabaseWriterLike;
|
|
67
88
|
/** Convenience: a **MySQL** `.global()` writer from a `mysql2/promise` connection/pool (created with `flags: ["FOUND_ROWS"]`). */
|
package/dist/global.mjs
CHANGED
|
@@ -1,9 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { postgresDialect, mysqlDialect } from './packem_shared/mysqlDialect-oNhZ58s8.mjs';
|
|
3
|
-
import { buildMysqlExec, buildPgExec } from './packem_shared/buildMysqlExec-DBbCjyq3.mjs';
|
|
4
|
-
|
|
5
|
-
const createHyperdriveGlobalCtxDb = ({ engine, exec, ...rest }) => createSqlCtxDb({ ...rest, dialect: engine === "postgres" ? postgresDialect : mysqlDialect, exec });
|
|
6
|
-
const createPostgresGlobalCtxDb = (client, options) => createHyperdriveGlobalCtxDb({ ...options, engine: "postgres", exec: buildPgExec(client) });
|
|
7
|
-
const createMysqlGlobalCtxDb = (connection, options) => createHyperdriveGlobalCtxDb({ ...options, engine: "mysql", exec: buildMysqlExec(connection) });
|
|
8
|
-
|
|
9
|
-
export { buildMysqlExec, buildPgExec, createHyperdriveGlobalCtxDb, createMysqlGlobalCtxDb, createPostgresGlobalCtxDb, mysqlDialect, postgresDialect };
|
|
1
|
+
import{createSqlCtxDb as o}from"@lunora/sql-store";import{postgresDialect as c,mysqlDialect as s}from"./packem_shared/mysqlDialect-BIV-y3MW.mjs";import{buildMysqlExec as i,buildPgExec as x}from"./packem_shared/buildMysqlExec-zneG1aUr.mjs";const r=({engine:e,exec:t,...l})=>o({...l,dialect:e==="postgres"?c:s,exec:t}),m=(e,t)=>r({...t,engine:"postgres",exec:x(e)}),p=(e,t)=>r({...t,engine:"mysql",exec:i(e)});export{i as buildMysqlExec,x as buildPgExec,r as createHyperdriveGlobalCtxDb,p as createMysqlGlobalCtxDb,m as createPostgresGlobalCtxDb,s as mysqlDialect,c as postgresDialect};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,165 +1,100 @@
|
|
|
1
|
+
import { H as HyperdriveLike, a as HyperdriveConnection, M as Mysql2Like, S as SqlClient, N as NodePgLike, P as PostgresJsLike } from "./packem_shared/types.d-DE1NYxyA.mjs";
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* deliberately
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
/** Port Hyperdrive presents to the driver. */
|
|
33
|
-
port: number;
|
|
34
|
-
/** User component of the connection. */
|
|
35
|
-
user: string;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* The connection config surfaced by {@link import("./create-hyperdrive").createHyperdrive | createHyperdrive}: the raw
|
|
39
|
-
* `connectionString` plus the discrete parts, ready to hand to a driver.
|
|
40
|
-
*/
|
|
41
|
-
interface HyperdriveConnection {
|
|
42
|
-
/** Database name. */
|
|
43
|
-
database: string;
|
|
44
|
-
/** Host (Hyperdrive's local proxy). */
|
|
45
|
-
host: string;
|
|
46
|
-
/** Password. */
|
|
47
|
-
password: string;
|
|
48
|
-
/** Port. */
|
|
49
|
-
port: number;
|
|
50
|
-
/** User. */
|
|
51
|
-
user: string;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* The driver-agnostic SQL surface bound to `ctx.sql` on **`ActionCtx` only**.
|
|
55
|
-
*
|
|
56
|
-
* This is the exact type the generated ctx imports as
|
|
57
|
-
* `import("@lunora/hyperdrive").SqlClient`. Keep the name and shape stable — the
|
|
58
|
-
* codegen ctx wiring (Phase 1) depends on it.
|
|
59
|
-
*
|
|
60
|
-
* It is intentionally minimal — a single parameterised `query` — so it maps onto
|
|
61
|
-
* `postgres` (postgres.js), `pg` (node-postgres) and `mysql2` alike via the
|
|
62
|
-
* {@link import("./create-hyperdrive").fromPostgresJs | fromPostgresJs} /
|
|
63
|
-
* {@link import("./create-hyperdrive").fromNodePg | fromNodePg} /
|
|
64
|
-
* {@link import("./create-hyperdrive").fromMysql2 | fromMysql2} adapters. Use
|
|
65
|
-
* positional placeholders that match your driver (`$1, $2` for Postgres, `?` for
|
|
66
|
-
* MySQL); the package does not rewrite SQL.
|
|
67
|
-
*
|
|
68
|
-
* Reminder (also on the emitted JSDoc): non-deterministic, action-only,
|
|
69
|
-
* non-reactive — writes here are not tracked by Lunora live queries.
|
|
70
|
-
*/
|
|
71
|
-
interface SqlClient {
|
|
72
|
-
/**
|
|
73
|
-
* Run a parameterised SQL statement and return the result rows.
|
|
74
|
-
* @param text SQL text with driver-native positional placeholders.
|
|
75
|
-
* @param params Bound parameter values, positionally matched to `text`.
|
|
76
|
-
* @returns The rows the statement produced (empty for non-`SELECT`s that
|
|
77
|
-
* return no rows).
|
|
78
|
-
*/
|
|
79
|
-
query: <Row = Record<string, unknown>>(text: string, params?: ReadonlyArray<unknown>) => Promise<Row[]>;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Structural projection of a `pg` (node-postgres) `Client`/`Pool`. Only the
|
|
83
|
-
* `query` method `fromNodePg` calls is required, kept structural for testing.
|
|
84
|
-
*/
|
|
85
|
-
interface NodePgLike {
|
|
86
|
-
query: (text: string, params?: ReadonlyArray<unknown>) => Promise<{
|
|
87
|
-
rows: unknown[];
|
|
88
|
-
}>;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Structural projection of a `postgres` (postgres.js) tagged-template client.
|
|
92
|
-
* The adapter uses the `.unsafe(text, params)` escape hatch so callers keep
|
|
93
|
-
* full control of the parameter list.
|
|
94
|
-
*/
|
|
95
|
-
interface PostgresJsLike {
|
|
96
|
-
unsafe: (text: string, params?: ReadonlyArray<unknown>) => Promise<unknown>;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Structural projection of a `mysql2/promise` connection/pool. `mysql2`'s
|
|
100
|
-
* `execute` resolves to a `[rows, fields]` tuple; the adapter takes the first
|
|
101
|
-
* element as the rows.
|
|
102
|
-
*/
|
|
103
|
-
interface Mysql2Like {
|
|
104
|
-
execute: (text: string, params?: ReadonlyArray<unknown>) => Promise<[unknown, unknown]>;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Surface a Cloudflare Hyperdrive binding as a connection ready to feed a
|
|
108
|
-
* user-supplied SQL driver.
|
|
109
|
-
*
|
|
110
|
-
* `@lunora/hyperdrive` deliberately **bundles no driver** — `postgres`, `pg` and
|
|
111
|
-
* `mysql2` are heavy and the choice is the user's (they are `optional`
|
|
112
|
-
* `peerDependencies`, never `dependencies`). This factory's only job is to lift
|
|
113
|
-
* the binding's connection details out; the user constructs their own driver
|
|
114
|
-
* from `connectionString` and wraps it with one of the {@link fromPostgresJs} /
|
|
115
|
-
* {@link fromNodePg} / {@link fromMysql2} adapters to get a {@link SqlClient}.
|
|
116
|
-
* @example
|
|
117
|
-
* ```ts
|
|
118
|
-
* import { createHyperdrive, fromPostgresJs } from "@lunora/hyperdrive";
|
|
119
|
-
* import postgres from "postgres";
|
|
120
|
-
*
|
|
121
|
-
* // inside an action (never a query/mutation):
|
|
122
|
-
* const { connectionString } = createHyperdrive(env.HYPERDRIVE);
|
|
123
|
-
* ctx.sql = fromPostgresJs(postgres(connectionString));
|
|
124
|
-
* const rows = await ctx.sql.query("select id from users where org = $1", [orgId]);
|
|
125
|
-
* ```
|
|
126
|
-
* @remarks
|
|
127
|
-
* Hyperdrive talks to an **external** database Lunora has no visibility into.
|
|
128
|
-
* Queries through `ctx.sql` are non-deterministic (action-only — enforced by the
|
|
129
|
-
* `hyperdrive_outside_action` advisor lint) and external writes are NOT tracked
|
|
130
|
-
* by Lunora live queries: subscriptions will not re-run when external rows
|
|
131
|
-
* change. Use Hyperdrive to *integrate* an existing DB from an action; if you
|
|
132
|
-
* want that data to be reactive, write a projection of it into a `defineSchema`
|
|
133
|
-
* DO/D1 table.
|
|
134
|
-
* @param binding The `env.HYPERDRIVE` binding (or a structural double).
|
|
135
|
-
* @returns The raw `connectionString` plus the discrete connection parts.
|
|
136
|
-
*/
|
|
3
|
+
* Surface a Cloudflare Hyperdrive binding as a connection ready to feed a
|
|
4
|
+
* user-supplied SQL driver.
|
|
5
|
+
*
|
|
6
|
+
* `@lunora/hyperdrive` deliberately **bundles no driver** — `postgres`, `pg` and
|
|
7
|
+
* `mysql2` are heavy and the choice is the user's (they are `optional`
|
|
8
|
+
* `peerDependencies`, never `dependencies`). This factory's only job is to lift
|
|
9
|
+
* the binding's connection details out; the user constructs their own driver
|
|
10
|
+
* from `connectionString` and wraps it with one of the {@link fromPostgresJs} /
|
|
11
|
+
* {@link fromNodePg} / {@link fromMysql2} adapters to get a {@link SqlClient}.
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { createHyperdrive, fromPostgresJs } from "@lunora/hyperdrive";
|
|
15
|
+
* import postgres from "postgres";
|
|
16
|
+
*
|
|
17
|
+
* // inside an action (never a query/mutation):
|
|
18
|
+
* const { connectionString } = createHyperdrive(env.HYPERDRIVE);
|
|
19
|
+
* ctx.sql = fromPostgresJs(postgres(connectionString));
|
|
20
|
+
* const rows = await ctx.sql.query("select id from users where org = $1", [orgId]);
|
|
21
|
+
* ```
|
|
22
|
+
* @remarks
|
|
23
|
+
* Hyperdrive talks to an **external** database Lunora has no visibility into.
|
|
24
|
+
* Queries through `ctx.sql` are non-deterministic (action-only — enforced by the
|
|
25
|
+
* `hyperdrive_outside_action` advisor lint) and external writes are NOT tracked
|
|
26
|
+
* by Lunora live queries: subscriptions will not re-run when external rows
|
|
27
|
+
* change. Use Hyperdrive to *integrate* an existing DB from an action; if you
|
|
28
|
+
* want that data to be reactive, write a projection of it into a `defineSchema`
|
|
29
|
+
* DO/D1 table.
|
|
30
|
+
* @param binding The `env.HYPERDRIVE` binding (or a structural double).
|
|
31
|
+
* @returns The raw `connectionString` plus the discrete connection parts.
|
|
32
|
+
*/
|
|
137
33
|
declare const createHyperdrive: (binding: HyperdriveLike) => {
|
|
138
34
|
config: HyperdriveConnection;
|
|
139
35
|
connectionString: string;
|
|
140
36
|
};
|
|
141
37
|
/**
|
|
142
|
-
* Wrap a `postgres` (postgres.js) client as a {@link SqlClient}.
|
|
143
|
-
*
|
|
144
|
-
* Uses the driver's `.unsafe(text, params)` escape hatch so the caller supplies
|
|
145
|
-
* a plain SQL string with `$1, $2, …` placeholders and a positional params
|
|
146
|
-
* array. postgres.js's `.unsafe` resolves to a row array.
|
|
147
|
-
*/
|
|
38
|
+
* Wrap a `postgres` (postgres.js) client as a {@link SqlClient}.
|
|
39
|
+
*
|
|
40
|
+
* Uses the driver's `.unsafe(text, params)` escape hatch so the caller supplies
|
|
41
|
+
* a plain SQL string with `$1, $2, …` placeholders and a positional params
|
|
42
|
+
* array. postgres.js's `.unsafe` resolves to a row array.
|
|
43
|
+
*/
|
|
148
44
|
declare const fromPostgresJs: (client: PostgresJsLike) => SqlClient;
|
|
149
45
|
/**
|
|
150
|
-
* Wrap a `pg` (node-postgres) `Client` or `Pool` as a {@link SqlClient}.
|
|
151
|
-
*
|
|
152
|
-
* node-postgres returns a result object whose `rows` field holds the row array.
|
|
153
|
-
*/
|
|
46
|
+
* Wrap a `pg` (node-postgres) `Client` or `Pool` as a {@link SqlClient}.
|
|
47
|
+
*
|
|
48
|
+
* node-postgres returns a result object whose `rows` field holds the row array.
|
|
49
|
+
*/
|
|
154
50
|
declare const fromNodePg: (client: NodePgLike) => SqlClient;
|
|
155
51
|
/**
|
|
156
|
-
* Wrap a `mysql2/promise` connection or pool as a {@link SqlClient}.
|
|
157
|
-
*
|
|
158
|
-
* Use `?` placeholders (MySQL positional syntax). `mysql2`'s `execute` resolves
|
|
159
|
-
* to a `[rows, fields]` tuple; the adapter returns the first element. For a
|
|
160
|
-
* non-`SELECT` (DML), `mysql2` yields a `ResultSetHeader` object rather than a
|
|
161
|
-
* row array, so the adapter normalises that to `[]` — matching the empty-array
|
|
162
|
-
* contract the postgres.js / node-postgres adapters already honour.
|
|
163
|
-
*/
|
|
52
|
+
* Wrap a `mysql2/promise` connection or pool as a {@link SqlClient}.
|
|
53
|
+
*
|
|
54
|
+
* Use `?` placeholders (MySQL positional syntax). `mysql2`'s `execute` resolves
|
|
55
|
+
* to a `[rows, fields]` tuple; the adapter returns the first element. For a
|
|
56
|
+
* non-`SELECT` (DML), `mysql2` yields a `ResultSetHeader` object rather than a
|
|
57
|
+
* row array, so the adapter normalises that to `[]` — matching the empty-array
|
|
58
|
+
* contract the postgres.js / node-postgres adapters already honour.
|
|
59
|
+
*/
|
|
164
60
|
declare const fromMysql2: (connection: Mysql2Like) => SqlClient;
|
|
165
|
-
|
|
61
|
+
/** How an external row maps to a Lunora document. */
|
|
62
|
+
interface ProjectOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Column whose value becomes the Lunora `_id` (stringified). Defaults to `"id"`.
|
|
65
|
+
* With no `map`, this column is dropped from the document body (it lives on as
|
|
66
|
+
* `_id`); with a `map`, the mapper owns the body and `_id` is added from here.
|
|
67
|
+
*/
|
|
68
|
+
idColumn?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Transform an external row into the stored document body. Omit for the default:
|
|
71
|
+
* every selected column except `idColumn` is copied verbatim. The returned object
|
|
72
|
+
* must not include `_id` — it is set from `idColumn`.
|
|
73
|
+
*/
|
|
74
|
+
map?: (row: Record<string, unknown>) => Record<string, unknown>;
|
|
75
|
+
}
|
|
76
|
+
/** Options for {@link pullSourceRows}: the parameterised tenant query plus the row projection. */
|
|
77
|
+
interface PullSourceOptions extends ProjectOptions {
|
|
78
|
+
/** Bound parameter values, positionally matched to `query` (the tenant scope binds here). */
|
|
79
|
+
params?: ReadonlyArray<unknown>;
|
|
80
|
+
/** The full tenant-membership query with driver-native placeholders (`$1` / `?`). */
|
|
81
|
+
query: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Project one external row to a Lunora document: lift `idColumn` to a stringified
|
|
85
|
+
* `_id`, then either apply `map` or copy every other column verbatim. Throws when
|
|
86
|
+
* the id column is missing/nullish so a misconfigured query fails loudly rather than
|
|
87
|
+
* materializing rows under an `"undefined"` id.
|
|
88
|
+
*
|
|
89
|
+
* Delegates to `@lunora/shard-engine`'s `liftSourceId` — the single id-lift the declarative
|
|
90
|
+
* `.source()` poll loop also uses — so the manual bridge and the codegen path can
|
|
91
|
+
* never diverge in their missing-id handling.
|
|
92
|
+
*/
|
|
93
|
+
declare const projectSourceRow: (row: Record<string, unknown>, options?: ProjectOptions) => Record<string, unknown>;
|
|
94
|
+
/**
|
|
95
|
+
* Run a parameterised tenant query against Hyperdrive and project every row to a
|
|
96
|
+
* Lunora document ready to hand to `materializeExternalRows`. Call this inside an
|
|
97
|
+
* **action** (where `ctx.sql` lives); pass the result to a mutation for the write.
|
|
98
|
+
*/
|
|
99
|
+
declare const pullSourceRows: (sql: SqlClient, options: PullSourceOptions) => Promise<Record<string, unknown>[]>;
|
|
100
|
+
export { type HyperdriveConnection, type HyperdriveLike, type Mysql2Like, type NodePgLike, type PostgresJsLike, type ProjectOptions, type PullSourceOptions, type SqlClient, createHyperdrive, fromMysql2, fromNodePg, fromPostgresJs, projectSourceRow, pullSourceRows };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,165 +1,100 @@
|
|
|
1
|
+
import { H as HyperdriveLike, a as HyperdriveConnection, M as Mysql2Like, S as SqlClient, N as NodePgLike, P as PostgresJsLike } from "./packem_shared/types.d-DE1NYxyA.js";
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* deliberately
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
/** Port Hyperdrive presents to the driver. */
|
|
33
|
-
port: number;
|
|
34
|
-
/** User component of the connection. */
|
|
35
|
-
user: string;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* The connection config surfaced by {@link import("./create-hyperdrive").createHyperdrive | createHyperdrive}: the raw
|
|
39
|
-
* `connectionString` plus the discrete parts, ready to hand to a driver.
|
|
40
|
-
*/
|
|
41
|
-
interface HyperdriveConnection {
|
|
42
|
-
/** Database name. */
|
|
43
|
-
database: string;
|
|
44
|
-
/** Host (Hyperdrive's local proxy). */
|
|
45
|
-
host: string;
|
|
46
|
-
/** Password. */
|
|
47
|
-
password: string;
|
|
48
|
-
/** Port. */
|
|
49
|
-
port: number;
|
|
50
|
-
/** User. */
|
|
51
|
-
user: string;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* The driver-agnostic SQL surface bound to `ctx.sql` on **`ActionCtx` only**.
|
|
55
|
-
*
|
|
56
|
-
* This is the exact type the generated ctx imports as
|
|
57
|
-
* `import("@lunora/hyperdrive").SqlClient`. Keep the name and shape stable — the
|
|
58
|
-
* codegen ctx wiring (Phase 1) depends on it.
|
|
59
|
-
*
|
|
60
|
-
* It is intentionally minimal — a single parameterised `query` — so it maps onto
|
|
61
|
-
* `postgres` (postgres.js), `pg` (node-postgres) and `mysql2` alike via the
|
|
62
|
-
* {@link import("./create-hyperdrive").fromPostgresJs | fromPostgresJs} /
|
|
63
|
-
* {@link import("./create-hyperdrive").fromNodePg | fromNodePg} /
|
|
64
|
-
* {@link import("./create-hyperdrive").fromMysql2 | fromMysql2} adapters. Use
|
|
65
|
-
* positional placeholders that match your driver (`$1, $2` for Postgres, `?` for
|
|
66
|
-
* MySQL); the package does not rewrite SQL.
|
|
67
|
-
*
|
|
68
|
-
* Reminder (also on the emitted JSDoc): non-deterministic, action-only,
|
|
69
|
-
* non-reactive — writes here are not tracked by Lunora live queries.
|
|
70
|
-
*/
|
|
71
|
-
interface SqlClient {
|
|
72
|
-
/**
|
|
73
|
-
* Run a parameterised SQL statement and return the result rows.
|
|
74
|
-
* @param text SQL text with driver-native positional placeholders.
|
|
75
|
-
* @param params Bound parameter values, positionally matched to `text`.
|
|
76
|
-
* @returns The rows the statement produced (empty for non-`SELECT`s that
|
|
77
|
-
* return no rows).
|
|
78
|
-
*/
|
|
79
|
-
query: <Row = Record<string, unknown>>(text: string, params?: ReadonlyArray<unknown>) => Promise<Row[]>;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Structural projection of a `pg` (node-postgres) `Client`/`Pool`. Only the
|
|
83
|
-
* `query` method `fromNodePg` calls is required, kept structural for testing.
|
|
84
|
-
*/
|
|
85
|
-
interface NodePgLike {
|
|
86
|
-
query: (text: string, params?: ReadonlyArray<unknown>) => Promise<{
|
|
87
|
-
rows: unknown[];
|
|
88
|
-
}>;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Structural projection of a `postgres` (postgres.js) tagged-template client.
|
|
92
|
-
* The adapter uses the `.unsafe(text, params)` escape hatch so callers keep
|
|
93
|
-
* full control of the parameter list.
|
|
94
|
-
*/
|
|
95
|
-
interface PostgresJsLike {
|
|
96
|
-
unsafe: (text: string, params?: ReadonlyArray<unknown>) => Promise<unknown>;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Structural projection of a `mysql2/promise` connection/pool. `mysql2`'s
|
|
100
|
-
* `execute` resolves to a `[rows, fields]` tuple; the adapter takes the first
|
|
101
|
-
* element as the rows.
|
|
102
|
-
*/
|
|
103
|
-
interface Mysql2Like {
|
|
104
|
-
execute: (text: string, params?: ReadonlyArray<unknown>) => Promise<[unknown, unknown]>;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Surface a Cloudflare Hyperdrive binding as a connection ready to feed a
|
|
108
|
-
* user-supplied SQL driver.
|
|
109
|
-
*
|
|
110
|
-
* `@lunora/hyperdrive` deliberately **bundles no driver** — `postgres`, `pg` and
|
|
111
|
-
* `mysql2` are heavy and the choice is the user's (they are `optional`
|
|
112
|
-
* `peerDependencies`, never `dependencies`). This factory's only job is to lift
|
|
113
|
-
* the binding's connection details out; the user constructs their own driver
|
|
114
|
-
* from `connectionString` and wraps it with one of the {@link fromPostgresJs} /
|
|
115
|
-
* {@link fromNodePg} / {@link fromMysql2} adapters to get a {@link SqlClient}.
|
|
116
|
-
* @example
|
|
117
|
-
* ```ts
|
|
118
|
-
* import { createHyperdrive, fromPostgresJs } from "@lunora/hyperdrive";
|
|
119
|
-
* import postgres from "postgres";
|
|
120
|
-
*
|
|
121
|
-
* // inside an action (never a query/mutation):
|
|
122
|
-
* const { connectionString } = createHyperdrive(env.HYPERDRIVE);
|
|
123
|
-
* ctx.sql = fromPostgresJs(postgres(connectionString));
|
|
124
|
-
* const rows = await ctx.sql.query("select id from users where org = $1", [orgId]);
|
|
125
|
-
* ```
|
|
126
|
-
* @remarks
|
|
127
|
-
* Hyperdrive talks to an **external** database Lunora has no visibility into.
|
|
128
|
-
* Queries through `ctx.sql` are non-deterministic (action-only — enforced by the
|
|
129
|
-
* `hyperdrive_outside_action` advisor lint) and external writes are NOT tracked
|
|
130
|
-
* by Lunora live queries: subscriptions will not re-run when external rows
|
|
131
|
-
* change. Use Hyperdrive to *integrate* an existing DB from an action; if you
|
|
132
|
-
* want that data to be reactive, write a projection of it into a `defineSchema`
|
|
133
|
-
* DO/D1 table.
|
|
134
|
-
* @param binding The `env.HYPERDRIVE` binding (or a structural double).
|
|
135
|
-
* @returns The raw `connectionString` plus the discrete connection parts.
|
|
136
|
-
*/
|
|
3
|
+
* Surface a Cloudflare Hyperdrive binding as a connection ready to feed a
|
|
4
|
+
* user-supplied SQL driver.
|
|
5
|
+
*
|
|
6
|
+
* `@lunora/hyperdrive` deliberately **bundles no driver** — `postgres`, `pg` and
|
|
7
|
+
* `mysql2` are heavy and the choice is the user's (they are `optional`
|
|
8
|
+
* `peerDependencies`, never `dependencies`). This factory's only job is to lift
|
|
9
|
+
* the binding's connection details out; the user constructs their own driver
|
|
10
|
+
* from `connectionString` and wraps it with one of the {@link fromPostgresJs} /
|
|
11
|
+
* {@link fromNodePg} / {@link fromMysql2} adapters to get a {@link SqlClient}.
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { createHyperdrive, fromPostgresJs } from "@lunora/hyperdrive";
|
|
15
|
+
* import postgres from "postgres";
|
|
16
|
+
*
|
|
17
|
+
* // inside an action (never a query/mutation):
|
|
18
|
+
* const { connectionString } = createHyperdrive(env.HYPERDRIVE);
|
|
19
|
+
* ctx.sql = fromPostgresJs(postgres(connectionString));
|
|
20
|
+
* const rows = await ctx.sql.query("select id from users where org = $1", [orgId]);
|
|
21
|
+
* ```
|
|
22
|
+
* @remarks
|
|
23
|
+
* Hyperdrive talks to an **external** database Lunora has no visibility into.
|
|
24
|
+
* Queries through `ctx.sql` are non-deterministic (action-only — enforced by the
|
|
25
|
+
* `hyperdrive_outside_action` advisor lint) and external writes are NOT tracked
|
|
26
|
+
* by Lunora live queries: subscriptions will not re-run when external rows
|
|
27
|
+
* change. Use Hyperdrive to *integrate* an existing DB from an action; if you
|
|
28
|
+
* want that data to be reactive, write a projection of it into a `defineSchema`
|
|
29
|
+
* DO/D1 table.
|
|
30
|
+
* @param binding The `env.HYPERDRIVE` binding (or a structural double).
|
|
31
|
+
* @returns The raw `connectionString` plus the discrete connection parts.
|
|
32
|
+
*/
|
|
137
33
|
declare const createHyperdrive: (binding: HyperdriveLike) => {
|
|
138
34
|
config: HyperdriveConnection;
|
|
139
35
|
connectionString: string;
|
|
140
36
|
};
|
|
141
37
|
/**
|
|
142
|
-
* Wrap a `postgres` (postgres.js) client as a {@link SqlClient}.
|
|
143
|
-
*
|
|
144
|
-
* Uses the driver's `.unsafe(text, params)` escape hatch so the caller supplies
|
|
145
|
-
* a plain SQL string with `$1, $2, …` placeholders and a positional params
|
|
146
|
-
* array. postgres.js's `.unsafe` resolves to a row array.
|
|
147
|
-
*/
|
|
38
|
+
* Wrap a `postgres` (postgres.js) client as a {@link SqlClient}.
|
|
39
|
+
*
|
|
40
|
+
* Uses the driver's `.unsafe(text, params)` escape hatch so the caller supplies
|
|
41
|
+
* a plain SQL string with `$1, $2, …` placeholders and a positional params
|
|
42
|
+
* array. postgres.js's `.unsafe` resolves to a row array.
|
|
43
|
+
*/
|
|
148
44
|
declare const fromPostgresJs: (client: PostgresJsLike) => SqlClient;
|
|
149
45
|
/**
|
|
150
|
-
* Wrap a `pg` (node-postgres) `Client` or `Pool` as a {@link SqlClient}.
|
|
151
|
-
*
|
|
152
|
-
* node-postgres returns a result object whose `rows` field holds the row array.
|
|
153
|
-
*/
|
|
46
|
+
* Wrap a `pg` (node-postgres) `Client` or `Pool` as a {@link SqlClient}.
|
|
47
|
+
*
|
|
48
|
+
* node-postgres returns a result object whose `rows` field holds the row array.
|
|
49
|
+
*/
|
|
154
50
|
declare const fromNodePg: (client: NodePgLike) => SqlClient;
|
|
155
51
|
/**
|
|
156
|
-
* Wrap a `mysql2/promise` connection or pool as a {@link SqlClient}.
|
|
157
|
-
*
|
|
158
|
-
* Use `?` placeholders (MySQL positional syntax). `mysql2`'s `execute` resolves
|
|
159
|
-
* to a `[rows, fields]` tuple; the adapter returns the first element. For a
|
|
160
|
-
* non-`SELECT` (DML), `mysql2` yields a `ResultSetHeader` object rather than a
|
|
161
|
-
* row array, so the adapter normalises that to `[]` — matching the empty-array
|
|
162
|
-
* contract the postgres.js / node-postgres adapters already honour.
|
|
163
|
-
*/
|
|
52
|
+
* Wrap a `mysql2/promise` connection or pool as a {@link SqlClient}.
|
|
53
|
+
*
|
|
54
|
+
* Use `?` placeholders (MySQL positional syntax). `mysql2`'s `execute` resolves
|
|
55
|
+
* to a `[rows, fields]` tuple; the adapter returns the first element. For a
|
|
56
|
+
* non-`SELECT` (DML), `mysql2` yields a `ResultSetHeader` object rather than a
|
|
57
|
+
* row array, so the adapter normalises that to `[]` — matching the empty-array
|
|
58
|
+
* contract the postgres.js / node-postgres adapters already honour.
|
|
59
|
+
*/
|
|
164
60
|
declare const fromMysql2: (connection: Mysql2Like) => SqlClient;
|
|
165
|
-
|
|
61
|
+
/** How an external row maps to a Lunora document. */
|
|
62
|
+
interface ProjectOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Column whose value becomes the Lunora `_id` (stringified). Defaults to `"id"`.
|
|
65
|
+
* With no `map`, this column is dropped from the document body (it lives on as
|
|
66
|
+
* `_id`); with a `map`, the mapper owns the body and `_id` is added from here.
|
|
67
|
+
*/
|
|
68
|
+
idColumn?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Transform an external row into the stored document body. Omit for the default:
|
|
71
|
+
* every selected column except `idColumn` is copied verbatim. The returned object
|
|
72
|
+
* must not include `_id` — it is set from `idColumn`.
|
|
73
|
+
*/
|
|
74
|
+
map?: (row: Record<string, unknown>) => Record<string, unknown>;
|
|
75
|
+
}
|
|
76
|
+
/** Options for {@link pullSourceRows}: the parameterised tenant query plus the row projection. */
|
|
77
|
+
interface PullSourceOptions extends ProjectOptions {
|
|
78
|
+
/** Bound parameter values, positionally matched to `query` (the tenant scope binds here). */
|
|
79
|
+
params?: ReadonlyArray<unknown>;
|
|
80
|
+
/** The full tenant-membership query with driver-native placeholders (`$1` / `?`). */
|
|
81
|
+
query: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Project one external row to a Lunora document: lift `idColumn` to a stringified
|
|
85
|
+
* `_id`, then either apply `map` or copy every other column verbatim. Throws when
|
|
86
|
+
* the id column is missing/nullish so a misconfigured query fails loudly rather than
|
|
87
|
+
* materializing rows under an `"undefined"` id.
|
|
88
|
+
*
|
|
89
|
+
* Delegates to `@lunora/shard-engine`'s `liftSourceId` — the single id-lift the declarative
|
|
90
|
+
* `.source()` poll loop also uses — so the manual bridge and the codegen path can
|
|
91
|
+
* never diverge in their missing-id handling.
|
|
92
|
+
*/
|
|
93
|
+
declare const projectSourceRow: (row: Record<string, unknown>, options?: ProjectOptions) => Record<string, unknown>;
|
|
94
|
+
/**
|
|
95
|
+
* Run a parameterised tenant query against Hyperdrive and project every row to a
|
|
96
|
+
* Lunora document ready to hand to `materializeExternalRows`. Call this inside an
|
|
97
|
+
* **action** (where `ctx.sql` lives); pass the result to a mutation for the write.
|
|
98
|
+
*/
|
|
99
|
+
declare const pullSourceRows: (sql: SqlClient, options: PullSourceOptions) => Promise<Record<string, unknown>[]>;
|
|
100
|
+
export { type HyperdriveConnection, type HyperdriveLike, type Mysql2Like, type NodePgLike, type PostgresJsLike, type ProjectOptions, type PullSourceOptions, type SqlClient, createHyperdrive, fromMysql2, fromNodePg, fromPostgresJs, projectSourceRow, pullSourceRows };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import{createHyperdrive as e,fromMysql2 as f,fromNodePg as m,fromPostgresJs as p}from"./packem_shared/createHyperdrive-DCe1WINF.mjs";import{projectSourceRow as t,pullSourceRows as c}from"./packem_shared/projectSourceRow-Dw1DLzyv.mjs";export{e as createHyperdrive,f as fromMysql2,m as fromNodePg,p as fromPostgresJs,t as projectSourceRow,c as pullSourceRows};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const r=s=>({all:(e,a)=>s.query(e,a),batch:async e=>{await Promise.all(e.map(a=>s.query(a.sql,a.params)))},run:async(e,a)=>(await s.query(e,a),{rowsAffected:0})}),t=s=>({all:async(e,a)=>{const[c]=await s.execute(e,a);return c},batch:async e=>{await Promise.all(e.map(a=>s.execute(a.sql,a.params)))},run:async(e,a)=>{const[c]=await s.execute(e,a);return{rowsAffected:c.affectedRows??0}}});export{t as buildMysqlExec,r as buildPgExec};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const o=e=>({config:{database:e.database,host:e.host,password:e.password,port:e.port,user:e.user},connectionString:e.connectionString}),t=e=>({query:async(r,s=[])=>await e.unsafe(r,s)}),n=e=>({query:async(r,s=[])=>(await e.query(r,s)).rows}),c=e=>({query:async(r,s=[])=>{const[a]=await e.execute(r,s);return Array.isArray(a)?a:[]}});export{o as createHyperdrive,c as fromMysql2,n as fromNodePg,t as fromPostgresJs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{sqliteEncode as E,sqliteDecode as m}from"@lunora/sql-store";import{sql as t}from"drizzle-orm";const i="__id__",n="__vector__",s=e=>t`${t.identifier(e)}.${t.identifier(n)}`,T=e=>t`to_tsvector('simple', ${e})`,o=e=>t`to_tsquery('simple', ${e.map((r,a)=>a===e.length-1?`${r}:*`:r).join(" & ")})`,l=/duplicate key value violates unique constraint/iu,c=e=>{switch(e){case"array":case"object":case"record":return"JSON";case"bigint":return"VARCHAR(64)";case"boolean":return"TINYINT";case"bytes":return"LONGBLOB";case"date":case"number":case"timestamp":return"DOUBLE";default:return"LONGTEXT"}},u=/TEXT|BLOB/u,p={companionTypes:{autoincrementPrimaryKey:"BIGSERIAL PRIMARY KEY",integer:"INTEGER",key:"TEXT",real:"DOUBLE PRECISION",text:"TEXT"},columnType:e=>{switch(e){case"boolean":return"INTEGER";case"bytes":return"BYTEA";case"date":case"number":case"timestamp":return"DOUBLE PRECISION";default:return"TEXT"}},decode:m,encode:E,frameworkColumns:()=>[{name:"id",type:"TEXT PRIMARY KEY"},{name:"_creationTime",type:"DOUBLE PRECISION NOT NULL"}],isUniqueViolation:e=>{const{code:r}=e;return r==="23505"||e instanceof Error&&l.test(e.message)},name:"postgres",supportsFts5:!1,nativeTextSearch:{createCompanion:(e,r)=>t`CREATE TABLE IF NOT EXISTS ${t.identifier(e)} (${t.identifier(i)} ${t.raw(r)} PRIMARY KEY, ${t.identifier(n)} tsvector)`,createIndexes:e=>[t`CREATE INDEX IF NOT EXISTS ${t.identifier(`${e}__gin`)} ON ${t.identifier(e)} USING GIN (${t.identifier(n)})`],indexDocument:(e,r,a)=>t`INSERT INTO ${t.identifier(e)} (${t.identifier(i)}, ${t.identifier(n)}) VALUES (${r}, ${T(a)})`,matches:(e,r)=>t`${s(e)} @@ ${o(r)}`,rank:(e,r)=>t`ts_rank_cd(${s(e)}, ${o(r)})`},supportsReturning:!0,textPatternOperatorClass:"text_pattern_ops",tableExists:e=>t`SELECT table_name FROM information_schema.tables WHERE table_schema = ANY (current_schemas(false)) AND table_name = ${e}`},I={affectedRows:e=>e.rowsAffected,companionTypes:{autoincrementPrimaryKey:"BIGINT AUTO_INCREMENT PRIMARY KEY",integer:"INTEGER",key:"VARCHAR(768)",real:"DOUBLE",text:"LONGTEXT"},columnType:c,decode:m,encode:E,frameworkColumns:()=>[{name:"id",type:"VARCHAR(768) PRIMARY KEY"},{name:"_creationTime",type:"DOUBLE NOT NULL"}],indexKeyPrefix:e=>u.test(c(e))?191:void 0,isUniqueViolation:e=>{const r=e;return r.errno===1062||r.code==="ER_DUP_ENTRY"},name:"mysql",supportsFts5:!1,supportsReturning:!1,tableExists:e=>t`SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ${e}`};export{I as mysqlDialect,p as postgresDialect};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{liftSourceId as m}from"@lunora/shard-engine";const s=(o,r={})=>m(o,r),i=async(o,r)=>{const{idColumn:a,map:e,params:p,query:t}=r;return(await o.query(t,p)).map(u=>s(u,{idColumn:a,map:e}))};export{s as projectSourceRow,i as pullSourceRows};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for `@lunora/hyperdrive`.
|
|
3
|
+
*
|
|
4
|
+
* Hyperdrive points at a database **Lunora does not own**. Everything here is
|
|
5
|
+
* deliberately structural (no hard dependency on `@cloudflare/workers-types` or
|
|
6
|
+
* any SQL driver) so unit tests can pass plain-object doubles, exactly like the
|
|
7
|
+
* `D1DatabaseLike` projection in `@lunora/d1`.
|
|
8
|
+
*
|
|
9
|
+
* The hard constraint, restated wherever this surface is used: Hyperdrive
|
|
10
|
+
* queries are **non-deterministic** (forbidden in `query`/`mutation`, allowed
|
|
11
|
+
* only in `action`s — see the `hyperdrive_outside_action` advisor lint), and
|
|
12
|
+
* external writes are **invisible to Lunora live queries** — a subscription will
|
|
13
|
+
* NOT re-run when an external Postgres/MySQL row changes.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Structural projection of the Cloudflare `Hyperdrive` binding (`env.HYPERDRIVE`).
|
|
17
|
+
*
|
|
18
|
+
* Mirrors the fields of the real `Hyperdrive` from `@cloudflare/workers-types`
|
|
19
|
+
* but stays structural so a unit test can pass a plain object. At runtime only
|
|
20
|
+
* `connectionString` is needed to construct a driver; the discrete connection
|
|
21
|
+
* parts are surfaced for drivers that prefer a config object over a DSN.
|
|
22
|
+
*/
|
|
23
|
+
interface HyperdriveLike {
|
|
24
|
+
/** A connection string Hyperdrive routes through its pooled, cached edge connection. */
|
|
25
|
+
connectionString: string;
|
|
26
|
+
/** Database name component of the connection. */
|
|
27
|
+
database: string;
|
|
28
|
+
/** Host Hyperdrive presents to the driver (the local proxy, not your origin DB). */
|
|
29
|
+
host: string;
|
|
30
|
+
/** Password component of the connection. */
|
|
31
|
+
password: string;
|
|
32
|
+
/** Port Hyperdrive presents to the driver. */
|
|
33
|
+
port: number;
|
|
34
|
+
/** User component of the connection. */
|
|
35
|
+
user: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The connection config surfaced by {@link import("./create-hyperdrive").createHyperdrive | createHyperdrive}: the raw
|
|
39
|
+
* `connectionString` plus the discrete parts, ready to hand to a driver.
|
|
40
|
+
*/
|
|
41
|
+
interface HyperdriveConnection {
|
|
42
|
+
/** Database name. */
|
|
43
|
+
database: string;
|
|
44
|
+
/** Host (Hyperdrive's local proxy). */
|
|
45
|
+
host: string;
|
|
46
|
+
/** Password. */
|
|
47
|
+
password: string;
|
|
48
|
+
/** Port. */
|
|
49
|
+
port: number;
|
|
50
|
+
/** User. */
|
|
51
|
+
user: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The driver-agnostic SQL surface bound to `ctx.sql` on **`ActionCtx` only**.
|
|
55
|
+
*
|
|
56
|
+
* This is the exact type the generated ctx imports as
|
|
57
|
+
* `import("@lunora/hyperdrive").SqlClient`. Keep the name and shape stable — the
|
|
58
|
+
* codegen ctx wiring (Phase 1) depends on it.
|
|
59
|
+
*
|
|
60
|
+
* It is intentionally minimal — a single parameterised `query` — so it maps onto
|
|
61
|
+
* `postgres` (postgres.js), `pg` (node-postgres) and `mysql2` alike via the
|
|
62
|
+
* {@link import("./create-hyperdrive").fromPostgresJs | fromPostgresJs} /
|
|
63
|
+
* {@link import("./create-hyperdrive").fromNodePg | fromNodePg} /
|
|
64
|
+
* {@link import("./create-hyperdrive").fromMysql2 | fromMysql2} adapters. Use
|
|
65
|
+
* positional placeholders that match your driver (`$1, $2` for Postgres, `?` for
|
|
66
|
+
* MySQL); the package does not rewrite SQL.
|
|
67
|
+
*
|
|
68
|
+
* Reminder (also on the emitted JSDoc): non-deterministic, action-only,
|
|
69
|
+
* non-reactive — writes here are not tracked by Lunora live queries.
|
|
70
|
+
*/
|
|
71
|
+
interface SqlClient {
|
|
72
|
+
/**
|
|
73
|
+
* Run a parameterised SQL statement and return the result rows.
|
|
74
|
+
*
|
|
75
|
+
* SECURITY: `text` is executed verbatim — the package never rewrites or
|
|
76
|
+
* escapes it. NEVER interpolate untrusted/user input into `text`; put every
|
|
77
|
+
* value in `params` and reference it with a positional placeholder (`$1`/`?`).
|
|
78
|
+
* Building `text` by string-concatenating request data is a SQL-injection
|
|
79
|
+
* sink against your own Postgres/MySQL. Identifiers (table/column names) can't
|
|
80
|
+
* be parameterised — allowlist them against a fixed set, don't interpolate.
|
|
81
|
+
* @param text SQL text with driver-native positional placeholders.
|
|
82
|
+
* @param params Bound parameter values, positionally matched to `text`.
|
|
83
|
+
* @returns The rows the statement produced (empty for non-`SELECT`s that
|
|
84
|
+
* return no rows).
|
|
85
|
+
*/
|
|
86
|
+
query: <Row = Record<string, unknown>>(text: string, params?: ReadonlyArray<unknown>) => Promise<Row[]>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Structural projection of a `pg` (node-postgres) `Client`/`Pool`. Only the
|
|
90
|
+
* `query` method `fromNodePg` calls is required, kept structural for testing.
|
|
91
|
+
*/
|
|
92
|
+
interface NodePgLike {
|
|
93
|
+
query: (text: string, params?: ReadonlyArray<unknown>) => Promise<{
|
|
94
|
+
rows: unknown[];
|
|
95
|
+
}>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Structural projection of a `postgres` (postgres.js) tagged-template client.
|
|
99
|
+
* The adapter uses the `.unsafe(text, params)` escape hatch so callers keep
|
|
100
|
+
* full control of the parameter list.
|
|
101
|
+
*/
|
|
102
|
+
interface PostgresJsLike {
|
|
103
|
+
unsafe: (text: string, params?: ReadonlyArray<unknown>) => Promise<unknown>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Structural projection of a `mysql2/promise` connection/pool. `mysql2`'s
|
|
107
|
+
* `execute` resolves to a `[rows, fields]` tuple; the adapter takes the first
|
|
108
|
+
* element as the rows.
|
|
109
|
+
*/
|
|
110
|
+
interface Mysql2Like {
|
|
111
|
+
execute: (text: string, params?: ReadonlyArray<unknown>) => Promise<[unknown, unknown]>;
|
|
112
|
+
}
|
|
113
|
+
export { HyperdriveLike as H, Mysql2Like as M, NodePgLike as N, PostgresJsLike as P, SqlClient as S, HyperdriveConnection as a };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for `@lunora/hyperdrive`.
|
|
3
|
+
*
|
|
4
|
+
* Hyperdrive points at a database **Lunora does not own**. Everything here is
|
|
5
|
+
* deliberately structural (no hard dependency on `@cloudflare/workers-types` or
|
|
6
|
+
* any SQL driver) so unit tests can pass plain-object doubles, exactly like the
|
|
7
|
+
* `D1DatabaseLike` projection in `@lunora/d1`.
|
|
8
|
+
*
|
|
9
|
+
* The hard constraint, restated wherever this surface is used: Hyperdrive
|
|
10
|
+
* queries are **non-deterministic** (forbidden in `query`/`mutation`, allowed
|
|
11
|
+
* only in `action`s — see the `hyperdrive_outside_action` advisor lint), and
|
|
12
|
+
* external writes are **invisible to Lunora live queries** — a subscription will
|
|
13
|
+
* NOT re-run when an external Postgres/MySQL row changes.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Structural projection of the Cloudflare `Hyperdrive` binding (`env.HYPERDRIVE`).
|
|
17
|
+
*
|
|
18
|
+
* Mirrors the fields of the real `Hyperdrive` from `@cloudflare/workers-types`
|
|
19
|
+
* but stays structural so a unit test can pass a plain object. At runtime only
|
|
20
|
+
* `connectionString` is needed to construct a driver; the discrete connection
|
|
21
|
+
* parts are surfaced for drivers that prefer a config object over a DSN.
|
|
22
|
+
*/
|
|
23
|
+
interface HyperdriveLike {
|
|
24
|
+
/** A connection string Hyperdrive routes through its pooled, cached edge connection. */
|
|
25
|
+
connectionString: string;
|
|
26
|
+
/** Database name component of the connection. */
|
|
27
|
+
database: string;
|
|
28
|
+
/** Host Hyperdrive presents to the driver (the local proxy, not your origin DB). */
|
|
29
|
+
host: string;
|
|
30
|
+
/** Password component of the connection. */
|
|
31
|
+
password: string;
|
|
32
|
+
/** Port Hyperdrive presents to the driver. */
|
|
33
|
+
port: number;
|
|
34
|
+
/** User component of the connection. */
|
|
35
|
+
user: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The connection config surfaced by {@link import("./create-hyperdrive").createHyperdrive | createHyperdrive}: the raw
|
|
39
|
+
* `connectionString` plus the discrete parts, ready to hand to a driver.
|
|
40
|
+
*/
|
|
41
|
+
interface HyperdriveConnection {
|
|
42
|
+
/** Database name. */
|
|
43
|
+
database: string;
|
|
44
|
+
/** Host (Hyperdrive's local proxy). */
|
|
45
|
+
host: string;
|
|
46
|
+
/** Password. */
|
|
47
|
+
password: string;
|
|
48
|
+
/** Port. */
|
|
49
|
+
port: number;
|
|
50
|
+
/** User. */
|
|
51
|
+
user: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The driver-agnostic SQL surface bound to `ctx.sql` on **`ActionCtx` only**.
|
|
55
|
+
*
|
|
56
|
+
* This is the exact type the generated ctx imports as
|
|
57
|
+
* `import("@lunora/hyperdrive").SqlClient`. Keep the name and shape stable — the
|
|
58
|
+
* codegen ctx wiring (Phase 1) depends on it.
|
|
59
|
+
*
|
|
60
|
+
* It is intentionally minimal — a single parameterised `query` — so it maps onto
|
|
61
|
+
* `postgres` (postgres.js), `pg` (node-postgres) and `mysql2` alike via the
|
|
62
|
+
* {@link import("./create-hyperdrive").fromPostgresJs | fromPostgresJs} /
|
|
63
|
+
* {@link import("./create-hyperdrive").fromNodePg | fromNodePg} /
|
|
64
|
+
* {@link import("./create-hyperdrive").fromMysql2 | fromMysql2} adapters. Use
|
|
65
|
+
* positional placeholders that match your driver (`$1, $2` for Postgres, `?` for
|
|
66
|
+
* MySQL); the package does not rewrite SQL.
|
|
67
|
+
*
|
|
68
|
+
* Reminder (also on the emitted JSDoc): non-deterministic, action-only,
|
|
69
|
+
* non-reactive — writes here are not tracked by Lunora live queries.
|
|
70
|
+
*/
|
|
71
|
+
interface SqlClient {
|
|
72
|
+
/**
|
|
73
|
+
* Run a parameterised SQL statement and return the result rows.
|
|
74
|
+
*
|
|
75
|
+
* SECURITY: `text` is executed verbatim — the package never rewrites or
|
|
76
|
+
* escapes it. NEVER interpolate untrusted/user input into `text`; put every
|
|
77
|
+
* value in `params` and reference it with a positional placeholder (`$1`/`?`).
|
|
78
|
+
* Building `text` by string-concatenating request data is a SQL-injection
|
|
79
|
+
* sink against your own Postgres/MySQL. Identifiers (table/column names) can't
|
|
80
|
+
* be parameterised — allowlist them against a fixed set, don't interpolate.
|
|
81
|
+
* @param text SQL text with driver-native positional placeholders.
|
|
82
|
+
* @param params Bound parameter values, positionally matched to `text`.
|
|
83
|
+
* @returns The rows the statement produced (empty for non-`SELECT`s that
|
|
84
|
+
* return no rows).
|
|
85
|
+
*/
|
|
86
|
+
query: <Row = Record<string, unknown>>(text: string, params?: ReadonlyArray<unknown>) => Promise<Row[]>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Structural projection of a `pg` (node-postgres) `Client`/`Pool`. Only the
|
|
90
|
+
* `query` method `fromNodePg` calls is required, kept structural for testing.
|
|
91
|
+
*/
|
|
92
|
+
interface NodePgLike {
|
|
93
|
+
query: (text: string, params?: ReadonlyArray<unknown>) => Promise<{
|
|
94
|
+
rows: unknown[];
|
|
95
|
+
}>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Structural projection of a `postgres` (postgres.js) tagged-template client.
|
|
99
|
+
* The adapter uses the `.unsafe(text, params)` escape hatch so callers keep
|
|
100
|
+
* full control of the parameter list.
|
|
101
|
+
*/
|
|
102
|
+
interface PostgresJsLike {
|
|
103
|
+
unsafe: (text: string, params?: ReadonlyArray<unknown>) => Promise<unknown>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Structural projection of a `mysql2/promise` connection/pool. `mysql2`'s
|
|
107
|
+
* `execute` resolves to a `[rows, fields]` tuple; the adapter takes the first
|
|
108
|
+
* element as the rows.
|
|
109
|
+
*/
|
|
110
|
+
interface Mysql2Like {
|
|
111
|
+
execute: (text: string, params?: ReadonlyArray<unknown>) => Promise<[unknown, unknown]>;
|
|
112
|
+
}
|
|
113
|
+
export { HyperdriveLike as H, Mysql2Like as M, NodePgLike as N, PostgresJsLike as P, SqlClient as S, HyperdriveConnection as a };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/hyperdrive",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.71",
|
|
4
4
|
"description": "Bring-your-own Postgres/MySQL for Lunora via Cloudflare Hyperdrive: a driver-agnostic, action-only ctx.sql",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -50,8 +50,9 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@lunora/
|
|
54
|
-
"@lunora/
|
|
53
|
+
"@lunora/errors": "1.0.0-alpha.16",
|
|
54
|
+
"@lunora/shard-engine": "1.0.0-alpha.17",
|
|
55
|
+
"@lunora/sql-store": "1.0.0-alpha.71",
|
|
55
56
|
"drizzle-orm": "^0.45.2"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
const buildPgExec = (client) => {
|
|
2
|
-
return {
|
|
3
|
-
all: (sql, params) => client.query(sql, params),
|
|
4
|
-
run: async (sql, params) => {
|
|
5
|
-
await client.query(sql, params);
|
|
6
|
-
return { rowsAffected: 0 };
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
const buildMysqlExec = (connection) => {
|
|
11
|
-
return {
|
|
12
|
-
all: async (sql, params) => {
|
|
13
|
-
const [rows] = await connection.execute(sql, params);
|
|
14
|
-
return rows;
|
|
15
|
-
},
|
|
16
|
-
run: async (sql, params) => {
|
|
17
|
-
const [result] = await connection.execute(sql, params);
|
|
18
|
-
return { rowsAffected: result.affectedRows ?? 0 };
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export { buildMysqlExec, buildPgExec };
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
const createHyperdrive = (binding) => {
|
|
2
|
-
const config = {
|
|
3
|
-
database: binding.database,
|
|
4
|
-
host: binding.host,
|
|
5
|
-
password: binding.password,
|
|
6
|
-
port: binding.port,
|
|
7
|
-
user: binding.user
|
|
8
|
-
};
|
|
9
|
-
return { config, connectionString: binding.connectionString };
|
|
10
|
-
};
|
|
11
|
-
const fromPostgresJs = (client) => {
|
|
12
|
-
return {
|
|
13
|
-
query: async (text, params = []) => {
|
|
14
|
-
const rows = await client.unsafe(text, params);
|
|
15
|
-
return rows;
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
const fromNodePg = (client) => {
|
|
20
|
-
return {
|
|
21
|
-
query: async (text, params = []) => {
|
|
22
|
-
const result = await client.query(text, params);
|
|
23
|
-
return result.rows;
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
const fromMysql2 = (connection) => {
|
|
28
|
-
return {
|
|
29
|
-
query: async (text, params = []) => {
|
|
30
|
-
const [rows] = await connection.execute(text, params);
|
|
31
|
-
return Array.isArray(rows) ? rows : [];
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export { createHyperdrive, fromMysql2, fromNodePg, fromPostgresJs };
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { sqliteEncode, sqliteDecode } from '@lunora/sql-store';
|
|
2
|
-
import { sql } from 'drizzle-orm';
|
|
3
|
-
|
|
4
|
-
const PG_UNIQUE_VIOLATION_RE = /duplicate key value violates unique constraint/iu;
|
|
5
|
-
const mysqlColumnType = (kind) => {
|
|
6
|
-
switch (kind) {
|
|
7
|
-
case "array":
|
|
8
|
-
case "object":
|
|
9
|
-
case "record": {
|
|
10
|
-
return "JSON";
|
|
11
|
-
}
|
|
12
|
-
case "bigint": {
|
|
13
|
-
return "VARCHAR(64)";
|
|
14
|
-
}
|
|
15
|
-
case "boolean": {
|
|
16
|
-
return "TINYINT";
|
|
17
|
-
}
|
|
18
|
-
case "bytes": {
|
|
19
|
-
return "LONGBLOB";
|
|
20
|
-
}
|
|
21
|
-
case "date":
|
|
22
|
-
case "number":
|
|
23
|
-
case "timestamp": {
|
|
24
|
-
return "DOUBLE";
|
|
25
|
-
}
|
|
26
|
-
default: {
|
|
27
|
-
return "LONGTEXT";
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
const MYSQL_PREFIX_INDEX_RE = /TEXT|BLOB/u;
|
|
32
|
-
const postgresDialect = {
|
|
33
|
-
companionTypes: {
|
|
34
|
-
autoincrementPrimaryKey: "BIGSERIAL PRIMARY KEY",
|
|
35
|
-
integer: "INTEGER",
|
|
36
|
-
key: "TEXT",
|
|
37
|
-
real: "DOUBLE PRECISION",
|
|
38
|
-
text: "TEXT"
|
|
39
|
-
},
|
|
40
|
-
columnType: (kind) => {
|
|
41
|
-
switch (kind) {
|
|
42
|
-
case "boolean": {
|
|
43
|
-
return "INTEGER";
|
|
44
|
-
}
|
|
45
|
-
case "bytes": {
|
|
46
|
-
return "BYTEA";
|
|
47
|
-
}
|
|
48
|
-
case "date":
|
|
49
|
-
case "number":
|
|
50
|
-
case "timestamp": {
|
|
51
|
-
return "DOUBLE PRECISION";
|
|
52
|
-
}
|
|
53
|
-
default: {
|
|
54
|
-
return "TEXT";
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
decode: (value, kind) => sqliteDecode(value, kind),
|
|
59
|
-
encode: (value) => sqliteEncode(value),
|
|
60
|
-
frameworkColumns: () => [
|
|
61
|
-
{ name: "id", type: "TEXT PRIMARY KEY" },
|
|
62
|
-
{ name: "_creationTime", type: "DOUBLE PRECISION NOT NULL" }
|
|
63
|
-
],
|
|
64
|
-
isUniqueViolation: (error) => {
|
|
65
|
-
const { code } = error;
|
|
66
|
-
return code === "23505" || error instanceof Error && PG_UNIQUE_VIOLATION_RE.test(error.message);
|
|
67
|
-
},
|
|
68
|
-
name: "postgres",
|
|
69
|
-
supportsReturning: true,
|
|
70
|
-
tableExists: (table) => sql`SELECT table_name FROM information_schema.tables WHERE table_name = ${table}`
|
|
71
|
-
};
|
|
72
|
-
const mysqlDialect = {
|
|
73
|
-
affectedRows: (result) => result.rowsAffected,
|
|
74
|
-
companionTypes: {
|
|
75
|
-
autoincrementPrimaryKey: "BIGINT AUTO_INCREMENT PRIMARY KEY",
|
|
76
|
-
integer: "INTEGER",
|
|
77
|
-
// VARCHAR so it can be a PRIMARY KEY and be fully indexed (TEXT cannot,
|
|
78
|
-
// without a prefix length). 768 = InnoDB utf8mb4 single-column index limit.
|
|
79
|
-
key: "VARCHAR(768)",
|
|
80
|
-
real: "DOUBLE",
|
|
81
|
-
// Unbounded post-image storage (CDC `doc`); never an index key, so no bound.
|
|
82
|
-
text: "LONGTEXT"
|
|
83
|
-
},
|
|
84
|
-
columnType: (kind) => mysqlColumnType(kind),
|
|
85
|
-
decode: (value, kind) => sqliteDecode(value, kind),
|
|
86
|
-
encode: (value) => sqliteEncode(value),
|
|
87
|
-
frameworkColumns: () => [
|
|
88
|
-
{ name: "id", type: "VARCHAR(768) PRIMARY KEY" },
|
|
89
|
-
{ name: "_creationTime", type: "DOUBLE NOT NULL" }
|
|
90
|
-
],
|
|
91
|
-
// InnoDB can't index a TEXT/LONGTEXT/BLOB column without a key prefix; bound it to 768 chars.
|
|
92
|
-
indexKeyPrefix: (kind) => MYSQL_PREFIX_INDEX_RE.test(mysqlColumnType(kind)) ? 768 : void 0,
|
|
93
|
-
isUniqueViolation: (error) => {
|
|
94
|
-
const candidate = error;
|
|
95
|
-
return candidate.errno === 1062 || candidate.code === "ER_DUP_ENTRY";
|
|
96
|
-
},
|
|
97
|
-
name: "mysql",
|
|
98
|
-
supportsReturning: false,
|
|
99
|
-
tableExists: (table) => sql`SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ${table}`
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
export { mysqlDialect, postgresDialect };
|