@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.
@@ -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 };
@@ -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.0",
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
- ".": "./src/index.ts"
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.0"
26
+ "@junejs/core": ">=0.1.0-dev.4"
20
27
  },
21
28
  "repository": {
22
29
  "type": "git",
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ export {
12
12
  requestLocal,
13
13
  setRequestLocale,
14
14
  currentLocale,
15
+ currentServices,
15
16
  registerSqlTagger,
16
17
  type RequestScope,
17
18
  } from "./scope";
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) {