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.
- package/CHANGELOG.md +14 -0
- package/dist/api/crud-router.d.ts +121 -0
- package/dist/api/crud-router.d.ts.map +1 -1
- package/dist/api/crud-router.js +104 -3
- package/dist/api/crud-router.js.map +1 -1
- package/dist/api/list-input-schema.d.ts +15 -0
- package/dist/api/list-input-schema.d.ts.map +1 -1
- package/dist/api/list-input-schema.js +16 -0
- package/dist/api/list-input-schema.js.map +1 -1
- package/dist/api/live-view.d.ts +14 -0
- package/dist/api/live-view.d.ts.map +1 -0
- package/dist/api/live-view.js +42 -0
- package/dist/api/live-view.js.map +1 -0
- package/dist/api/live-window.d.ts +40 -0
- package/dist/api/live-window.d.ts.map +1 -0
- package/dist/api/live-window.js +83 -0
- package/dist/api/live-window.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/live/apply.d.ts +13 -0
- package/dist/live/apply.d.ts.map +1 -0
- package/dist/live/apply.js +32 -0
- package/dist/live/apply.js.map +1 -0
- package/dist/live/index.d.ts +48 -0
- package/dist/live/index.d.ts.map +1 -0
- package/dist/live/index.js +108 -0
- package/dist/live/index.js.map +1 -0
- package/dist/live/protocol.d.ts +44 -0
- package/dist/live/protocol.d.ts.map +1 -0
- package/dist/live/protocol.js +12 -0
- package/dist/live/protocol.js.map +1 -0
- package/dist/live/sse.d.ts +22 -0
- package/dist/live/sse.d.ts.map +1 -0
- package/dist/live/sse.js +41 -0
- package/dist/live/sse.js.map +1 -0
- package/dist/realtime/filter.d.ts +34 -5
- package/dist/realtime/filter.d.ts.map +1 -1
- package/dist/realtime/filter.js +62 -25
- package/dist/realtime/filter.js.map +1 -1
- package/llms.txt +3 -3
- package/package.json +6 -2
package/dist/realtime/filter.js
CHANGED
|
@@ -1,9 +1,50 @@
|
|
|
1
1
|
import { getEventMeta, withEventMeta } from '@orpc/server';
|
|
2
2
|
import { checkAccess, rowMatchesScope, tableEntryForName } from '../access.js';
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (
|
|
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;
|
|
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
|
-
|
|
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
|
|
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.
|
|
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": {
|