@happyvertical/smrt-core 0.38.2 → 0.38.3

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 (128) hide show
  1. package/AGENTS.md +11 -2
  2. package/dist/change-feed.d.ts +19 -10
  3. package/dist/change-feed.d.ts.map +1 -1
  4. package/dist/change-feed.js +43 -13
  5. package/dist/change-feed.js.map +1 -1
  6. package/dist/change-signals.d.ts +83 -0
  7. package/dist/change-signals.d.ts.map +1 -0
  8. package/dist/change-signals.js +272 -0
  9. package/dist/change-signals.js.map +1 -0
  10. package/dist/collection-cache.d.ts +21 -0
  11. package/dist/collection-cache.d.ts.map +1 -1
  12. package/dist/collection-cache.js +0 -0
  13. package/dist/collection-cache.js.map +1 -1
  14. package/dist/config.d.ts +1 -1
  15. package/dist/config.d.ts.map +1 -1
  16. package/dist/config.js.map +1 -1
  17. package/dist/decorators/index.d.ts +8 -0
  18. package/dist/decorators/index.d.ts.map +1 -1
  19. package/dist/decorators/index.js.map +1 -1
  20. package/dist/errors.js +2 -1
  21. package/dist/errors.js.map +1 -1
  22. package/dist/generators/changes-route.d.ts +15 -27
  23. package/dist/generators/changes-route.d.ts.map +1 -1
  24. package/dist/generators/changes-route.js +17 -7
  25. package/dist/generators/changes-route.js.map +1 -1
  26. package/dist/generators/cli.d.ts +3 -0
  27. package/dist/generators/cli.d.ts.map +1 -1
  28. package/dist/generators/cli.js +7 -4
  29. package/dist/generators/cli.js.map +1 -1
  30. package/dist/generators/conditional-get.d.ts +17 -6
  31. package/dist/generators/conditional-get.d.ts.map +1 -1
  32. package/dist/generators/conditional-get.js +19 -13
  33. package/dist/generators/conditional-get.js.map +1 -1
  34. package/dist/generators/events-route.d.ts +72 -0
  35. package/dist/generators/events-route.d.ts.map +1 -0
  36. package/dist/generators/events-route.js +234 -0
  37. package/dist/generators/events-route.js.map +1 -0
  38. package/dist/generators/index.d.ts +1 -0
  39. package/dist/generators/index.d.ts.map +1 -1
  40. package/dist/generators/index.js +2 -1
  41. package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
  42. package/dist/generators/mcp-runtime-template.js +11 -5
  43. package/dist/generators/mcp-runtime-template.js.map +1 -1
  44. package/dist/generators/mcp.d.ts +3 -0
  45. package/dist/generators/mcp.d.ts.map +1 -1
  46. package/dist/generators/mcp.js +18 -9
  47. package/dist/generators/mcp.js.map +1 -1
  48. package/dist/generators/rest.d.ts +7 -1
  49. package/dist/generators/rest.d.ts.map +1 -1
  50. package/dist/generators/rest.js +77 -13
  51. package/dist/generators/rest.js.map +1 -1
  52. package/dist/generators.js +2 -1
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +3 -1
  56. package/dist/manifest/generator.js.map +1 -1
  57. package/dist/manifest/manifest-loader.js.map +1 -1
  58. package/dist/manifest/static-manifest.d.ts.map +1 -1
  59. package/dist/manifest/static-manifest.js +7 -3
  60. package/dist/manifest/static-manifest.js.map +1 -1
  61. package/dist/manifest/store.js +1 -1
  62. package/dist/manifest/store.js.map +1 -1
  63. package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
  64. package/dist/manifest/test-manifest-stub.js +1050 -124
  65. package/dist/manifest/test-manifest-stub.js.map +1 -1
  66. package/dist/manifest.json +9 -3
  67. package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts +1 -0
  68. package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts.map +1 -1
  69. package/dist/mcp-advisor/types.d.ts +3 -0
  70. package/dist/mcp-advisor/types.d.ts.map +1 -1
  71. package/dist/migrations/tracker.js +5 -4
  72. package/dist/migrations/tracker.js.map +1 -1
  73. package/dist/migrations/types.d.ts +1 -1
  74. package/dist/migrations/types.d.ts.map +1 -1
  75. package/dist/object.d.ts +17 -8
  76. package/dist/object.d.ts.map +1 -1
  77. package/dist/object.js +42 -26
  78. package/dist/object.js.map +1 -1
  79. package/dist/registry/class-registration.d.ts.map +1 -1
  80. package/dist/registry/class-registration.js +12 -0
  81. package/dist/registry/class-registration.js.map +1 -1
  82. package/dist/registry/shared-state.d.ts +2 -1
  83. package/dist/registry/shared-state.d.ts.map +1 -1
  84. package/dist/registry/shared-state.js.map +1 -1
  85. package/dist/registry/types.d.ts.map +1 -1
  86. package/dist/registry.d.ts.map +1 -1
  87. package/dist/registry.js +1 -1
  88. package/dist/registry.js.map +1 -1
  89. package/dist/runtime/client.d.ts.map +1 -1
  90. package/dist/runtime/client.js.map +1 -1
  91. package/dist/scanner/manifest-generator.js.map +1 -1
  92. package/dist/scanner/types.d.ts +7 -0
  93. package/dist/scanner/types.d.ts.map +1 -1
  94. package/dist/schema/ddl/base-strategy.js +1 -1
  95. package/dist/schema/ddl/base-strategy.js.map +1 -1
  96. package/dist/scripts/migrate-datetime-to-timestamp.ts +1 -1
  97. package/dist/signals/sanitizer.d.ts.map +1 -1
  98. package/dist/signals/sanitizer.js.map +1 -1
  99. package/dist/smrt-knowledge.json +8 -8
  100. package/dist/testing/index.d.ts +1 -0
  101. package/dist/testing/index.d.ts.map +1 -1
  102. package/dist/testing/index.js +2 -1
  103. package/dist/testing/notifications-stub.d.ts +24 -0
  104. package/dist/testing/notifications-stub.d.ts.map +1 -0
  105. package/dist/testing/notifications-stub.js +62 -0
  106. package/dist/testing/notifications-stub.js.map +1 -0
  107. package/dist/testing.js +2 -1
  108. package/dist/tools/tool-generator.js +1 -1
  109. package/dist/tools/tool-generator.js.map +1 -1
  110. package/dist/vite-plugin/changes-route.d.ts +14 -0
  111. package/dist/vite-plugin/changes-route.d.ts.map +1 -1
  112. package/dist/vite-plugin/changes-route.js +2 -2
  113. package/dist/vite-plugin/changes-route.js.map +1 -1
  114. package/dist/vite-plugin/events-route.d.ts +9 -0
  115. package/dist/vite-plugin/events-route.d.ts.map +1 -0
  116. package/dist/vite-plugin/events-route.js +155 -0
  117. package/dist/vite-plugin/events-route.js.map +1 -0
  118. package/dist/vite-plugin/index.d.ts +8 -0
  119. package/dist/vite-plugin/index.d.ts.map +1 -1
  120. package/dist/vite-plugin/index.js +8 -2
  121. package/dist/vite-plugin/index.js.map +1 -1
  122. package/dist/vite-plugin/sveltekit-generator.d.ts +8 -0
  123. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  124. package/dist/vite-plugin/sveltekit-generator.js +193 -31
  125. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  126. package/dist/vite-plugin/web-collections.js +1 -1
  127. package/dist/vite-plugin/web-collections.js.map +1 -1
  128. 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;KACtC,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,CAm5BlE"}
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: [...exclude || [], "**/*.svelte"]
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
- return ` ${methodName}: (id, options) => __smrtFetchJson(basePath + '/${collection}/' + id + '/${segmentFor(methodName)}', {
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 || {})