@mesofact/runtime 0.8.29
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 +19 -0
- package/dist/adapters/r2.d.ts +24 -0
- package/dist/adapters/r2.d.ts.map +1 -0
- package/dist/adapters/r2.js +136 -0
- package/dist/adapters/r2.js.map +1 -0
- package/dist/adapters/sqlite.d.ts +25 -0
- package/dist/adapters/sqlite.d.ts.map +1 -0
- package/dist/adapters/sqlite.js +131 -0
- package/dist/adapters/sqlite.js.map +1 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +139 -0
- package/dist/config.js.map +1 -0
- package/dist/contract.d.ts +40 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +5 -0
- package/dist/contract.js.map +1 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +40 -0
- package/dist/errors.js.map +1 -0
- package/dist/head.d.ts +32 -0
- package/dist/head.d.ts.map +1 -0
- package/dist/head.js +93 -0
- package/dist/head.js.map +1 -0
- package/dist/health.d.ts +14 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +85 -0
- package/dist/health.js.map +1 -0
- package/dist/hooks.d.ts +28 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +64 -0
- package/dist/hooks.js.map +1 -0
- package/dist/hydration.d.ts +6 -0
- package/dist/hydration.d.ts.map +1 -0
- package/dist/hydration.js +68 -0
- package/dist/hydration.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +68 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +9 -0
- package/dist/manifest.js.map +1 -0
- package/dist/routes.d.ts +67 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +130 -0
- package/dist/routes.js.map +1 -0
- package/dist/source.d.ts +36 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +52 -0
- package/dist/source.js.map +1 -0
- package/dist/track-ctx.d.ts +13 -0
- package/dist/track-ctx.d.ts.map +1 -0
- package/dist/track-ctx.js +15 -0
- package/dist/track-ctx.js.map +1 -0
- package/dist/validate.d.ts +20 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +333 -0
- package/dist/validate.js.map +1 -0
- package/package.json +40 -0
- package/src/adapters/r2.ts +163 -0
- package/src/adapters/sqlite.ts +182 -0
- package/src/config.ts +213 -0
- package/src/contract.ts +82 -0
- package/src/errors.ts +52 -0
- package/src/head.ts +130 -0
- package/src/health.ts +99 -0
- package/src/hooks.ts +72 -0
- package/src/hydration.ts +72 -0
- package/src/index.ts +113 -0
- package/src/manifest.ts +104 -0
- package/src/routes.ts +320 -0
- package/src/source.ts +91 -0
- package/src/track-ctx.ts +29 -0
- package/src/validate.ts +388 -0
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# @mesofact/runtime
|
|
2
|
+
|
|
3
|
+
Render contract types and adapter API for [mesofact](../../). Consumed by
|
|
4
|
+
render entrypoints (e.g. `examples/yah-dev/`) and by any outside TS project
|
|
5
|
+
that needs the contract — frontends never import a mesofact SDK; they just
|
|
6
|
+
type their `render(req)` against `RenderRequest` / `RenderResult`.
|
|
7
|
+
|
|
8
|
+
See [`../../.yah/docs/architecture/mesofact.md`](../../.yah/docs/architecture/mesofact.md)
|
|
9
|
+
§"The shared seam: one render contract" and §"Adapter API surface".
|
|
10
|
+
|
|
11
|
+
## Client-safe subpath: `@mesofact/runtime/hydration`
|
|
12
|
+
|
|
13
|
+
The package root (`.`) barrel re-exports server-only code (`track-ctx.ts` →
|
|
14
|
+
`node:async_hooks`, `config.ts` → `node:fs`) that a browser bundler can't
|
|
15
|
+
tree-shake past — importing *anything* from `@mesofact/runtime` pulls those
|
|
16
|
+
Node builtins into a client bundle (R513-B11). Browser host adapters that
|
|
17
|
+
only need the hydration handoff constants/helpers (`SPA_STATE_SCRIPT_ID`,
|
|
18
|
+
`hydrationScriptTag`, ...) must import from `@mesofact/runtime/hydration`
|
|
19
|
+
instead — it resolves to `src/hydration.ts`, which has no imports of its own.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BaseSource, type BlobSource, type ListOpts, type R2Object } from "../source.js";
|
|
2
|
+
export type R2Config = {
|
|
3
|
+
name: string;
|
|
4
|
+
bucket: string;
|
|
5
|
+
endpoint: string;
|
|
6
|
+
accessKeyId: string;
|
|
7
|
+
secretAccessKey: string;
|
|
8
|
+
httpFetch?: typeof fetch;
|
|
9
|
+
};
|
|
10
|
+
export declare class R2Adapter extends BaseSource implements BlobSource {
|
|
11
|
+
readonly bucket: string;
|
|
12
|
+
private readonly client;
|
|
13
|
+
private readonly endpoint;
|
|
14
|
+
private readonly httpFetch;
|
|
15
|
+
constructor(config: R2Config);
|
|
16
|
+
fetch(key: string): Promise<Uint8Array | null>;
|
|
17
|
+
list(prefix: string, opts?: ListOpts): Promise<R2Object[]>;
|
|
18
|
+
private send;
|
|
19
|
+
private race;
|
|
20
|
+
}
|
|
21
|
+
export declare function registerR2(adapter: R2Adapter): void;
|
|
22
|
+
export declare function clearR2Registry(): void;
|
|
23
|
+
export declare function r2(name: string): BlobSource;
|
|
24
|
+
//# sourceMappingURL=r2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2.d.ts","sourceRoot":"","sources":["../../src/adapters/r2.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAKzF,MAAM,MAAM,QAAQ,GAAG;IAGrB,IAAI,EAAE,MAAM,CAAC;IAGb,MAAM,EAAE,MAAM,CAAC;IAGf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IAIxB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,CAAC;AAEF,qBAAa,SAAU,SAAQ,UAAW,YAAW,UAAU;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAE7B,MAAM,EAAE,QAAQ;IAiBtB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAa9C,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,QAAa,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;YAetD,IAAI;YAKJ,IAAI;CAenB;AA4CD,wBAAgB,UAAU,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,CAEnD;AAED,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED,wBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAQ3C"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// `r2` adapter — read-only BlobSource over Cloudflare R2 (or any S3-compatible
|
|
2
|
+
// endpoint). Signs requests with SigV4 via `aws4fetch`, emits `r2:<bucket>:<key>`
|
|
3
|
+
// tags into the ambient trackCtx, honors per-call `.noTrack()` / `.timeout(ms)`.
|
|
4
|
+
//
|
|
5
|
+
// See `.yah/docs/architecture/mesofact.md` §"Adapter API surface" + §"Adapter
|
|
6
|
+
// read-set provenance".
|
|
7
|
+
import { AwsClient } from "aws4fetch";
|
|
8
|
+
import { BaseSource } from "../source.js";
|
|
9
|
+
import { SourceQueryError, SourceTimeoutError, SourceUnavailableError } from "../errors.js";
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 2000;
|
|
11
|
+
export class R2Adapter extends BaseSource {
|
|
12
|
+
bucket;
|
|
13
|
+
client;
|
|
14
|
+
endpoint;
|
|
15
|
+
httpFetch;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
super(config.name);
|
|
18
|
+
this.bucket = config.bucket;
|
|
19
|
+
this.endpoint = config.endpoint.replace(/\/$/, "");
|
|
20
|
+
this.client = new AwsClient({
|
|
21
|
+
accessKeyId: config.accessKeyId,
|
|
22
|
+
secretAccessKey: config.secretAccessKey,
|
|
23
|
+
service: "s3",
|
|
24
|
+
region: "auto",
|
|
25
|
+
});
|
|
26
|
+
// aws4fetch always uses globalThis.fetch internally; we sign manually and
|
|
27
|
+
// dispatch through `httpFetch` so tests can swap it without touching the
|
|
28
|
+
// global. Calling `globalThis.fetch.bind(globalThis)` preserves the
|
|
29
|
+
// expected receiver for fetch implementations that check it.
|
|
30
|
+
this.httpFetch = config.httpFetch ?? globalThis.fetch.bind(globalThis);
|
|
31
|
+
}
|
|
32
|
+
async fetch(key) {
|
|
33
|
+
const { track, timeout_ms } = this.consumeOverrides(DEFAULT_TIMEOUT_MS);
|
|
34
|
+
this.emitTag(`r2:${this.bucket}:${key}`, track);
|
|
35
|
+
const url = `${this.endpoint}/${this.bucket}/${encodeKey(key)}`;
|
|
36
|
+
const res = await this.send(url, { method: "GET" }, timeout_ms);
|
|
37
|
+
if (res.status === 404)
|
|
38
|
+
return null;
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
throw new SourceQueryError(this.name, `r2 GET ${key} → HTTP ${res.status}`);
|
|
41
|
+
}
|
|
42
|
+
const buf = await res.arrayBuffer();
|
|
43
|
+
return new Uint8Array(buf);
|
|
44
|
+
}
|
|
45
|
+
async list(prefix, opts = {}) {
|
|
46
|
+
const { track, timeout_ms } = this.consumeOverrides(DEFAULT_TIMEOUT_MS);
|
|
47
|
+
this.emitTag(`r2:${this.bucket}:${prefix}*`, track);
|
|
48
|
+
const params = new URLSearchParams({ "list-type": "2", prefix });
|
|
49
|
+
if (opts.limit !== undefined)
|
|
50
|
+
params.set("max-keys", String(opts.limit));
|
|
51
|
+
if (opts.cursor)
|
|
52
|
+
params.set("continuation-token", opts.cursor);
|
|
53
|
+
if (opts.delimiter)
|
|
54
|
+
params.set("delimiter", opts.delimiter);
|
|
55
|
+
const url = `${this.endpoint}/${this.bucket}?${params.toString()}`;
|
|
56
|
+
const res = await this.send(url, { method: "GET" }, timeout_ms);
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
throw new SourceQueryError(this.name, `r2 LIST ${prefix} → HTTP ${res.status}`);
|
|
59
|
+
}
|
|
60
|
+
return parseListV2(await res.text());
|
|
61
|
+
}
|
|
62
|
+
async send(url, init, timeout_ms) {
|
|
63
|
+
const signed = await this.client.sign(url, init);
|
|
64
|
+
return this.race(() => this.httpFetch(signed), timeout_ms);
|
|
65
|
+
}
|
|
66
|
+
async race(call, timeout_ms) {
|
|
67
|
+
let timer;
|
|
68
|
+
try {
|
|
69
|
+
return await Promise.race([
|
|
70
|
+
call().catch((err) => {
|
|
71
|
+
throw new SourceUnavailableError(this.name, { cause: err });
|
|
72
|
+
}),
|
|
73
|
+
new Promise((_, reject) => {
|
|
74
|
+
timer = setTimeout(() => reject(new SourceTimeoutError(this.name, timeout_ms)), timeout_ms);
|
|
75
|
+
}),
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
if (timer)
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// `encodeURIComponent` re-encodes `/`, which we want to preserve so S3 sees
|
|
85
|
+
// path-style keys correctly. Other reserved chars stay percent-encoded.
|
|
86
|
+
function encodeKey(key) {
|
|
87
|
+
return key
|
|
88
|
+
.split("/")
|
|
89
|
+
.map((segment) => encodeURIComponent(segment))
|
|
90
|
+
.join("/");
|
|
91
|
+
}
|
|
92
|
+
// Minimal ListBucketResult v2 parser. S3's XML is a fixed shape; a regex over
|
|
93
|
+
// the `<Contents>` blocks is faster and dep-free than pulling in a full XML
|
|
94
|
+
// parser, and the only fields the contract exposes are key/size/last_modified/
|
|
95
|
+
// etag (per `R2Object`).
|
|
96
|
+
function parseListV2(xml) {
|
|
97
|
+
const out = [];
|
|
98
|
+
for (const match of xml.matchAll(/<Contents>([\s\S]*?)<\/Contents>/g)) {
|
|
99
|
+
const inner = match[1];
|
|
100
|
+
const key = pick(inner, "Key");
|
|
101
|
+
const sizeStr = pick(inner, "Size");
|
|
102
|
+
const last_modified = pick(inner, "LastModified");
|
|
103
|
+
const etagRaw = pick(inner, "ETag");
|
|
104
|
+
if (key === undefined || sizeStr === undefined || last_modified === undefined)
|
|
105
|
+
continue;
|
|
106
|
+
out.push({
|
|
107
|
+
key,
|
|
108
|
+
size: Number.parseInt(sizeStr, 10),
|
|
109
|
+
last_modified,
|
|
110
|
+
...(etagRaw ? { etag: etagRaw.replace(/^"|"$/g, "") } : {}),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
function pick(haystack, tag) {
|
|
116
|
+
const m = haystack.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
|
|
117
|
+
return m?.[1];
|
|
118
|
+
}
|
|
119
|
+
// Per-process registry. Configuration in T4 loads `mesofact.config.toml` and
|
|
120
|
+
// calls `registerR2()` for each `[sources.*]` of `kind = "r2"`. Render code
|
|
121
|
+
// looks up by name via `r2(name)`.
|
|
122
|
+
const registry = new Map();
|
|
123
|
+
export function registerR2(adapter) {
|
|
124
|
+
registry.set(adapter.name, adapter);
|
|
125
|
+
}
|
|
126
|
+
export function clearR2Registry() {
|
|
127
|
+
registry.clear();
|
|
128
|
+
}
|
|
129
|
+
export function r2(name) {
|
|
130
|
+
const adapter = registry.get(name);
|
|
131
|
+
if (!adapter) {
|
|
132
|
+
throw new Error(`r2 source not registered: ${name} (declare it in mesofact.config.toml under [sources.${name}])`);
|
|
133
|
+
}
|
|
134
|
+
return adapter;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=r2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2.js","sourceRoot":"","sources":["../../src/adapters/r2.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BaseSource, type KeyValueSource } from "../source.js";
|
|
2
|
+
export interface SqliteRunner {
|
|
3
|
+
all(sql: string, params: unknown[]): unknown[];
|
|
4
|
+
close?(): void;
|
|
5
|
+
}
|
|
6
|
+
export type SqliteConfig = {
|
|
7
|
+
name: string;
|
|
8
|
+
path: string;
|
|
9
|
+
runner?: SqliteRunner;
|
|
10
|
+
};
|
|
11
|
+
export declare class SqliteAdapter extends BaseSource implements KeyValueSource {
|
|
12
|
+
readonly path: string;
|
|
13
|
+
private runner;
|
|
14
|
+
private opening;
|
|
15
|
+
constructor(config: SqliteConfig);
|
|
16
|
+
get<T>(table: string, id: string): Promise<T | null>;
|
|
17
|
+
query<T>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
18
|
+
private run;
|
|
19
|
+
private ensureRunner;
|
|
20
|
+
private race;
|
|
21
|
+
}
|
|
22
|
+
export declare function registerSqlite(adapter: SqliteAdapter): void;
|
|
23
|
+
export declare function clearSqliteRegistry(): void;
|
|
24
|
+
export declare function sqlite(name: string): KeyValueSource;
|
|
25
|
+
//# sourceMappingURL=sqlite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAS/D,MAAM,WAAW,YAAY;IAE3B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;IAC/C,KAAK,CAAC,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,MAAM,YAAY,GAAG;IAGzB,IAAI,EAAE,MAAM,CAAC;IAGb,IAAI,EAAE,MAAM,CAAC;IAGb,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,CAAC;AAEF,qBAAa,aAAc,SAAQ,UAAW,YAAW,cAAc;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,OAAO,CAAsC;gBAEzC,MAAM,EAAE,YAAY;IAM1B,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAWpD,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;YAcnD,GAAG;YAYH,YAAY;YAaZ,IAAI;CAgBnB;AAmDD,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAE3D;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAQnD"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// `sqlite` adapter — read-only KeyValueSource over a local SQLite file (or a
|
|
2
|
+
// Litestream / LiteFS replica). Emits `sqlite:<source>:<table>:<id>` (row) and
|
|
3
|
+
// `sqlite:<source>:<table>` (table-wide) tags into the ambient trackCtx, honors
|
|
4
|
+
// per-call `.noTrack()` / `.timeout(ms)`, 100 ms default timeout.
|
|
5
|
+
//
|
|
6
|
+
// See `.yah/docs/architecture/mesofact.md` §"Adapter API surface", §"Adapter
|
|
7
|
+
// read-set provenance" (tag taxonomy), and §"Cache-key composition" (the
|
|
8
|
+
// proxy folds the file mtime into the cache key — generation is computed
|
|
9
|
+
// proxy-side from the source path, not here).
|
|
10
|
+
import { BaseSource } from "../source.js";
|
|
11
|
+
import { SourceQueryError, SourceTimeoutError, SourceUnavailableError } from "../errors.js";
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 100;
|
|
13
|
+
export class SqliteAdapter extends BaseSource {
|
|
14
|
+
path;
|
|
15
|
+
runner;
|
|
16
|
+
opening = null;
|
|
17
|
+
constructor(config) {
|
|
18
|
+
super(config.name);
|
|
19
|
+
this.path = config.path;
|
|
20
|
+
this.runner = config.runner ?? null;
|
|
21
|
+
}
|
|
22
|
+
async get(table, id) {
|
|
23
|
+
const { track, timeout_ms } = this.consumeOverrides(DEFAULT_TIMEOUT_MS);
|
|
24
|
+
this.emitTag(`sqlite:${this.name}:${table}:${id}`, track);
|
|
25
|
+
const rows = await this.run(`SELECT * FROM ${quoteIdent(table)} WHERE id = ? LIMIT 1`, [id], timeout_ms);
|
|
26
|
+
return rows[0] ?? null;
|
|
27
|
+
}
|
|
28
|
+
async query(sql, params = []) {
|
|
29
|
+
const { track, timeout_ms } = this.consumeOverrides(DEFAULT_TIMEOUT_MS);
|
|
30
|
+
// Tag every table the query reads. When no table can be extracted (CTEs,
|
|
31
|
+
// exotic SQL), fall back to a source-wide tag so invalidation stays
|
|
32
|
+
// conservative — over-purging is recoverable; a missed tag is stale-forever.
|
|
33
|
+
const tables = extractTables(sql);
|
|
34
|
+
if (tables.length === 0) {
|
|
35
|
+
this.emitTag(`sqlite:${this.name}`, track);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
for (const table of tables)
|
|
39
|
+
this.emitTag(`sqlite:${this.name}:${table}`, track);
|
|
40
|
+
}
|
|
41
|
+
return (await this.run(sql, params, timeout_ms));
|
|
42
|
+
}
|
|
43
|
+
async run(sql, params, timeout_ms) {
|
|
44
|
+
const exec = (async () => {
|
|
45
|
+
const runner = await this.ensureRunner();
|
|
46
|
+
try {
|
|
47
|
+
return runner.all(sql, params);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
throw new SourceQueryError(this.name, sqliteMessage(sql, err), { cause: err });
|
|
51
|
+
}
|
|
52
|
+
})();
|
|
53
|
+
return this.race(exec, timeout_ms);
|
|
54
|
+
}
|
|
55
|
+
async ensureRunner() {
|
|
56
|
+
if (this.runner)
|
|
57
|
+
return this.runner;
|
|
58
|
+
if (!this.opening) {
|
|
59
|
+
this.opening = openBunSqlite(this.path)
|
|
60
|
+
.then((r) => (this.runner = r))
|
|
61
|
+
.catch((err) => {
|
|
62
|
+
this.opening = null;
|
|
63
|
+
throw new SourceUnavailableError(this.name, { cause: err });
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return this.opening;
|
|
67
|
+
}
|
|
68
|
+
async race(call, timeout_ms) {
|
|
69
|
+
let timer;
|
|
70
|
+
try {
|
|
71
|
+
return await Promise.race([
|
|
72
|
+
call,
|
|
73
|
+
new Promise((_, reject) => {
|
|
74
|
+
timer = setTimeout(() => reject(new SourceTimeoutError(this.name, timeout_ms)), timeout_ms);
|
|
75
|
+
}),
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
if (timer)
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function sqliteMessage(sql, err) {
|
|
85
|
+
const head = sql.length > 80 ? `${sql.slice(0, 77)}...` : sql;
|
|
86
|
+
return `sqlite query failed (${head}): ${err instanceof Error ? err.message : String(err)}`;
|
|
87
|
+
}
|
|
88
|
+
// Quote a table identifier so a table named like a keyword (or containing a
|
|
89
|
+
// dot) can't break the generated SQL. Table names come from server-side render
|
|
90
|
+
// code, not request input, so this is defensive rather than an injection guard;
|
|
91
|
+
// `id` and `query` params are always bound, never interpolated.
|
|
92
|
+
function quoteIdent(name) {
|
|
93
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
94
|
+
}
|
|
95
|
+
// Pull table names out of a SELECT for table-wide tag emission. A regex over
|
|
96
|
+
// `FROM`/`JOIN` clauses — same pragmatic posture as the build's regex source
|
|
97
|
+
// inference and the r2 list-XML parser. Misses are absorbed by the source-wide
|
|
98
|
+
// fallback in `query`.
|
|
99
|
+
function extractTables(sql) {
|
|
100
|
+
const out = new Set();
|
|
101
|
+
for (const m of sql.matchAll(/\b(?:from|join)\s+["'`]?([A-Za-z_][\w$]*)/gi)) {
|
|
102
|
+
out.add(m[1]);
|
|
103
|
+
}
|
|
104
|
+
return [...out];
|
|
105
|
+
}
|
|
106
|
+
async function openBunSqlite(path) {
|
|
107
|
+
const mod = (await import("bun:sqlite"));
|
|
108
|
+
const db = new mod.Database(path, { readonly: true });
|
|
109
|
+
return {
|
|
110
|
+
all: (sql, params) => db.query(sql).all(...params),
|
|
111
|
+
close: () => db.close(),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// Per-process registry, mirroring the r2 adapter. `registerSourcesFromConfig`
|
|
115
|
+
// populates it from `[sources.*]` of `kind = "sqlite"`; render code looks up by
|
|
116
|
+
// name via `sqlite(name)`.
|
|
117
|
+
const registry = new Map();
|
|
118
|
+
export function registerSqlite(adapter) {
|
|
119
|
+
registry.set(adapter.name, adapter);
|
|
120
|
+
}
|
|
121
|
+
export function clearSqliteRegistry() {
|
|
122
|
+
registry.clear();
|
|
123
|
+
}
|
|
124
|
+
export function sqlite(name) {
|
|
125
|
+
const adapter = registry.get(name);
|
|
126
|
+
if (!adapter) {
|
|
127
|
+
throw new Error(`sqlite source not registered: ${name} (declare it in mesofact.config.toml under [sources.${name}])`);
|
|
128
|
+
}
|
|
129
|
+
return adapter;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,+EAA+E;AAC/E,gFAAgF;AAChF,kEAAkE;AAClE,EAAE;AACF,6EAA6E;AAC7E,yEAAyE;AACzE,yEAAyE;AACzE,8CAA8C;AAE9C,OAAO,EAAE,UAAU,EAAuB,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE5F,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAwB/B,MAAM,OAAO,aAAc,SAAQ,UAAU;IAClC,IAAI,CAAS;IACd,MAAM,CAAsB;IAC5B,OAAO,GAAiC,IAAI,CAAC;IAErD,YAAY,MAAoB;QAC9B,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,KAAa,EAAE,EAAU;QACpC,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAC;QACxE,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,IAAI,IAAI,KAAK,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CACzB,iBAAiB,UAAU,CAAC,KAAK,CAAC,uBAAuB,EACzD,CAAC,EAAE,CAAC,EACJ,UAAU,CACX,CAAC;QACF,OAAQ,IAAI,CAAC,CAAC,CAAmB,IAAI,IAAI,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,KAAK,CAAI,GAAW,EAAE,SAAoB,EAAE;QAChD,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAC;QACxE,yEAAyE;QACzE,oEAAoE;QACpE,6EAA6E;QAC7E,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;QAC7C,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,KAAK,IAAI,MAAM;gBAAE,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,IAAI,IAAI,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,CAAQ,CAAC;IAC1D,CAAC;IAEO,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,MAAiB,EAAE,UAAkB;QAClE,MAAM,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE;YACvB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;YACzC,IAAI,CAAC;gBACH,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACjC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YACjF,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACrC,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;iBACpC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;iBAC9B,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACb,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBACpB,MAAM,IAAI,sBAAsB,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC;QACP,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,IAAI,CAAI,IAAgB,EAAE,UAAkB;QACxD,IAAI,KAAgD,CAAC;QACrD,IAAI,CAAC;YACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAI;gBAC3B,IAAI;gBACJ,IAAI,OAAO,CAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;oBAC3B,KAAK,GAAG,UAAU,CAChB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,EAC3D,UAAU,CACX,CAAC;gBACJ,CAAC,CAAC;aACH,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;CACF;AAED,SAAS,aAAa,CAAC,GAAW,EAAE,GAAY;IAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9D,OAAO,wBAAwB,IAAI,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;AAC9F,CAAC;AAED,4EAA4E;AAC5E,+EAA+E;AAC/E,gFAAgF;AAChF,gEAAgE;AAChE,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;AACzC,CAAC;AAED,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,uBAAuB;AACvB,SAAS,aAAa,CAAC,GAAW;IAChC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,6CAA6C,CAAC,EAAE,CAAC;QAC5E,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;AAClB,CAAC;AAUD,KAAK,UAAU,aAAa,CAAC,IAAY;IACvC,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,YAAsB,CAAC,CAEhD,CAAC;IACF,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,OAAO;QACL,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QAClD,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE;KACxB,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,gFAAgF;AAChF,2BAA2B;AAC3B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;AAElD,MAAM,UAAU,cAAc,CAAC,OAAsB;IACnD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,iCAAiC,IAAI,uDAAuD,IAAI,IAAI,CACrG,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SourceScope } from "./validate.js";
|
|
2
|
+
export type R2SourceConfig = {
|
|
3
|
+
kind: "r2";
|
|
4
|
+
scope: SourceScope;
|
|
5
|
+
bucket: string;
|
|
6
|
+
endpoint_env: string;
|
|
7
|
+
access_key_id_env?: string;
|
|
8
|
+
secret_access_key_env?: string;
|
|
9
|
+
};
|
|
10
|
+
export type SqliteSourceConfig = {
|
|
11
|
+
kind: "sqlite";
|
|
12
|
+
scope: SourceScope;
|
|
13
|
+
path: string;
|
|
14
|
+
};
|
|
15
|
+
export type SourceConfig = R2SourceConfig | SqliteSourceConfig;
|
|
16
|
+
export type BuildConfig = {
|
|
17
|
+
public_dir?: string;
|
|
18
|
+
};
|
|
19
|
+
export type MesofactConfig = {
|
|
20
|
+
sources: Record<string, SourceConfig>;
|
|
21
|
+
build?: BuildConfig;
|
|
22
|
+
};
|
|
23
|
+
export declare function loadConfig(path: string): MesofactConfig;
|
|
24
|
+
export declare function parseConfig(toml: string): MesofactConfig;
|
|
25
|
+
export declare function registerSourcesFromConfig(config: MesofactConfig, env?: Record<string, string | undefined>): string[];
|
|
26
|
+
export declare class ConfigError extends Error {
|
|
27
|
+
constructor(message: string);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IAIf,YAAY,EAAE,MAAM,CAAC;IAGrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IAInB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,kBAAkB,CAAC;AAM/D,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAEvD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAgBxD;AAoFD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,cAAc,EACtB,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,MAAM,EAAE,CA2BV;AAiBD,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// `mesofact.config.toml` parser. Yubaba writes this file atomically and
|
|
2
|
+
// SIGHUPs the proxy + Bun pool on change; mesofact reads it on boot to
|
|
3
|
+
// instantiate adapters. Credentials come in via env vars (yubaba injects);
|
|
4
|
+
// the config carries only the env var *names*, never the secrets themselves.
|
|
5
|
+
//
|
|
6
|
+
// See `.yah/docs/architecture/mesofact.md` §"Yubaba owns config and credentials".
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { parse as parseToml } from "smol-toml";
|
|
9
|
+
import { R2Adapter, registerR2 } from "./adapters/r2.js";
|
|
10
|
+
import { SqliteAdapter, registerSqlite } from "./adapters/sqlite.js";
|
|
11
|
+
export function loadConfig(path) {
|
|
12
|
+
return parseConfig(readFileSync(path, "utf8"));
|
|
13
|
+
}
|
|
14
|
+
export function parseConfig(toml) {
|
|
15
|
+
const raw = parseToml(toml);
|
|
16
|
+
if (!isPlainObject(raw)) {
|
|
17
|
+
throw new ConfigError("config must be a TOML object at top level");
|
|
18
|
+
}
|
|
19
|
+
const build = parseBuild(raw.build);
|
|
20
|
+
const sourcesRaw = raw.sources;
|
|
21
|
+
if (sourcesRaw === undefined)
|
|
22
|
+
return { sources: {}, ...(build ? { build } : {}) };
|
|
23
|
+
if (!isPlainObject(sourcesRaw)) {
|
|
24
|
+
throw new ConfigError("[sources] must be a table");
|
|
25
|
+
}
|
|
26
|
+
const sources = {};
|
|
27
|
+
for (const [name, body] of Object.entries(sourcesRaw)) {
|
|
28
|
+
sources[name] = parseSource(name, body);
|
|
29
|
+
}
|
|
30
|
+
return { sources, ...(build ? { build } : {}) };
|
|
31
|
+
}
|
|
32
|
+
function parseBuild(raw) {
|
|
33
|
+
if (raw === undefined)
|
|
34
|
+
return undefined;
|
|
35
|
+
if (!isPlainObject(raw)) {
|
|
36
|
+
throw new ConfigError("[build] must be a table");
|
|
37
|
+
}
|
|
38
|
+
const out = {};
|
|
39
|
+
if (raw.public_dir !== undefined) {
|
|
40
|
+
if (typeof raw.public_dir !== "string" || raw.public_dir.length === 0) {
|
|
41
|
+
throw new ConfigError("[build] public_dir must be a non-empty string");
|
|
42
|
+
}
|
|
43
|
+
out.public_dir = raw.public_dir;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
function parseSource(name, body) {
|
|
48
|
+
if (!isPlainObject(body)) {
|
|
49
|
+
throw new ConfigError(`[sources.${name}] must be a table`);
|
|
50
|
+
}
|
|
51
|
+
const kind = body.kind;
|
|
52
|
+
const scope = parseScope(name, body.scope);
|
|
53
|
+
if (kind === "r2")
|
|
54
|
+
return parseR2(name, body, scope);
|
|
55
|
+
if (kind === "sqlite")
|
|
56
|
+
return parseSqlite(name, body, scope);
|
|
57
|
+
// P4 shipped r2; P9 adds sqlite. pg/rpc widen this union later.
|
|
58
|
+
throw new ConfigError(`[sources.${name}] unsupported kind: ${JSON.stringify(kind)} (supported: "r2", "sqlite")`);
|
|
59
|
+
}
|
|
60
|
+
function parseScope(name, raw) {
|
|
61
|
+
const scope = raw ?? "global";
|
|
62
|
+
if (scope !== "global" && scope !== "project" && scope !== "user") {
|
|
63
|
+
throw new ConfigError(`[sources.${name}] invalid scope: ${JSON.stringify(scope)} (expected "global" | "project" | "user")`);
|
|
64
|
+
}
|
|
65
|
+
return scope;
|
|
66
|
+
}
|
|
67
|
+
function parseR2(name, body, scope) {
|
|
68
|
+
const bucket = body.bucket;
|
|
69
|
+
if (typeof bucket !== "string" || bucket === "") {
|
|
70
|
+
throw new ConfigError(`[sources.${name}] missing or empty \`bucket\` (string)`);
|
|
71
|
+
}
|
|
72
|
+
const endpoint_env = body.endpoint_env;
|
|
73
|
+
if (typeof endpoint_env !== "string" || endpoint_env === "") {
|
|
74
|
+
throw new ConfigError(`[sources.${name}] missing or empty \`endpoint_env\` (string)`);
|
|
75
|
+
}
|
|
76
|
+
const access_key_id_env = optionalString(body.access_key_id_env, name, "access_key_id_env");
|
|
77
|
+
const secret_access_key_env = optionalString(body.secret_access_key_env, name, "secret_access_key_env");
|
|
78
|
+
return {
|
|
79
|
+
kind: "r2",
|
|
80
|
+
scope,
|
|
81
|
+
bucket,
|
|
82
|
+
endpoint_env,
|
|
83
|
+
...(access_key_id_env !== undefined ? { access_key_id_env } : {}),
|
|
84
|
+
...(secret_access_key_env !== undefined ? { secret_access_key_env } : {}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function parseSqlite(name, body, scope) {
|
|
88
|
+
const path = body.path;
|
|
89
|
+
if (typeof path !== "string" || path === "") {
|
|
90
|
+
throw new ConfigError(`[sources.${name}] missing or empty \`path\` (string)`);
|
|
91
|
+
}
|
|
92
|
+
return { kind: "sqlite", scope, path };
|
|
93
|
+
}
|
|
94
|
+
function optionalString(value, source, field) {
|
|
95
|
+
if (value === undefined)
|
|
96
|
+
return undefined;
|
|
97
|
+
if (typeof value !== "string" || value === "") {
|
|
98
|
+
throw new ConfigError(`[sources.${source}] \`${field}\` must be a non-empty string`);
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
// Resolve credentials from env and register an R2Adapter for each r2 source.
|
|
103
|
+
// Returns the resolved adapter names so callers can verify a known set landed.
|
|
104
|
+
export function registerSourcesFromConfig(config, env = process.env) {
|
|
105
|
+
const registered = [];
|
|
106
|
+
for (const [name, src] of Object.entries(config.sources)) {
|
|
107
|
+
if (src.kind === "r2") {
|
|
108
|
+
const endpoint = requireEnv(env, src.endpoint_env, name, "endpoint_env");
|
|
109
|
+
const accessKeyId = requireEnv(env, src.access_key_id_env ?? "AWS_ACCESS_KEY_ID", name, "access_key_id_env");
|
|
110
|
+
const secretAccessKey = requireEnv(env, src.secret_access_key_env ?? "AWS_SECRET_ACCESS_KEY", name, "secret_access_key_env");
|
|
111
|
+
registerR2(new R2Adapter({ name, bucket: src.bucket, endpoint, accessKeyId, secretAccessKey }));
|
|
112
|
+
registered.push(name);
|
|
113
|
+
}
|
|
114
|
+
else if (src.kind === "sqlite") {
|
|
115
|
+
// No credentials — sqlite is a local file. The DB opens lazily on first
|
|
116
|
+
// read, so registration never touches disk.
|
|
117
|
+
registerSqlite(new SqliteAdapter({ name, path: src.path }));
|
|
118
|
+
registered.push(name);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return registered;
|
|
122
|
+
}
|
|
123
|
+
function requireEnv(env, varName, source, field) {
|
|
124
|
+
const v = env[varName];
|
|
125
|
+
if (v === undefined || v === "") {
|
|
126
|
+
throw new ConfigError(`[sources.${source}] env var \`${varName}\` (from ${field}) is unset or empty`);
|
|
127
|
+
}
|
|
128
|
+
return v;
|
|
129
|
+
}
|
|
130
|
+
export class ConfigError extends Error {
|
|
131
|
+
constructor(message) {
|
|
132
|
+
super(message);
|
|
133
|
+
this.name = "ConfigError";
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function isPlainObject(v) {
|
|
137
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Head } from "./head.js";
|
|
2
|
+
export type Region = string;
|
|
3
|
+
export type User = {
|
|
4
|
+
id: string;
|
|
5
|
+
attrs: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
export type Project = {
|
|
8
|
+
id: string;
|
|
9
|
+
home_region: Region;
|
|
10
|
+
generation: string;
|
|
11
|
+
};
|
|
12
|
+
export type RenderRequest = {
|
|
13
|
+
url: string;
|
|
14
|
+
params: Record<string, string>;
|
|
15
|
+
query: Record<string, string>;
|
|
16
|
+
headers: Record<string, string>;
|
|
17
|
+
cookies: Record<string, string>;
|
|
18
|
+
user?: User;
|
|
19
|
+
project?: Project;
|
|
20
|
+
region?: Region;
|
|
21
|
+
ctx?: Record<string, unknown>;
|
|
22
|
+
data?: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
export type CachePolicy = {
|
|
25
|
+
ttl: number;
|
|
26
|
+
tags?: readonly string[];
|
|
27
|
+
};
|
|
28
|
+
export type Hydration = {
|
|
29
|
+
script?: string;
|
|
30
|
+
initial_state?: unknown;
|
|
31
|
+
};
|
|
32
|
+
export type RenderResult = {
|
|
33
|
+
html: string;
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
cache: CachePolicy;
|
|
36
|
+
hydration?: Hydration;
|
|
37
|
+
head?: Head;
|
|
38
|
+
};
|
|
39
|
+
export type RenderFn = (req: RenderRequest) => Promise<RenderResult>;
|
|
40
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B,MAAM,MAAM,IAAI,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAKhC,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAKhB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAK9B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B,CAAC;AAkBF,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IAItB,IAAI,CAAC,EAAE,IAAI,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC"}
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,yEAAyE;AACzE,6DAA6D"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare class SourceError extends Error {
|
|
2
|
+
readonly source: string;
|
|
3
|
+
readonly retryable: boolean;
|
|
4
|
+
constructor(message: string, source: string, retryable: boolean, options?: {
|
|
5
|
+
cause?: unknown;
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
export declare class SourceUnavailableError extends SourceError {
|
|
9
|
+
constructor(source: string, options?: {
|
|
10
|
+
cause?: unknown;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export declare class SourceTimeoutError extends SourceError {
|
|
14
|
+
readonly timeout_ms: number;
|
|
15
|
+
constructor(source: string, timeout_ms: number, options?: {
|
|
16
|
+
cause?: unknown;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export declare class SourceQueryError extends SourceError {
|
|
20
|
+
constructor(source: string, message: string, options?: {
|
|
21
|
+
cause?: unknown;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export declare class RowNotFoundError extends SourceError {
|
|
25
|
+
readonly table: string;
|
|
26
|
+
readonly id: string;
|
|
27
|
+
constructor(source: string, table: string, id: string);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAIA,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAG1B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO,EAClB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAOhC;AAED,qBAAa,sBAAuB,SAAQ,WAAW;gBACzC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAG1D;AAED,qBAAa,kBAAmB,SAAQ,WAAW;IACjD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEhB,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI9E;AAED,qBAAa,gBAAiB,SAAQ,WAAW;gBACnC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAG3E;AAED,qBAAa,gBAAiB,SAAQ,WAAW;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAER,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;CAKtD"}
|