@junejs/db 0.0.33-dev.0 → 0.0.33-dev.2
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/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/scope.d.ts +20 -0
- package/dist/scope.js +62 -0
- package/package.json +11 -4
- package/src/index.ts +1 -0
- package/src/scope.ts +20 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { RequestScope, blob, currentLocale, currentServices, db, ensureScope, kv, registerSqlTagger, requestLocal, runInScope, setRequestLocale } from "./scope.js";
|
|
2
|
+
export { type RequestScope, blob, currentLocale, currentServices, db, ensureScope, kv, registerSqlTagger, requestLocal, runInScope, setRequestLocale };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { blob, currentLocale, currentServices, db, ensureScope, kv, registerSqlTagger, requestLocal, runInScope, setRequestLocale } from "./scope.js";
|
|
2
|
+
export { blob, currentLocale, currentServices, db, ensureScope, kv, registerSqlTagger, requestLocal, runInScope, setRequestLocale };
|
package/dist/scope.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { JuneBlob, JuneDb, JuneKv, Resources } from "@junejs/core/resources";
|
|
2
|
+
//#region src/scope.d.ts
|
|
3
|
+
type RequestScope = {
|
|
4
|
+
resources: Resources;
|
|
5
|
+
locals?: Map<symbol, unknown>;
|
|
6
|
+
locale?: string;
|
|
7
|
+
services?: unknown;
|
|
8
|
+
};
|
|
9
|
+
declare function ensureScope(): Promise<void>;
|
|
10
|
+
declare function runInScope<T>(scope: RequestScope, fn: () => T): T;
|
|
11
|
+
declare function requestLocal<T>(key: symbol, make: () => T): T;
|
|
12
|
+
declare function setRequestLocale(locale: string): void;
|
|
13
|
+
declare function currentLocale(): string | undefined;
|
|
14
|
+
declare function currentServices<T = unknown>(): T | undefined;
|
|
15
|
+
declare function registerSqlTagger(tag: (sql: string) => void): void;
|
|
16
|
+
declare const db: JuneDb;
|
|
17
|
+
declare const kv: JuneKv;
|
|
18
|
+
declare const blob: JuneBlob;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { RequestScope, blob, currentLocale, currentServices, db, ensureScope, kv, registerSqlTagger, requestLocal, runInScope, setRequestLocale };
|
package/dist/scope.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//#region src/scope.ts
|
|
2
|
+
let als = null;
|
|
3
|
+
let ensuring = null;
|
|
4
|
+
async function ensureScope() {
|
|
5
|
+
if (als) return;
|
|
6
|
+
ensuring ??= (async () => {
|
|
7
|
+
try {
|
|
8
|
+
als = new (await (import("node:async_hooks"))).AsyncLocalStorage();
|
|
9
|
+
} catch {}
|
|
10
|
+
})();
|
|
11
|
+
await ensuring;
|
|
12
|
+
}
|
|
13
|
+
function runInScope(scope, fn) {
|
|
14
|
+
return als ? als.run(scope, fn) : fn();
|
|
15
|
+
}
|
|
16
|
+
function requestLocal(key, make) {
|
|
17
|
+
const store = als?.getStore();
|
|
18
|
+
if (!store) throw new Error("June: request-scoped state was used outside a request scope. It is only available while a request is being handled (a route loader/view or an action).");
|
|
19
|
+
const locals = store.locals ??= /* @__PURE__ */ new Map();
|
|
20
|
+
if (!locals.has(key)) locals.set(key, make());
|
|
21
|
+
return locals.get(key);
|
|
22
|
+
}
|
|
23
|
+
function setRequestLocale(locale) {
|
|
24
|
+
const store = als?.getStore();
|
|
25
|
+
if (store) store.locale = locale;
|
|
26
|
+
}
|
|
27
|
+
function currentLocale() {
|
|
28
|
+
return als?.getStore()?.locale;
|
|
29
|
+
}
|
|
30
|
+
function currentServices() {
|
|
31
|
+
return als?.getStore()?.services ?? void 0;
|
|
32
|
+
}
|
|
33
|
+
function pick(name) {
|
|
34
|
+
const store = als?.getStore();
|
|
35
|
+
if (!store) throw new Error(`June: \`${name}\` was used outside a request scope. Resources are only available while a request is being handled (a route loader/view or an action).`);
|
|
36
|
+
const resource = store.resources[name];
|
|
37
|
+
if (!resource) throw new Error(`June: no \`${name}\` resource is declared. Add \`resources: { ${name}: … }\` to june.config.ts (e.g. \`db: sqlite()\`).`);
|
|
38
|
+
return resource;
|
|
39
|
+
}
|
|
40
|
+
function ambient(name) {
|
|
41
|
+
return new Proxy({}, { get(_t, prop) {
|
|
42
|
+
const resource = pick(name);
|
|
43
|
+
const value = resource[prop];
|
|
44
|
+
return typeof value === "function" ? value.bind(resource) : value;
|
|
45
|
+
} });
|
|
46
|
+
}
|
|
47
|
+
let sqlTagger = null;
|
|
48
|
+
function registerSqlTagger(tag) {
|
|
49
|
+
sqlTagger = tag;
|
|
50
|
+
}
|
|
51
|
+
const db = new Proxy({}, { get(_t, prop) {
|
|
52
|
+
const handle = pick("db");
|
|
53
|
+
const value = handle[prop];
|
|
54
|
+
if (typeof value !== "function") return value;
|
|
55
|
+
const fn = value.bind(handle);
|
|
56
|
+
if (sqlTagger && (prop === "query" || prop === "get" || prop === "run")) return (sql, params) => (sqlTagger(sql), fn(sql, params));
|
|
57
|
+
return fn;
|
|
58
|
+
} });
|
|
59
|
+
const kv = ambient("kv");
|
|
60
|
+
const blob = ambient("blob");
|
|
61
|
+
//#endregion
|
|
62
|
+
export { blob, currentLocale, currentServices, db, ensureScope, kv, registerSqlTagger, requestLocal, runInScope, setRequestLocale };
|
package/package.json
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junejs/db",
|
|
3
|
-
"version": "0.0.33-dev.
|
|
3
|
+
"version": "0.0.33-dev.2",
|
|
4
4
|
"description": "June — the agent-native React framework. Ambient data resources (db/kv/blob).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://june.build",
|
|
8
8
|
"exports": {
|
|
9
|
-
".":
|
|
9
|
+
".": {
|
|
10
|
+
"source": "./src/index.ts",
|
|
11
|
+
"bun": "./src/index.ts",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
10
15
|
},
|
|
11
16
|
"files": [
|
|
17
|
+
"dist",
|
|
12
18
|
"src"
|
|
13
19
|
],
|
|
14
20
|
"scripts": {
|
|
15
21
|
"test": "bun test",
|
|
16
|
-
"typecheck": "tsc --noEmit"
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"build": "tsdown"
|
|
17
24
|
},
|
|
18
25
|
"dependencies": {
|
|
19
|
-
"@junejs/core": ">=0.1.0-dev.
|
|
26
|
+
"@junejs/core": ">=0.1.0-dev.4"
|
|
20
27
|
},
|
|
21
28
|
"repository": {
|
|
22
29
|
"type": "git",
|
package/src/index.ts
CHANGED
package/src/scope.ts
CHANGED
|
@@ -33,6 +33,15 @@ export type RequestScope = {
|
|
|
33
33
|
// opt-in layer (e.g. @junejs/i18n's ambient `t`) reads it WITHOUT the core
|
|
34
34
|
// pipeline depending on that layer. Undefined when no i18n is configured.
|
|
35
35
|
locale?: string;
|
|
36
|
+
// App-defined services bag, seeded by the host at each ISOLATE ENTRY from that
|
|
37
|
+
// isolate's env — the Worker pipeline from the worker env, a Durable Object from
|
|
38
|
+
// its own env (env only exists inside an invocation on workerd, never at module
|
|
39
|
+
// top-level). This is the seam for resources June does NOT model (Vectorize,
|
|
40
|
+
// Workers AI, an app ledger, a signing secret): the app builds it where env lives
|
|
41
|
+
// and reads it ambiently via currentServices(), so a tool needs neither a
|
|
42
|
+
// module-global setter nor `env` on ctx. Opaque here on purpose — @junejs/db
|
|
43
|
+
// never names the app's service shape; the app supplies the type at the read.
|
|
44
|
+
services?: unknown;
|
|
36
45
|
};
|
|
37
46
|
|
|
38
47
|
// The minimal slice of AsyncLocalStorage we use — kept structural so this module
|
|
@@ -100,6 +109,17 @@ export function currentLocale(): string | undefined {
|
|
|
100
109
|
return als?.getStore()?.locale;
|
|
101
110
|
}
|
|
102
111
|
|
|
112
|
+
// The current isolate's app-defined services bag (see RequestScope.services), or
|
|
113
|
+
// undefined outside a scope / when none was seeded. Soft (returns undefined) rather
|
|
114
|
+
// than throwing, because services are optional and app-defined: the app wraps this
|
|
115
|
+
// in its OWN typed accessor that throws when a specific service it needs is missing.
|
|
116
|
+
// @junejs/db stays generic — it carries the bag, never inspects it. A nullish bag
|
|
117
|
+
// (unset OR explicitly `null`) normalizes to `undefined` so the `T | undefined`
|
|
118
|
+
// return type is sound — a caller guarding only `undefined` can't be handed a null.
|
|
119
|
+
export function currentServices<T = unknown>(): T | undefined {
|
|
120
|
+
return (als?.getStore()?.services ?? undefined) as T | undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
103
123
|
function pick<K extends keyof Resources>(name: K): NonNullable<Resources[K]> {
|
|
104
124
|
const store = als?.getStore();
|
|
105
125
|
if (!store) {
|