bunderstack 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/api/crud-router.d.ts +121 -0
  3. package/dist/api/crud-router.d.ts.map +1 -1
  4. package/dist/api/crud-router.js +104 -3
  5. package/dist/api/crud-router.js.map +1 -1
  6. package/dist/api/list-input-schema.d.ts +15 -0
  7. package/dist/api/list-input-schema.d.ts.map +1 -1
  8. package/dist/api/list-input-schema.js +16 -0
  9. package/dist/api/list-input-schema.js.map +1 -1
  10. package/dist/api/live-view.d.ts +14 -0
  11. package/dist/api/live-view.d.ts.map +1 -0
  12. package/dist/api/live-view.js +42 -0
  13. package/dist/api/live-view.js.map +1 -0
  14. package/dist/api/live-window.d.ts +40 -0
  15. package/dist/api/live-window.d.ts.map +1 -0
  16. package/dist/api/live-window.js +83 -0
  17. package/dist/api/live-window.js.map +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/live/apply.d.ts +13 -0
  22. package/dist/live/apply.d.ts.map +1 -0
  23. package/dist/live/apply.js +32 -0
  24. package/dist/live/apply.js.map +1 -0
  25. package/dist/live/index.d.ts +48 -0
  26. package/dist/live/index.d.ts.map +1 -0
  27. package/dist/live/index.js +108 -0
  28. package/dist/live/index.js.map +1 -0
  29. package/dist/live/protocol.d.ts +44 -0
  30. package/dist/live/protocol.d.ts.map +1 -0
  31. package/dist/live/protocol.js +12 -0
  32. package/dist/live/protocol.js.map +1 -0
  33. package/dist/live/sse.d.ts +22 -0
  34. package/dist/live/sse.d.ts.map +1 -0
  35. package/dist/live/sse.js +41 -0
  36. package/dist/live/sse.js.map +1 -0
  37. package/dist/realtime/filter.d.ts +34 -5
  38. package/dist/realtime/filter.d.ts.map +1 -1
  39. package/dist/realtime/filter.js +62 -25
  40. package/dist/realtime/filter.js.map +1 -1
  41. package/llms.txt +3 -3
  42. package/package.json +6 -2
@@ -1,9 +1,50 @@
1
1
  import { getEventMeta, withEventMeta } from '@orpc/server';
2
2
  import { checkAccess, rowMatchesScope, tableEntryForName } from '../access.js';
3
- export async function* filterRealtimeChanges(source, options) {
4
- const subscriptions = new Set(options.subscriptions);
3
+ /**
4
+ * Whether one change may reach this subscriber: the given right plus the read
5
+ * scope of the row, evaluated against the caller's session. The session
6
+ * resolves at most once per stream.
7
+ */
8
+ export function createChangeGuard(entry, options) {
5
9
  let sessionPromise;
6
10
  const getSession = () => (sessionPromise ??= options.getSession());
11
+ return async (change) => {
12
+ if (!entry.enabled || options.rule === 'deny')
13
+ return false;
14
+ const needsSession = options.rule !== 'public' || entry.readScope !== undefined;
15
+ const session = needsSession
16
+ ? await getSession()
17
+ : { user: null, activeOrganizationId: null };
18
+ const context = {
19
+ request: options.request,
20
+ user: session.user,
21
+ row: change.record,
22
+ session: { activeOrganizationId: session.activeOrganizationId },
23
+ };
24
+ if (!(await checkAccess(options.rule, context, entry.ownerColumn)).allowed) {
25
+ return false;
26
+ }
27
+ if (entry.readScope &&
28
+ !rowMatchesScope(change.record, entry.readScope(context))) {
29
+ return false;
30
+ }
31
+ return true;
32
+ };
33
+ }
34
+ /** Preserve publisher event metadata (ids) across the projection. */
35
+ function project(change) {
36
+ const projected = {
37
+ table: change.table,
38
+ action: change.action,
39
+ record: change.record,
40
+ };
41
+ const meta = getEventMeta(change);
42
+ return meta ? withEventMeta(projected, meta) : projected;
43
+ }
44
+ export async function* filterRealtimeChanges(source, options) {
45
+ const subscriptions = new Set(options.subscriptions);
46
+ // One guard — and therefore one cached session — per table entry.
47
+ const guards = new Map();
7
48
  for await (const change of source) {
8
49
  // Events name tables by schema key; the SQL-name lookup stays as a fallback
9
50
  // for publishers outside the CRUD path that only know the physical name.
@@ -17,32 +58,28 @@ export async function* filterRealtimeChanges(source, options) {
17
58
  !subscriptions.has(`${change.table}/${String(recordId)}`))) {
18
59
  continue;
19
60
  }
20
- if (entry.get === 'deny')
61
+ let guard = guards.get(entry);
62
+ if (!guard) {
63
+ guard = createChangeGuard(entry, { ...options, rule: entry.get });
64
+ guards.set(entry, guard);
65
+ }
66
+ if (!(await guard(change)))
21
67
  continue;
22
- const needsSession = entry.get !== 'public' || entry.readScope !== undefined;
23
- const session = needsSession
24
- ? await getSession()
25
- : { user: null, activeOrganizationId: null };
26
- const context = {
27
- request: options.request,
28
- user: session.user,
29
- row: change.record,
30
- session: { activeOrganizationId: session.activeOrganizationId },
31
- };
32
- if (!(await checkAccess(entry.get, context, entry.ownerColumn)).allowed) {
68
+ yield project(change);
69
+ }
70
+ }
71
+ /**
72
+ * The same stream narrowed to one table with its access entry known up front —
73
+ * what a live view (`GET /{table}:live`) consumes.
74
+ */
75
+ export async function* filterTableChanges(source, options) {
76
+ const guard = createChangeGuard(options.entry, options);
77
+ for await (const change of source) {
78
+ if (change.table !== options.tableName)
33
79
  continue;
34
- }
35
- if (entry.readScope &&
36
- !rowMatchesScope(change.record, entry.readScope(context))) {
80
+ if (!(await guard(change)))
37
81
  continue;
38
- }
39
- const projected = {
40
- table: change.table,
41
- action: change.action,
42
- record: change.record,
43
- };
44
- const meta = getEventMeta(change);
45
- yield meta ? withEventMeta(projected, meta) : projected;
82
+ yield project(change);
46
83
  }
47
84
  }
48
85
  //# sourceMappingURL=filter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/realtime/filter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAK1D,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAY3E,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,qBAAqB,CAC1C,MAAqC,EACrC,OAAqC;IAErC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;IACpD,IAAI,cAES,CAAA;IACb,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,CAAC,cAAc,KAAK,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;IAElE,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;QAClC,4EAA4E;QAC5E,yEAAyE;QACzE,MAAM,KAAK,GACT,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;YAChC,iBAAiB,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;QACjD,IAAI,CAAC,KAAK,EAAE,OAAO;YAAE,SAAQ;QAE7B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAA;QACjC,IACE,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;YAChC,CAAC,QAAQ,IAAI,IAAI;gBACf,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,EAC5D,CAAC;YACD,SAAQ;QACV,CAAC;QACD,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM;YAAE,SAAQ;QAElC,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,CAAA;QAC5E,MAAM,OAAO,GAAG,YAAY;YAC1B,CAAC,CAAC,MAAM,UAAU,EAAE;YACpB,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAA;QAC9C,MAAM,OAAO,GAAG;YACd,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,GAAG,EAAE,MAAM,CAAC,MAAM;YAClB,OAAO,EAAE,EAAE,oBAAoB,EAAE,OAAO,CAAC,oBAAoB,EAAE;SAChE,CAAA;QACD,IAAI,CAAC,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;YACxE,SAAQ;QACV,CAAC;QACD,IACE,KAAK,CAAC,SAAS;YACf,CAAC,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EACzD,CAAC;YACD,SAAQ;QACV,CAAC;QAED,MAAM,SAAS,GAAmB;YAChC,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAA;QACD,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;QACjC,MAAM,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACzD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/realtime/filter.ts"],"names":[],"mappings":"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"}
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  BUNDERSTACK
2
2
 
3
3
  A batteries-included backend framework for Bun. Version 0.17 (beta).
4
- Docs: https://bunderstack.dev/docs
4
+ Docs: https://bunderstack.kcrz.dev/docs
5
5
  This file is written for coding agents. It is dense on purpose.
6
6
 
7
7
  WHAT IT IS
@@ -13,7 +13,7 @@ subscriptions, and a health check live in the same oRPC graph, reachable both
13
13
  as typed RPC and as ordinary HTTP.
14
14
 
15
15
  Stack: Bun, Drizzle (+ drizzle-kit), Better Auth, oRPC v2, libSQL or Postgres,
16
- sharp. Validation accepts any Standard Schema library; generated schemas use
16
+ Bun.Image. Validation accepts any Standard Schema library; generated schemas use
17
17
  Valibot. There is no Hono, no tRPC, and no Zod requirement.
18
18
 
19
19
  Packages: bunderstack (server), bunderstack-query (client + TanStack Query),
@@ -303,7 +303,7 @@ STORAGE
303
303
  }
304
304
 
305
305
  Canonical URL: /api/files/{bucket}/{+path}. transforms: true enables on-the-fly
306
- image derivatives through sharp. app.storage exposes delete, bucket, sweep,
306
+ image derivatives through Bun.Image. app.storage exposes delete, bucket, sweep,
307
307
  getUrl, and upload for server-generated files.
308
308
 
309
309
  REALTIME
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunderstack",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Batteries-included backend framework for Bun: type-safe oRPC APIs, auth, storage, realtime, jobs, email, and validated env from one config.",
5
5
  "keywords": [
6
6
  "backend",
@@ -60,6 +60,10 @@
60
60
  "types": "./dist/database/postgres-js.d.ts",
61
61
  "default": "./dist/database/postgres-js.js"
62
62
  },
63
+ "./live": {
64
+ "types": "./dist/live/index.d.ts",
65
+ "default": "./dist/live/index.js"
66
+ },
63
67
  "./provision": {
64
68
  "types": "./dist/provision.d.ts",
65
69
  "default": "./dist/provision.js"
@@ -107,7 +111,7 @@
107
111
  "db:push": "drizzle-kit push",
108
112
  "dev": "bun --hot ../../examples/standalone/server.ts",
109
113
  "prepack": "bun run build",
110
- "test": "bun test",
114
+ "test": "bun test --parallel --timeout=30000",
111
115
  "typecheck": "tsc --noEmit"
112
116
  },
113
117
  "dependencies": {