@notis_ai/cli 0.2.0-beta.191.1 → 0.2.0-beta.197.1

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 (107) hide show
  1. package/README.md +485 -86
  2. package/config/portal-route-roots.json +47 -0
  3. package/dist/agent-hooks/notis-agent-hook.mjs +32663 -6357
  4. package/dist/base-skills/notis-apps/SKILL.md +88 -67
  5. package/dist/base-skills/notis-apps/references/architecture.md +114 -167
  6. package/dist/base-skills/notis-apps/references/design.md +26 -107
  7. package/dist/base-skills/notis-apps/references/memory.md +58 -0
  8. package/dist/base-skills/notis-apps/references/reading.md +52 -74
  9. package/dist/base-skills/notis-apps/references/release.md +104 -142
  10. package/dist/base-skills/notis-apps/references/sdk.md +113 -60
  11. package/dist/base-skills/notis-apps/references/troubleshooting.md +22 -21
  12. package/dist/base-skills/notis-apps/references/views.md +142 -0
  13. package/dist/base-skills/notis-cli/SKILL.md +4 -4
  14. package/dist/base-skills/notis-cli/references/app-delivery.md +12 -11
  15. package/dist/base-skills/notis-cli/references/native-databases.md +6 -6
  16. package/dist/base-skills/notis-cli/references/tool-examples.md +4 -4
  17. package/dist/base-skills/notis-query/SKILL.md +34 -60
  18. package/dist/base-skills/notis-query/references/database-discovery.md +59 -69
  19. package/dist/base-skills/notis-query/references/documents.md +63 -50
  20. package/dist/base-skills/notis-query/references/query.md +57 -524
  21. package/dist/sdk/src/bundleModule.ts +79 -0
  22. package/dist/sdk/src/components/DocumentEditor.tsx +6 -3
  23. package/dist/sdk/src/components/DocumentPage.tsx +141 -0
  24. package/dist/sdk/src/components/RecordComponents.tsx +33 -0
  25. package/dist/sdk/src/components/RenderChartData.tsx +19 -0
  26. package/dist/sdk/src/config.ts +4 -26
  27. package/dist/sdk/src/documents.ts +2 -0
  28. package/dist/sdk/src/hooks/useAction.ts +28 -0
  29. package/dist/sdk/src/hooks/useCloudComputer.ts +35 -4
  30. package/dist/sdk/src/hooks/useCloudComputerShell.ts +114 -0
  31. package/dist/sdk/src/hooks/useHandover.ts +5 -4
  32. package/dist/sdk/src/hooks/useNativeDocumentBody.ts +50 -0
  33. package/dist/sdk/src/hooks/useNativeDocuments.ts +83 -0
  34. package/dist/sdk/src/hooks/useNotis.ts +3 -0
  35. package/dist/sdk/src/hooks/useNotisNavigation.ts +26 -11
  36. package/dist/sdk/src/hooks/useQuery.ts +7 -4
  37. package/dist/sdk/src/hooks/useViewParams.ts +121 -0
  38. package/dist/sdk/src/hooks/useViewerReads.ts +129 -0
  39. package/dist/sdk/src/index.ts +36 -0
  40. package/dist/sdk/src/interactions/shortcuts.tsx +17 -3
  41. package/dist/sdk/src/nativeData.ts +39 -0
  42. package/dist/sdk/src/presentation.tsx +82 -0
  43. package/dist/sdk/src/provider.tsx +9 -4
  44. package/dist/sdk/src/queryCache.ts +5 -3
  45. package/dist/sdk/src/queryErrors.ts +51 -0
  46. package/dist/sdk/src/runtime.ts +146 -8
  47. package/dist/sdk/src/space.ts +425 -0
  48. package/dist/sdk/src/styles.css +96 -72
  49. package/dist/sdk/src/vite.ts +11 -1
  50. package/dist/skill-sync/index.js +458 -68
  51. package/dist/skill-sync/index.js.map +4 -4
  52. package/dist/skill-sync-worker.mjs +451 -77
  53. package/dist/space-harness/host.js +66 -0
  54. package/dist/view-renderer/dist/host.css +1 -0
  55. package/dist/view-renderer/dist/host.js +1042 -0
  56. package/dist/view-renderer/dist/theme.css +73 -0
  57. package/dist/view-renderer/src/dom.js +180 -0
  58. package/dist/view-renderer/src/errors.js +8 -0
  59. package/dist/view-renderer/src/host.tsx +173 -0
  60. package/dist/view-renderer/src/index.js +4 -0
  61. package/dist/view-renderer/src/pool.js +76 -0
  62. package/dist/view-renderer/src/remote-broker.js +59 -0
  63. package/dist/view-renderer/src/renderer.js +201 -0
  64. package/package.json +11 -5
  65. package/skills/notis-apps/cli.md +270 -163
  66. package/skills/notis-cli/AGENT_INSTRUCTIONS.md +3 -3
  67. package/skills/notis-onboarding/BRIEF.md +7 -3
  68. package/src/command-specs/apps.js +1 -8
  69. package/src/command-specs/files.js +29 -0
  70. package/src/command-specs/handover.js +45 -16
  71. package/src/command-specs/helpers.js +1 -0
  72. package/src/command-specs/index.js +10 -4
  73. package/src/command-specs/skills.js +229 -0
  74. package/src/command-specs/spaces.js +572 -0
  75. package/src/command-specs/views.js +69 -0
  76. package/src/runtime/agent-browser.js +30 -7
  77. package/src/runtime/app-boundary-validator.js +7 -4
  78. package/src/runtime/app-data-literals.js +82 -0
  79. package/src/runtime/app-platform.js +321 -253
  80. package/src/runtime/confined-space-build.js +74 -0
  81. package/src/runtime/confined-space-verify.js +49 -0
  82. package/src/runtime/native-record-files.js +47 -0
  83. package/src/runtime/retired-sdk-files.js +20 -0
  84. package/src/runtime/skill-sync/cloud-client.ts +26 -4
  85. package/src/runtime/skill-sync/index.ts +154 -35
  86. package/src/runtime/skill-sync/local-scanner.ts +125 -17
  87. package/src/runtime/skill-sync/native-identity.ts +102 -0
  88. package/src/runtime/skill-sync/native-plan.ts +59 -0
  89. package/src/runtime/skill-sync/sync-plan.ts +16 -9
  90. package/src/runtime/skill-sync/types.ts +32 -6
  91. package/src/runtime/source-root.js +32 -0
  92. package/src/runtime/space-collection-scaffold.js +66 -0
  93. package/src/runtime/space-database-structures.js +229 -0
  94. package/src/runtime/space-fixture-broker.js +50 -0
  95. package/src/runtime/space-lock.js +337 -0
  96. package/src/runtime/space-platform.js +105 -0
  97. package/src/runtime/space-source.js +281 -0
  98. package/src/runtime/space-test-server.js +327 -0
  99. package/src/runtime/space-verification.js +89 -0
  100. package/src/runtime/space-verify-browser-worker.js +51 -0
  101. package/src/runtime/space-view-manifest.js +254 -0
  102. package/src/runtime/sync-skills.js +1 -1
  103. package/src/runtime/transport.js +8 -1
  104. package/src/runtime/view-render.js +49 -0
  105. package/src/space-harness-host.ts +103 -0
  106. package/template/app/globals.css +1 -0
  107. package/src/command-specs/reports.js +0 -97
@@ -1,543 +1,76 @@
1
- ## Supported execution mode
1
+ # Structured native query grammar
2
2
 
3
- `LOCAL_NOTIS_DATABASE_QUERY` supports direct structured queries only.
4
-
5
- Natural-language query generation is retired. Do not send free-form requests like "find overdue tasks from important clients"; build the structured `query` payload yourself.
6
-
7
- ## Request shape
8
-
9
- ```json
10
- {
11
- "database_id": "tasks-db-id",
12
- "database_slug": "tasks",
13
- "query": {
14
- "filter": {
15
- "operator": "and",
16
- "conditions": [
17
- {
18
- "property": "Status",
19
- "type": "status",
20
- "operator": "equals",
21
- "value": "In Progress"
22
- }
23
- ]
24
- },
25
- "sorts": [
26
- {
27
- "property": "Due Date",
28
- "direction": "ascending"
29
- }
30
- ],
31
- "page_size": 20
32
- },
33
- "offset": 0
34
- }
35
- ```
36
-
37
- Top-level fields:
38
-
39
- - One of `database_id` or `database_slug` is required.
40
- - `database_id`: stable native database ID. Prefer this when it is available from database context, `list_databases`, or `get_database`.
41
- - `database_slug`: native database slug. Use this when `database_id` is not available, or pass it alongside `database_id` as a fallback.
42
- - `query`: required object
43
- - `offset`: optional numeric pagination offset
44
-
45
- When both `database_id` and `database_slug` are provided, they must identify the same database. If the ID is stale and no database is found by ID, the runtime may fall back to the slug.
46
-
47
- `query` fields:
48
-
49
- - `filter`: optional rule tree
50
- - `sorts`: optional array of sort definitions
51
- - `page_size`: optional integer page size
52
-
53
- ## Filter shape
54
-
55
- ```json
56
- {
57
- "operator": "and",
58
- "conditions": [
59
- {
60
- "property": "Status",
61
- "type": "status",
62
- "operator": "equals",
63
- "value": "In Progress"
64
- },
65
- {
66
- "property": "Priority",
67
- "type": "select",
68
- "operator": "equals",
69
- "value": "High"
70
- }
71
- ]
72
- }
73
- ```
74
-
75
- Use nested groups when needed:
76
-
77
- ```json
78
- {
79
- "operator": "or",
80
- "conditions": [
81
- {
82
- "property": "Status",
83
- "type": "status",
84
- "operator": "equals",
85
- "value": "Todo"
86
- },
87
- {
88
- "operator": "and",
89
- "conditions": [
90
- {
91
- "property": "Priority",
92
- "type": "select",
93
- "operator": "equals",
94
- "value": "High"
95
- },
96
- {
97
- "property": "Archived",
98
- "type": "checkbox",
99
- "operator": "equals",
100
- "value": false
101
- }
102
- ]
103
- }
104
- ]
105
- }
106
- ```
107
-
108
- ## Supported property semantics
109
-
110
- Use the real Notis property name and the correct property type.
111
-
112
- ### Title and rich text
113
-
114
- Recommended operators:
115
-
116
- - `contains`
117
- - `equals`
118
- - `not_equals`
119
-
120
- Example:
121
-
122
- ```json
123
- {
124
- "property": "Title",
125
- "type": "title",
126
- "operator": "contains",
127
- "value": "launch"
128
- }
129
- ```
130
-
131
- ### Select and status
132
-
133
- Recommended operators:
134
-
135
- - `equals`
136
- - `not_equals`
137
- - `in`
138
-
139
- Example:
140
-
141
- ```json
142
- {
143
- "property": "Status",
144
- "type": "status",
145
- "operator": "equals",
146
- "value": "Done"
147
- }
148
- ```
149
-
150
- ### Multi-select
151
-
152
- Recommended operators:
153
-
154
- - `contains`
155
- - `not_contains`
156
-
157
- Example:
158
-
159
- ```json
160
- {
161
- "property": "Tags",
162
- "type": "multi_select",
163
- "operator": "contains",
164
- "value": "Urgent"
165
- }
166
- ```
167
-
168
- ### Checkbox
169
-
170
- Recommended operator:
171
-
172
- - `equals`
173
-
174
- Example:
175
-
176
- ```json
177
- {
178
- "property": "Archived",
179
- "type": "checkbox",
180
- "operator": "equals",
181
- "value": false
182
- }
183
- ```
184
-
185
- ### Number
186
-
187
- Recommended operators:
188
-
189
- - `equals`
190
- - `not_equals`
191
- - `greater_than`
192
- - `greater_than_or_equal`
193
- - `less_than`
194
- - `less_than_or_equal`
195
-
196
- Example:
197
-
198
- ```json
199
- {
200
- "property": "Score",
201
- "type": "number",
202
- "operator": "greater_than_or_equal",
203
- "value": 80
204
- }
205
- ```
206
-
207
- ### Date
208
-
209
- Recommended operators:
210
-
211
- - `equals`
212
- - `before`
213
- - `after`
214
- - `on_or_before`
215
- - `on_or_after`
216
-
217
- Use ISO dates or timestamps depending on the property precision.
218
-
219
- Example:
3
+ Discover and inspect `LOCAL_NOTIS_DATABASE_QUERY`; use its current input schema.
4
+ For native Spaces use `protocol: 1`, the exact discovery `target`, and `input`.
5
+ The native query lives directly inside `input`, without another request/query wrapper.
220
6
 
221
7
  ```json
222
8
  {
223
- "property": "Due Date",
224
- "type": "date",
225
- "operator": "on_or_before",
226
- "value": "2026-03-31"
227
- }
228
- ```
229
-
230
- ### Relation
231
-
232
- Recommended operators:
233
-
234
- - `contains`
235
- - `not_contains`
236
-
237
- Pass the related `document_id`, not the display title.
238
-
239
- Example:
240
-
241
- ```json
242
- {
243
- "property": "Project",
244
- "type": "relation",
245
- "operator": "contains",
246
- "value": "doc_project_123"
247
- }
248
- ```
249
-
250
- ### Formula
251
-
252
- Treat formula values according to the returned data type. In practice, use the matching operator family for the computed result:
253
-
254
- - text-like formula: `contains` or `equals`
255
- - number-like formula: numeric comparison operators
256
- - boolean-like formula: `equals`
257
-
258
- Only use formula filters when the formula property already exists in the schema.
259
-
260
- ## Sorts
261
-
262
- Sort objects look like this:
263
-
264
- ```json
265
- {
266
- "property": "Created At",
267
- "direction": "descending"
268
- }
269
- ```
270
-
271
- Recommended directions:
272
-
273
- - `ascending`
274
- - `descending`
275
-
276
- Common sorts:
277
-
278
- - title sorts for alphabetical browsing
279
- - status then date sorts for workflow queues
280
- - timestamp sorts such as `Created At`, `Updated At`, or `Last Edited Time`
281
-
282
- Example:
283
-
284
- ```json
285
- [
286
- {
287
- "property": "Last Edited Time",
288
- "direction": "descending"
9
+ "protocol": 1,
10
+ "target": {"space_id": "<exact-discovered-space-id>", "binding": "<exact-discovered-binding-id>"},
11
+ "input": {
12
+ "mode": "rows",
13
+ "filter": {"field": {"property": "Status"}, "op": "equals", "value": "Done"},
14
+ "sorts": [{"field": {"column": "updated_at"}, "direction": "desc"}],
15
+ "page_size": 20,
16
+ "offset": 0,
17
+ "include_content": false
289
18
  }
290
- ]
291
- ```
292
-
293
- ## Pagination
294
-
295
- Use `page_size` to cap the number of results per call.
296
-
297
- The response returns:
298
-
299
- - `documents`
300
- - `results_count`
301
- - `has_more`
302
- - `next_offset`
303
- - `query`
304
- - `complementary_instructions`
305
-
306
- Pagination rules:
307
-
308
- - start with `offset: 0` or omit it
309
- - if `has_more` is true, call again with `offset: next_offset`
310
-
311
- Example follow-up call:
312
-
313
- ```json
314
- {
315
- "database_id": "tasks-db-id",
316
- "query": {
317
- "page_size": 25
318
- },
319
- "offset": 25
320
19
  }
321
20
  ```
322
21
 
323
- ## Response shape
22
+ Use the complete Space binding target returned by schema discovery. Every
23
+ database lives in a Space: a bare `database_id` gets `resolved_target` from
24
+ `LOCAL_NOTIS_DATABASE_GET_DATABASE`, and a bare-id query answers
25
+ `database_in_spaces` with its links. Never remove its record context or substitute
26
+ another database/owner after a denial. `action_id`, when present, retains the
27
+ saved action's narrower authority rather than granting direct Editor access.
324
28
 
325
- The result includes database rows in `documents`. Each document usually contains:
29
+ ## Fields and filters
326
30
 
327
- - `document_id`
328
- - title-like display fields
329
- - matching property values
330
- - metadata helpful for follow-up reads or updates
31
+ Choose one field reference: `{property_id}`, an unambiguous `{property}` label,
32
+ or a supported `{column}` such as `record_key`, `id`, `title`, `created_at`,
33
+ `updated_at`, `last_edited_time`, `content_type`, `file_type`. Current property
34
+ names take precedence; use stable IDs when names are ambiguous.
331
35
 
332
- Use `document_id` from the query response when you need to:
36
+ A condition is `{field, op, value}`. Empty checks (`is_empty`, `is_not_empty`)
37
+ omit `value`. Combine with `{and: [conditions]}` or `{or: [conditions]}`; groups
38
+ must be nonempty. Invalid conditions fail—they never become an unrestricted query.
333
39
 
334
- - fetch full content with `LOCAL_NOTIS_DATABASE_GET_DOCUMENT`
335
- - update a record with a generated database upsert tool
336
- - attach it to a relation field in another upsert
40
+ | Type | Operators |
41
+ | --- | --- |
42
+ | Record identity | `equals`, `not_equals`, `in` |
43
+ | Text/title/URL | `equals`, `not_equals`, `contains`, `not_contains`, `starts_with`, `ends_with`, empty checks |
44
+ | Number | `equals`, `not_equals`, `greater_than`, `less_than`, inclusive variants, empty checks |
45
+ | Checkbox | `equals`, `not_equals` with real booleans |
46
+ | Select/status | `equals`, `not_equals`, empty checks; use a current option ID or unambiguous label |
47
+ | Multi-select/relation | `contains`, `does_not_contain`, empty checks |
48
+ | Date | `equals`, `before`, `after`, `on_or_before`, `on_or_after`, empty checks; use ISO dates/timestamps |
337
49
 
338
- ## Examples
50
+ A relation filter uses the related record identity returned by its native read,
51
+ not a guessed title. Do not encode a relation as a scalar text equality. Avoid
52
+ assuming every application uses the same status labels or Inbox semantics.
339
53
 
340
- ### Title or rich text contains
54
+ ## Pagination and projection
341
55
 
342
- ```json
343
- {
344
- "database_id": "notes-db-id",
345
- "query": {
346
- "filter": {
347
- "operator": "and",
348
- "conditions": [
349
- {
350
- "property": "Title",
351
- "type": "title",
352
- "operator": "contains",
353
- "value": "pricing"
354
- }
355
- ]
356
- },
357
- "page_size": 10
358
- }
359
- }
360
- ```
56
+ Use bounded pages (1–500) and the returned continuation/offset until complete.
57
+ Native row results expose `rows`, stable `record_key`, revision and schema
58
+ metadata; do not assume the older `documents` result shape. Sorts use
59
+ `{field, direction: 'asc'|'desc'}`. Multi-select/relation fields cannot be sorted.
361
60
 
362
- ### Select or status equals
61
+ Use `include_content: false` for lists. Include bodies only when needed.
62
+ Archived rows are excluded unless `include_archived: true`; binned/inaccessible
63
+ rows remain excluded by authority. Do not turn a missing page into a zero count.
363
64
 
364
- ```json
365
- {
366
- "database_id": "tasks-db-id",
367
- "query": {
368
- "filter": {
369
- "operator": "and",
370
- "conditions": [
371
- {
372
- "property": "Status",
373
- "type": "status",
374
- "operator": "equals",
375
- "value": "In Progress"
376
- }
377
- ]
378
- }
379
- }
380
- }
381
- ```
382
-
383
- ### Multi-select contains
384
-
385
- ```json
386
- {
387
- "database_id": "content-db-id",
388
- "query": {
389
- "filter": {
390
- "operator": "and",
391
- "conditions": [
392
- {
393
- "property": "Tags",
394
- "type": "multi_select",
395
- "operator": "contains",
396
- "value": "Newsletter"
397
- }
398
- ]
399
- }
400
- }
401
- }
402
- ```
403
-
404
- ### Checkbox equals
405
-
406
- ```json
407
- {
408
- "database_id": "tasks-db-id",
409
- "query": {
410
- "filter": {
411
- "operator": "and",
412
- "conditions": [
413
- {
414
- "property": "Completed",
415
- "type": "checkbox",
416
- "operator": "equals",
417
- "value": true
418
- }
419
- ]
420
- }
421
- }
422
- }
423
- ```
424
-
425
- ### Number comparison
426
-
427
- ```json
428
- {
429
- "database_id": "deals-db-id",
430
- "query": {
431
- "filter": {
432
- "operator": "and",
433
- "conditions": [
434
- {
435
- "property": "Amount",
436
- "type": "number",
437
- "operator": "greater_than",
438
- "value": 10000
439
- }
440
- ]
441
- }
442
- }
443
- }
444
- ```
445
-
446
- ### Date comparison
447
-
448
- ```json
449
- {
450
- "database_id": "tasks-db-id",
451
- "query": {
452
- "filter": {
453
- "operator": "and",
454
- "conditions": [
455
- {
456
- "property": "Due Date",
457
- "type": "date",
458
- "operator": "on_or_after",
459
- "value": "2026-03-01"
460
- }
461
- ]
462
- },
463
- "sorts": [
464
- {
465
- "property": "Due Date",
466
- "direction": "ascending"
467
- }
468
- ]
469
- }
470
- }
471
- ```
472
-
473
- ### Relation contains
474
-
475
- ```json
476
- {
477
- "database_id": "tasks-db-id",
478
- "query": {
479
- "filter": {
480
- "operator": "and",
481
- "conditions": [
482
- {
483
- "property": "Project",
484
- "type": "relation",
485
- "operator": "contains",
486
- "value": "doc_project_123"
487
- }
488
- ]
489
- }
490
- }
491
- }
492
- ```
493
-
494
- ### Timestamp sort
495
-
496
- ```json
497
- {
498
- "database_id": "tasks-db-id",
499
- "query": {
500
- "sorts": [
501
- {
502
- "property": "Last Edited Time",
503
- "direction": "descending"
504
- }
505
- ],
506
- "page_size": 20
507
- }
508
- }
509
- ```
510
-
511
- ### Pagination follow-up
512
-
513
- First call:
514
-
515
- ```json
516
- {
517
- "database_id": "tasks-db-id",
518
- "query": {
519
- "page_size": 20
520
- }
521
- }
522
- ```
523
-
524
- Second call after a response with `has_more: true` and `next_offset: 20`:
525
-
526
- ```json
527
- {
528
- "database_id": "tasks-db-id",
529
- "query": {
530
- "page_size": 20
531
- },
532
- "offset": 20
533
- }
534
- ```
65
+ ## Count, aggregate and search
535
66
 
536
- ## Practical workflow
67
+ - `mode: 'count'` applies the same authorized filter and returns its count.
68
+ - `mode: 'aggregate'` adds `aggregate: {op: 'sum'|'avg'|'min'|'max', field}` over
69
+ a numeric field. It is computed within the same native scope.
70
+ - `mode: 'search'` adds `search: {text, fields: [field, ...]}` over supported text
71
+ fields; this is structured native search, not semantic memory retrieval.
72
+ - For one current body, use `input: {mode: 'document_body', record_key}` with the
73
+ exact body-capable target. The response includes Markdown and current revisions.
537
74
 
538
- 1. Call `LOCAL_NOTIS_DATABASE_LIST_DATABASES` if you do not know the database ID yet.
539
- 2. Call `LOCAL_NOTIS_DATABASE_GET_DATABASE` if you need schema detail before building filters or relation payloads.
540
- 3. Call `LOCAL_NOTIS_DATABASE_QUERY` with `database_id` plus structured filters and sorts. Use `database_slug` only when the ID is not available.
541
- 4. If you need the full body of one row, call `LOCAL_NOTIS_DATABASE_GET_DOCUMENT` with the returned `document_id`.
542
- 5. If you need to update one of the matched rows, call the relevant generated database upsert tool with that `document_id`.
543
- 6. If you need a relation value, query the related database first and pass the resulting `document_id` into the upsert.
75
+ For visual dashboards or a link's selected params, resolve/render the view rather
76
+ than guessing that an independent query recreates its presentation or period.
@@ -0,0 +1,79 @@
1
+ /** Framework-neutral ESM loading shared by Portal and credentialless verification. */
2
+ import type React from 'react';
3
+
4
+ export function rewriteReactImports(source: string): string {
5
+ // Create inline data URLs that re-export from window globals.
6
+ // This avoids the "Cannot find module" error with blob URL imports.
7
+ const reactShim = `data:text/javascript,${encodeURIComponent(
8
+ 'const R = window.React; export default R; export const { createContext, createRef, useState, useEffect, useContext, useCallback, useMemo, useRef, useReducer, useLayoutEffect, useId, memo, forwardRef, createElement, Fragment, Children, cloneElement, isValidElement, lazy, Suspense, startTransition, useTransition, useDeferredValue, useSyncExternalStore, useInsertionEffect, useDebugValue, useImperativeHandle, Component, PureComponent } = R;'
9
+ )}`;
10
+ const jsxShim = `data:text/javascript,${encodeURIComponent(
11
+ 'const J = window.__ReactJSXRuntime || {}; const R = window.React; export const jsx = J.jsx || R.createElement; export const jsxs = J.jsxs || R.createElement; export const jsxDEV = J.jsxDEV || R.createElement; export const Fragment = J.Fragment || R.Fragment;'
12
+ )}`;
13
+ const reactDomShim = `data:text/javascript,${encodeURIComponent(
14
+ 'const RD = window.ReactDOM; export default RD; export const { createPortal, flushSync, unstable_batchedUpdates } = RD;'
15
+ )}`;
16
+ const reactDomClientShim = `data:text/javascript,${encodeURIComponent(
17
+ 'const RDC = window.ReactDOMClient; export default RDC; export const { createRoot, hydrateRoot } = RDC;'
18
+ )}`;
19
+
20
+ let result = source;
21
+ // Replace bare side-effect imports
22
+ result = result.replace(/import\s*["']react-dom\/client["'];?/g, `import "${reactDomClientShim}";`);
23
+ result = result.replace(/import\s*["']react-dom["'];?/g, `import "${reactDomShim}";`);
24
+ result = result.replace(/import\s*["']react\/jsx-runtime["'];?/g, `import "${jsxShim}";`);
25
+ result = result.replace(/import\s*["']react\/jsx-dev-runtime["'];?/g, `import "${jsxShim}";`);
26
+ result = result.replace(/import\s*["']react["'];?/g, `import "${reactShim}";`);
27
+
28
+ // Replace import statements with data URL shims
29
+ // Handle: import { ... } from "react/jsx-runtime"
30
+ result = result.replace(/from\s*["']react\/jsx-runtime["']/g, `from "${jsxShim}"`);
31
+ result = result.replace(/from\s*["']react\/jsx-dev-runtime["']/g, `from "${jsxShim}"`);
32
+ // Handle react-dom/client before react-dom
33
+ result = result.replace(/from\s*["']react-dom\/client["']/g, `from "${reactDomClientShim}"`);
34
+ // Handle: import ... from "react-dom"
35
+ result = result.replace(/from\s*["']react-dom["']/g, `from "${reactDomShim}"`);
36
+ // Handle: import ... from "react" (must come after react-dom and react/jsx-runtime)
37
+ result = result.replace(/from\s*["']react["']/g, `from "${reactShim}"`);
38
+
39
+ return result;
40
+ }
41
+
42
+ export function resolveBundleRouteExport(
43
+ moduleExports: Record<string, React.ComponentType>,
44
+ exportCandidates: string[],
45
+ strict = false,
46
+ ): { name: string; component: React.ComponentType } | null {
47
+ const namedExport = exportCandidates.find((name) => Boolean(moduleExports[name]));
48
+ if (namedExport) {
49
+ return { name: namedExport, component: moduleExports[namedExport] };
50
+ }
51
+ if (strict) return null;
52
+ if (moduleExports.default) {
53
+ return { name: 'default', component: moduleExports.default };
54
+ }
55
+
56
+ // Vite may minify an entry export even though the manifest still carries
57
+ // its source name. A bundle with one non-shell component is unambiguous, so
58
+ // keep Dev usable instead of showing "View not available" for that route.
59
+ const componentExports = Object.entries(moduleExports).filter(
60
+ ([name, value]) => name !== '__AppShell' && Boolean(value),
61
+ );
62
+ if (componentExports.length !== 1) {
63
+ return null;
64
+ }
65
+ const [name, component] = componentExports[0];
66
+ return { name, component };
67
+ }
68
+
69
+ export async function importRewrittenBundleSource(source: string): Promise<Record<string, React.ComponentType>> {
70
+ const blob = new Blob([source], { type: 'text/javascript' });
71
+ const url = URL.createObjectURL(blob);
72
+ try {
73
+ return await import(/* webpackIgnore: true */ url) as Record<string, React.ComponentType>;
74
+ } finally { URL.revokeObjectURL(url); }
75
+ }
76
+
77
+ export function importBundleSource(source: string) {
78
+ return importRewrittenBundleSource(rewriteReactImports(source));
79
+ }