@happyvertical/smrt-core 0.38.2 → 0.38.4
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/AGENTS.md +11 -2
- package/dist/__tests__/fixtures/advisor-test-classes.d.ts +9 -0
- package/dist/__tests__/fixtures/advisor-test-classes.d.ts.map +1 -1
- package/dist/change-feed.d.ts +19 -10
- package/dist/change-feed.d.ts.map +1 -1
- package/dist/change-feed.js +43 -13
- package/dist/change-feed.js.map +1 -1
- package/dist/change-signals.d.ts +83 -0
- package/dist/change-signals.d.ts.map +1 -0
- package/dist/change-signals.js +272 -0
- package/dist/change-signals.js.map +1 -0
- package/dist/collection-cache.d.ts +21 -0
- package/dist/collection-cache.d.ts.map +1 -1
- package/dist/collection-cache.js +0 -0
- package/dist/collection-cache.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/decorators/index.d.ts +8 -0
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js.map +1 -1
- package/dist/errors.js +2 -1
- package/dist/errors.js.map +1 -1
- package/dist/generators/changes-route.d.ts +15 -27
- package/dist/generators/changes-route.d.ts.map +1 -1
- package/dist/generators/changes-route.js +17 -7
- package/dist/generators/changes-route.js.map +1 -1
- package/dist/generators/cli.d.ts +3 -0
- package/dist/generators/cli.d.ts.map +1 -1
- package/dist/generators/cli.js +7 -4
- package/dist/generators/cli.js.map +1 -1
- package/dist/generators/conditional-get.d.ts +17 -6
- package/dist/generators/conditional-get.d.ts.map +1 -1
- package/dist/generators/conditional-get.js +19 -13
- package/dist/generators/conditional-get.js.map +1 -1
- package/dist/generators/events-route.d.ts +72 -0
- package/dist/generators/events-route.d.ts.map +1 -0
- package/dist/generators/events-route.js +234 -0
- package/dist/generators/events-route.js.map +1 -0
- package/dist/generators/index.d.ts +1 -0
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +2 -1
- package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
- package/dist/generators/mcp-runtime-template.js +11 -5
- package/dist/generators/mcp-runtime-template.js.map +1 -1
- package/dist/generators/mcp.d.ts +3 -0
- package/dist/generators/mcp.d.ts.map +1 -1
- package/dist/generators/mcp.js +18 -9
- package/dist/generators/mcp.js.map +1 -1
- package/dist/generators/rest.d.ts +7 -1
- package/dist/generators/rest.d.ts.map +1 -1
- package/dist/generators/rest.js +77 -13
- package/dist/generators/rest.js.map +1 -1
- package/dist/generators.js +2 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/manifest/generator.js.map +1 -1
- package/dist/manifest/manifest-loader.js.map +1 -1
- package/dist/manifest/static-manifest.d.ts.map +1 -1
- package/dist/manifest/static-manifest.js +7 -3
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
- package/dist/manifest/test-manifest-stub.js +1171 -90
- package/dist/manifest/test-manifest-stub.js.map +1 -1
- package/dist/manifest.json +9 -3
- package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts +1 -0
- package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts.map +1 -1
- package/dist/mcp-advisor/types.d.ts +4 -0
- package/dist/mcp-advisor/types.d.ts.map +1 -1
- package/dist/migrations/tracker.js +5 -4
- package/dist/migrations/tracker.js.map +1 -1
- package/dist/migrations/types.d.ts +1 -1
- package/dist/migrations/types.d.ts.map +1 -1
- package/dist/object.d.ts +17 -8
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +42 -26
- package/dist/object.js.map +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +12 -0
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/shared-state.d.ts +2 -1
- package/dist/registry/shared-state.d.ts.map +1 -1
- package/dist/registry/shared-state.js.map +1 -1
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +1 -1
- package/dist/registry.js.map +1 -1
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js.map +1 -1
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/scanner/types.d.ts +7 -0
- package/dist/scanner/types.d.ts.map +1 -1
- package/dist/schema/ddl/base-strategy.js +1 -1
- package/dist/schema/ddl/base-strategy.js.map +1 -1
- package/dist/scripts/migrate-datetime-to-timestamp.ts +1 -1
- package/dist/signals/sanitizer.d.ts.map +1 -1
- package/dist/signals/sanitizer.js.map +1 -1
- package/dist/smrt-knowledge.json +8 -8
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +2 -1
- package/dist/testing/notifications-stub.d.ts +24 -0
- package/dist/testing/notifications-stub.d.ts.map +1 -0
- package/dist/testing/notifications-stub.js +62 -0
- package/dist/testing/notifications-stub.js.map +1 -0
- package/dist/testing.js +2 -1
- package/dist/tools/tool-generator.js +1 -1
- package/dist/tools/tool-generator.js.map +1 -1
- package/dist/vite-plugin/changes-route.d.ts +14 -0
- package/dist/vite-plugin/changes-route.d.ts.map +1 -1
- package/dist/vite-plugin/changes-route.js +2 -2
- package/dist/vite-plugin/changes-route.js.map +1 -1
- package/dist/vite-plugin/events-route.d.ts +9 -0
- package/dist/vite-plugin/events-route.d.ts.map +1 -0
- package/dist/vite-plugin/events-route.js +155 -0
- package/dist/vite-plugin/events-route.js.map +1 -0
- package/dist/vite-plugin/index.d.ts +8 -0
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +8 -2
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.d.ts +8 -0
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +239 -38
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/web-collections.js +1 -1
- package/dist/vite-plugin/web-collections.js.map +1 -1
- package/package.json +12 -12
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { AUTO_GENERATED_ROUTE_HEADER } from "./route-header.js";
|
|
2
|
+
import { manifestHasTenantScopedObject, resolveAnchorClassName } from "./changes-route.js";
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
//#region src/vite-plugin/events-route.ts
|
|
6
|
+
/**
|
|
7
|
+
* SvelteKit `_events` route generation — live change signals (issue #1763,
|
|
8
|
+
* SERVER half; parent PRD #1755).
|
|
9
|
+
*
|
|
10
|
+
* Emits `{routesDir}/_events/+server.ts` — an auth-guarded, tenant-scoped
|
|
11
|
+
* Server-Sent-Events endpoint that streams coarse change signals (no row
|
|
12
|
+
* payloads). It is the push companion to the generated `_changes` route
|
|
13
|
+
* (#1758). Kept in its own module so `sveltekit-generator.ts` only carries a
|
|
14
|
+
* one-line registration.
|
|
15
|
+
*
|
|
16
|
+
* The stream lifecycle (subscribe, catch-up, heartbeat, teardown) lives in
|
|
17
|
+
* core's `buildChangeEventStream`, imported by the generated file — so the
|
|
18
|
+
* emitted route stays THIN and the stream logic is written and tested once.
|
|
19
|
+
*
|
|
20
|
+
* Design notes (mirror `changes-route.ts`):
|
|
21
|
+
* - **Fail-closed auth** (#1540 posture): the handler requires an authenticated
|
|
22
|
+
* principal on `locals`. The signal stream spans every table, so per-model
|
|
23
|
+
* `api: { public }` opt-outs deliberately do not apply.
|
|
24
|
+
* - **Tenant scoping**: when the project has tenant-scoped objects, the route
|
|
25
|
+
* establishes tenant context from `locals` exactly like the `_changes` route,
|
|
26
|
+
* then captures the resolved scope ONCE at connection open and passes it to
|
|
27
|
+
* the stream (delivery filters against that fixed value — it cannot
|
|
28
|
+
* re-resolve per signal outside the request's ALS context).
|
|
29
|
+
* - **Database resolution**: anchors on the project's first generated
|
|
30
|
+
* collection (alphabetical) via `getCollection()`, exactly as `_changes`.
|
|
31
|
+
* - Cleanup rides the generated-route sweep: the file starts with
|
|
32
|
+
* {@link AUTO_GENERATED_ROUTE_HEADER}.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Generate the `_events/+server.ts` route. Returns true when a route was
|
|
36
|
+
* written. Disabled with `sveltekit: { eventsRoute: { enabled: false } }`;
|
|
37
|
+
* skipped (with a log line) when the manifest has no objects to anchor on.
|
|
38
|
+
*/
|
|
39
|
+
function generateEventsRoute(projectRoot, manifest, options) {
|
|
40
|
+
if (options.eventsRoute?.enabled === false) return false;
|
|
41
|
+
const anchorClassName = resolveAnchorClassName(manifest);
|
|
42
|
+
if (!anchorClassName) {
|
|
43
|
+
console.log("[smrt] Skipping _events route - no SMRT objects to anchor the database on");
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const routeDir = join(projectRoot, options.routesDir, "_events");
|
|
47
|
+
const content = generateEventsRouteTemplate(anchorClassName, manifestHasTenantScopedObject(manifest));
|
|
48
|
+
if (!existsSync(routeDir)) mkdirSync(routeDir, { recursive: true });
|
|
49
|
+
const filePath = join(routeDir, "+server.ts");
|
|
50
|
+
writeFileSync(filePath, content, "utf-8");
|
|
51
|
+
console.log(`[smrt] Generated: ${filePath}`);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
function generateEventsRouteTemplate(anchorClassName, tenantScoped) {
|
|
55
|
+
return `${AUTO_GENERATED_ROUTE_HEADER}
|
|
56
|
+
// DO NOT EDIT - changes will be overwritten
|
|
57
|
+
//
|
|
58
|
+
// GET /_events — Server-Sent-Events stream of coarse change signals (#1763).
|
|
59
|
+
// Part of the client/mobile sync contract: each event names {table, operation,
|
|
60
|
+
// rowId, tenantId} (NEVER a row payload — authorization stays on the read path)
|
|
61
|
+
// and carries a seq cursor in the SSE id: field. A reconnecting EventSource
|
|
62
|
+
// resends Last-Event-ID; the route replays missed changes from it, then streams
|
|
63
|
+
// live. Same-origin only for this slice (no CORS).
|
|
64
|
+
|
|
65
|
+
import { error } from '@sveltejs/kit';
|
|
66
|
+
import {
|
|
67
|
+
buildChangeEventStream,
|
|
68
|
+
resolveDispatchTenantScope,
|
|
69
|
+
} from '@happyvertical/smrt-core';
|
|
70
|
+
import { getCollection } from '$lib/server/smrt';
|
|
71
|
+
import type { RequestHandler } from './$types';
|
|
72
|
+
|
|
73
|
+
// Fail-closed authorization (#1540): the signal stream spans every table, so
|
|
74
|
+
// it is never public — an authenticated principal on \`locals\` is required.
|
|
75
|
+
function hasAuthenticatedPrincipal(locals: unknown): boolean {
|
|
76
|
+
if (!locals || typeof locals !== 'object') return false;
|
|
77
|
+
const l = locals as Record<string, unknown>;
|
|
78
|
+
const isResolvedPrincipal = (v: unknown) =>
|
|
79
|
+
typeof v === 'object' && v !== null;
|
|
80
|
+
return (
|
|
81
|
+
isResolvedPrincipal(l.user) ||
|
|
82
|
+
isResolvedPrincipal(l.session) ||
|
|
83
|
+
l.smrtAuth === true
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function requireRouteAuth(locals: unknown): void {
|
|
88
|
+
if (!hasAuthenticatedPrincipal(locals)) {
|
|
89
|
+
throw error(401, 'Authentication required');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Last-Event-ID (auto-reconnect) takes precedence over ?since=; default is
|
|
94
|
+
// live-forward only (no catch-up replay).
|
|
95
|
+
function parseCursor(request: Request, url: URL): number | null {
|
|
96
|
+
const lastEventId = request.headers.get('Last-Event-ID');
|
|
97
|
+
if (lastEventId !== null && lastEventId.trim() !== '') {
|
|
98
|
+
const n = Number(lastEventId);
|
|
99
|
+
if (Number.isFinite(n) && n >= 0) return Math.floor(n);
|
|
100
|
+
}
|
|
101
|
+
const since = url.searchParams.get('since');
|
|
102
|
+
if (since !== null && since.trim() !== '') {
|
|
103
|
+
const n = Number(since);
|
|
104
|
+
if (Number.isFinite(n) && n >= 0) return Math.floor(n);
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
${tenantScoped ? `
|
|
109
|
+
import { enterTenantContext, hasTenantContext } from '@happyvertical/smrt-tenancy';
|
|
110
|
+
|
|
111
|
+
function establishTenantContext(locals: unknown): void {
|
|
112
|
+
if (hasTenantContext()) return;
|
|
113
|
+
if (!locals || typeof locals !== 'object') return;
|
|
114
|
+
const l = locals as Record<string, unknown>;
|
|
115
|
+
const user = l.user as Record<string, unknown> | undefined;
|
|
116
|
+
const session = l.session as Record<string, unknown> | undefined;
|
|
117
|
+
const tenantId = l.tenantId ?? user?.tenantId ?? session?.tenantId;
|
|
118
|
+
if (typeof tenantId === 'string' && tenantId) {
|
|
119
|
+
enterTenantContext({ tenantId });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
` : ""}
|
|
123
|
+
export const GET: RequestHandler = async ({ locals, url, request }) => {
|
|
124
|
+
requireRouteAuth(locals);${tenantScoped ? "\n establishTenantContext(locals);" : ""}
|
|
125
|
+
|
|
126
|
+
const cursor = parseCursor(request, url);
|
|
127
|
+
// Capture the tenant scope ONCE at connection open — signal delivery runs
|
|
128
|
+
// outside this request's tenant context and must filter against a fixed
|
|
129
|
+
// value, not a per-signal re-resolution. resolveDispatchTenantScope() reads
|
|
130
|
+
// the context established above (fail-closed: tenancy on + no tenant → global
|
|
131
|
+
// rows only; tenancy off → unenforced, all rows).
|
|
132
|
+
const tenantScope = resolveDispatchTenantScope();
|
|
133
|
+
|
|
134
|
+
// The feed lives in the project's database; anchor on the
|
|
135
|
+
// ${anchorClassName} collection to reuse its configured connection.
|
|
136
|
+
const collection = await getCollection('${anchorClassName}');
|
|
137
|
+
return new Response(
|
|
138
|
+
buildChangeEventStream(collection.db, { cursor, tenantScope }),
|
|
139
|
+
{
|
|
140
|
+
status: 200,
|
|
141
|
+
headers: {
|
|
142
|
+
'Content-Type': 'text/event-stream',
|
|
143
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
144
|
+
Connection: 'keep-alive',
|
|
145
|
+
'X-Accel-Buffering': 'no',
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
`;
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
export { generateEventsRoute };
|
|
154
|
+
|
|
155
|
+
//# sourceMappingURL=events-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events-route.js","names":[],"sources":["../../src/vite-plugin/events-route.ts"],"sourcesContent":["/**\n * SvelteKit `_events` route generation — live change signals (issue #1763,\n * SERVER half; parent PRD #1755).\n *\n * Emits `{routesDir}/_events/+server.ts` — an auth-guarded, tenant-scoped\n * Server-Sent-Events endpoint that streams coarse change signals (no row\n * payloads). It is the push companion to the generated `_changes` route\n * (#1758). Kept in its own module so `sveltekit-generator.ts` only carries a\n * one-line registration.\n *\n * The stream lifecycle (subscribe, catch-up, heartbeat, teardown) lives in\n * core's `buildChangeEventStream`, imported by the generated file — so the\n * emitted route stays THIN and the stream logic is written and tested once.\n *\n * Design notes (mirror `changes-route.ts`):\n * - **Fail-closed auth** (#1540 posture): the handler requires an authenticated\n * principal on `locals`. The signal stream spans every table, so per-model\n * `api: { public }` opt-outs deliberately do not apply.\n * - **Tenant scoping**: when the project has tenant-scoped objects, the route\n * establishes tenant context from `locals` exactly like the `_changes` route,\n * then captures the resolved scope ONCE at connection open and passes it to\n * the stream (delivery filters against that fixed value — it cannot\n * re-resolve per signal outside the request's ALS context).\n * - **Database resolution**: anchors on the project's first generated\n * collection (alphabetical) via `getCollection()`, exactly as `_changes`.\n * - Cleanup rides the generated-route sweep: the file starts with\n * {@link AUTO_GENERATED_ROUTE_HEADER}.\n */\n\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { SmartObjectManifest } from '../scanner/types';\nimport {\n manifestHasTenantScopedObject,\n resolveAnchorClassName,\n} from './changes-route.js';\nimport { AUTO_GENERATED_ROUTE_HEADER } from './route-header.js';\nimport type { SvelteKitOptions } from './sveltekit-generator.js';\n\n/**\n * Generate the `_events/+server.ts` route. Returns true when a route was\n * written. Disabled with `sveltekit: { eventsRoute: { enabled: false } }`;\n * skipped (with a log line) when the manifest has no objects to anchor on.\n */\nexport function generateEventsRoute(\n projectRoot: string,\n manifest: SmartObjectManifest,\n options: SvelteKitOptions,\n): boolean {\n if (options.eventsRoute?.enabled === false) {\n return false;\n }\n\n const anchorClassName = resolveAnchorClassName(manifest);\n if (!anchorClassName) {\n console.log(\n '[smrt] Skipping _events route - no SMRT objects to anchor the database on',\n );\n return false;\n }\n\n const routeDir = join(projectRoot, options.routesDir, '_events');\n const content = generateEventsRouteTemplate(\n anchorClassName,\n manifestHasTenantScopedObject(manifest),\n );\n\n if (!existsSync(routeDir)) {\n mkdirSync(routeDir, { recursive: true });\n }\n const filePath = join(routeDir, '+server.ts');\n writeFileSync(filePath, content, 'utf-8');\n console.log(`[smrt] Generated: ${filePath}`);\n return true;\n}\n\nfunction generateEventsRouteTemplate(\n anchorClassName: string,\n tenantScoped: boolean,\n): string {\n const tenantHelper = tenantScoped\n ? `\nimport { enterTenantContext, hasTenantContext } from '@happyvertical/smrt-tenancy';\n\nfunction establishTenantContext(locals: unknown): void {\n if (hasTenantContext()) return;\n if (!locals || typeof locals !== 'object') return;\n const l = locals as Record<string, unknown>;\n const user = l.user as Record<string, unknown> | undefined;\n const session = l.session as Record<string, unknown> | undefined;\n const tenantId = l.tenantId ?? user?.tenantId ?? session?.tenantId;\n if (typeof tenantId === 'string' && tenantId) {\n enterTenantContext({ tenantId });\n }\n}\n`\n : '';\n const tenantCall = tenantScoped ? '\\n establishTenantContext(locals);' : '';\n\n return `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n//\n// GET /_events — Server-Sent-Events stream of coarse change signals (#1763).\n// Part of the client/mobile sync contract: each event names {table, operation,\n// rowId, tenantId} (NEVER a row payload — authorization stays on the read path)\n// and carries a seq cursor in the SSE id: field. A reconnecting EventSource\n// resends Last-Event-ID; the route replays missed changes from it, then streams\n// live. Same-origin only for this slice (no CORS).\n\nimport { error } from '@sveltejs/kit';\nimport {\n buildChangeEventStream,\n resolveDispatchTenantScope,\n} from '@happyvertical/smrt-core';\nimport { getCollection } from '$lib/server/smrt';\nimport type { RequestHandler } from './$types';\n\n// Fail-closed authorization (#1540): the signal stream spans every table, so\n// it is never public — an authenticated principal on \\`locals\\` is required.\nfunction hasAuthenticatedPrincipal(locals: unknown): boolean {\n if (!locals || typeof locals !== 'object') return false;\n const l = locals as Record<string, unknown>;\n const isResolvedPrincipal = (v: unknown) =>\n typeof v === 'object' && v !== null;\n return (\n isResolvedPrincipal(l.user) ||\n isResolvedPrincipal(l.session) ||\n l.smrtAuth === true\n );\n}\n\nfunction requireRouteAuth(locals: unknown): void {\n if (!hasAuthenticatedPrincipal(locals)) {\n throw error(401, 'Authentication required');\n }\n}\n\n// Last-Event-ID (auto-reconnect) takes precedence over ?since=; default is\n// live-forward only (no catch-up replay).\nfunction parseCursor(request: Request, url: URL): number | null {\n const lastEventId = request.headers.get('Last-Event-ID');\n if (lastEventId !== null && lastEventId.trim() !== '') {\n const n = Number(lastEventId);\n if (Number.isFinite(n) && n >= 0) return Math.floor(n);\n }\n const since = url.searchParams.get('since');\n if (since !== null && since.trim() !== '') {\n const n = Number(since);\n if (Number.isFinite(n) && n >= 0) return Math.floor(n);\n }\n return null;\n}\n${tenantHelper}\nexport const GET: RequestHandler = async ({ locals, url, request }) => {\n requireRouteAuth(locals);${tenantCall}\n\n const cursor = parseCursor(request, url);\n // Capture the tenant scope ONCE at connection open — signal delivery runs\n // outside this request's tenant context and must filter against a fixed\n // value, not a per-signal re-resolution. resolveDispatchTenantScope() reads\n // the context established above (fail-closed: tenancy on + no tenant → global\n // rows only; tenancy off → unenforced, all rows).\n const tenantScope = resolveDispatchTenantScope();\n\n // The feed lives in the project's database; anchor on the\n // ${anchorClassName} collection to reuse its configured connection.\n const collection = await getCollection('${anchorClassName}');\n return new Response(\n buildChangeEventStream(collection.db, { cursor, tenantScope }),\n {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n },\n },\n );\n};\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,SAAgB,oBACd,aACA,UACA,SACS;CACT,IAAI,QAAQ,aAAa,YAAY,OACnC,OAAO;CAGT,MAAM,kBAAkB,uBAAuB,QAAQ;CACvD,IAAI,CAAC,iBAAiB;EACpB,QAAQ,IACN,2EACF;EACA,OAAO;CACT;CAEA,MAAM,WAAW,KAAK,aAAa,QAAQ,WAAW,SAAS;CAC/D,MAAM,UAAU,4BACd,iBACA,8BAA8B,QAAQ,CACxC;CAEA,IAAI,CAAC,WAAW,QAAQ,GACtB,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CAEzC,MAAM,WAAW,KAAK,UAAU,YAAY;CAC5C,cAAc,UAAU,SAAS,OAAO;CACxC,QAAQ,IAAI,qBAAqB,UAAU;CAC3C,OAAO;AACT;AAEA,SAAS,4BACP,iBACA,cACQ;CAoBR,OAAO,GAAG,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAnBjB,eACjB;;;;;;;;;;;;;;IAeA,GAwDS;;6BAvDM,eAAe,wCAAwC,GAyDpC;;;;;;;;;;;OAWjC,gBAAgB;4CACqB,gBAAgB;;;;;;;;;;;;;;;AAe5D"}
|
|
@@ -53,6 +53,14 @@ export interface SmrtPluginOptions {
|
|
|
53
53
|
changesRoute?: {
|
|
54
54
|
enabled?: boolean;
|
|
55
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* Live `_events` SSE route generation (#1763). Enabled by default (the
|
|
58
|
+
* route is auth-guarded fail-closed, same-origin only); `{ enabled: false }`
|
|
59
|
+
* skips it.
|
|
60
|
+
*/
|
|
61
|
+
eventsRoute?: {
|
|
62
|
+
enabled?: boolean;
|
|
63
|
+
};
|
|
56
64
|
};
|
|
57
65
|
/** Domain-scoped agent/developer knowledge artifact generation. */
|
|
58
66
|
knowledge?: DomainKnowledgeConfig | false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAIlE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAkB5D,YAAY,EACV,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,6BAA6B,EAC7B,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAalC,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,6BAA6B;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,8CAA8C;IAC9C,SAAS,CAAC,EAAE;QACV,yDAAyD;QACzD,OAAO,EAAE,OAAO,CAAC;QACjB,wEAAwE;QACxE,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,qEAAqE;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mEAAmE;QACnE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mDAAmD;QACnD,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;WAIG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB;;;WAGG;QACH,YAAY,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAIlE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAkB5D,YAAY,EACV,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,6BAA6B,EAC7B,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAalC,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,6BAA6B;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,8CAA8C;IAC9C,SAAS,CAAC,EAAE;QACV,yDAAyD;QACzD,OAAO,EAAE,OAAO,CAAC;QACjB,wEAAwE;QACxE,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,qEAAqE;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mEAAmE;QACnE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mDAAmD;QACnD,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;WAIG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB;;;WAGG;QACH,YAAY,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QACrC;;;;WAIG;QACH,WAAW,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;KACrC,CAAC;IACF,mEAAmE;IACnE,SAAS,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAC1C;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AA+BD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,mBAAmB,GAC5B,MAAM,CAaR;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAu5BlE"}
|
|
@@ -260,6 +260,7 @@ function smrtPlugin(options = {}) {
|
|
|
260
260
|
configFileName: svelteKit.configFileName || "smrt.ts",
|
|
261
261
|
kebabRoutes: svelteKit.kebabRoutes ?? false,
|
|
262
262
|
changesRoute: svelteKit.changesRoute,
|
|
263
|
+
eventsRoute: svelteKit.eventsRoute,
|
|
263
264
|
knowledge: await resolveKnowledgeConfig(resolvedConfig.root, manifest)
|
|
264
265
|
});
|
|
265
266
|
},
|
|
@@ -324,6 +325,7 @@ function smrtPlugin(options = {}) {
|
|
|
324
325
|
configFileName: svelteKit.configFileName || "smrt.ts",
|
|
325
326
|
kebabRoutes: svelteKit.kebabRoutes ?? false,
|
|
326
327
|
changesRoute: svelteKit.changesRoute,
|
|
328
|
+
eventsRoute: svelteKit.eventsRoute,
|
|
327
329
|
knowledge: await resolveKnowledgeConfig(server.config.root, manifest)
|
|
328
330
|
});
|
|
329
331
|
Object.values(VIRTUAL_MODULES).forEach((id) => {
|
|
@@ -352,6 +354,7 @@ function smrtPlugin(options = {}) {
|
|
|
352
354
|
configFileName: svelteKit.configFileName || "smrt.ts",
|
|
353
355
|
kebabRoutes: svelteKit.kebabRoutes ?? false,
|
|
354
356
|
changesRoute: svelteKit.changesRoute,
|
|
357
|
+
eventsRoute: svelteKit.eventsRoute,
|
|
355
358
|
knowledge: await resolveKnowledgeConfig(server.config.root, manifest)
|
|
356
359
|
});
|
|
357
360
|
}
|
|
@@ -376,6 +379,7 @@ function smrtPlugin(options = {}) {
|
|
|
376
379
|
configFileName: svelteKit.configFileName || "smrt.ts",
|
|
377
380
|
kebabRoutes: svelteKit.kebabRoutes ?? false,
|
|
378
381
|
changesRoute: svelteKit.changesRoute,
|
|
382
|
+
eventsRoute: svelteKit.eventsRoute,
|
|
379
383
|
knowledge: await resolveKnowledgeConfig(server.config.root, manifest)
|
|
380
384
|
});
|
|
381
385
|
}
|
|
@@ -463,10 +467,11 @@ function smrtPlugin(options = {}) {
|
|
|
463
467
|
try {
|
|
464
468
|
const { OxcScanner, ManifestAdapter } = await importScanner();
|
|
465
469
|
console.log(`[smrt] Using experimental OXC scanner for faster builds`);
|
|
470
|
+
const safeExclude = [...exclude || [], "**/*.svelte"];
|
|
466
471
|
const { results, resolved } = await new OxcScanner({
|
|
467
472
|
cwd: rootDir,
|
|
468
473
|
include,
|
|
469
|
-
exclude:
|
|
474
|
+
exclude: safeExclude
|
|
470
475
|
}).scanAndResolve();
|
|
471
476
|
if (results.errors.length > 0) {
|
|
472
477
|
console.error(`\n[smrt] ❌ Build failed: ${results.errors.length} error(s) during scanning:\n`);
|
|
@@ -626,7 +631,8 @@ export function createClient(basePath = '/api/v1') {
|
|
|
626
631
|
return options.kebabRoutes ? methodNameToKebab(methodName) : methodName;
|
|
627
632
|
};
|
|
628
633
|
const customMethodImpls = customMethods.map(([methodName, _method]) => {
|
|
629
|
-
|
|
634
|
+
const urlSegment = segmentFor(methodName);
|
|
635
|
+
return ` ${methodName}: (id, options) => __smrtFetchJson(basePath + '/${collection}/' + id + '/${urlSegment}', {
|
|
630
636
|
method: 'POST',
|
|
631
637
|
headers: { 'Content-Type': 'application/json' },
|
|
632
638
|
body: JSON.stringify(options || {})
|