@exponential_app/engine 0.0.0-stage → 0.1.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 (85) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +54 -0
  3. package/README.md +35 -2
  4. package/dist/capabilities.js +29 -0
  5. package/dist/capabilities.js.map +9 -0
  6. package/dist/chunks/index-3nf9f7pv.js +315 -0
  7. package/dist/chunks/index-3nf9f7pv.js.map +10 -0
  8. package/dist/chunks/index-3rgr16n2.js +443 -0
  9. package/dist/chunks/index-3rgr16n2.js.map +9 -0
  10. package/dist/chunks/index-79swfbsm.js +366 -0
  11. package/dist/chunks/index-79swfbsm.js.map +10 -0
  12. package/dist/chunks/index-knntt4cb.js +259 -0
  13. package/dist/chunks/index-knntt4cb.js.map +10 -0
  14. package/dist/chunks/index-m2845dn6.js +72 -0
  15. package/dist/chunks/index-m2845dn6.js.map +10 -0
  16. package/dist/chunks/index-pqpnz4ns.js +2213 -0
  17. package/dist/chunks/index-pqpnz4ns.js.map +9 -0
  18. package/dist/chunks/index-q4zws02e.js +2301 -0
  19. package/dist/chunks/index-q4zws02e.js.map +10 -0
  20. package/dist/chunks/index-s3xjr701.js +170 -0
  21. package/dist/chunks/index-s3xjr701.js.map +10 -0
  22. package/dist/chunks/index-sk8b0dh4.js +126 -0
  23. package/dist/chunks/index-sk8b0dh4.js.map +10 -0
  24. package/dist/chunks/index-tvsykgcv.js +3015 -0
  25. package/dist/chunks/index-tvsykgcv.js.map +19 -0
  26. package/dist/chunks/index-v0m7r4y3.js +6869 -0
  27. package/dist/chunks/index-v0m7r4y3.js.map +13 -0
  28. package/dist/consent.js +24 -0
  29. package/dist/consent.js.map +9 -0
  30. package/dist/index.js +654 -0
  31. package/dist/index.js.map +12 -0
  32. package/dist/lifecycle.js +11 -0
  33. package/dist/lifecycle.js.map +9 -0
  34. package/dist/outbox.js +27 -0
  35. package/dist/outbox.js.map +9 -0
  36. package/dist/pdf.js +15 -0
  37. package/dist/pdf.js.map +9 -0
  38. package/dist/providers.js +26 -0
  39. package/dist/providers.js.map +9 -0
  40. package/dist/runner.js +24 -0
  41. package/dist/runner.js.map +9 -0
  42. package/dist/tools.js +38 -0
  43. package/dist/tools.js.map +9 -0
  44. package/dist/types/domain-contract/src/app-records.d.ts +3148 -0
  45. package/dist/types/domain-contract/src/app-share.d.ts +222 -0
  46. package/dist/types/domain-contract/src/apps.d.ts +612 -0
  47. package/dist/types/eengine/fixtures/providers.json +55 -0
  48. package/dist/types/eengine/src/broker.d.ts +80 -0
  49. package/dist/types/eengine/src/capabilities.d.ts +88 -0
  50. package/dist/types/eengine/src/consent.d.ts +50 -0
  51. package/dist/types/eengine/src/index.d.ts +12 -0
  52. package/dist/types/eengine/src/instance.d.ts +131 -0
  53. package/dist/types/eengine/src/lifecycle.d.ts +64 -0
  54. package/dist/types/eengine/src/outbox.d.ts +3 -0
  55. package/dist/types/eengine/src/pdf.d.ts +36 -0
  56. package/dist/types/eengine/src/providers.d.ts +38 -0
  57. package/dist/types/eengine/src/runner.d.ts +82 -0
  58. package/dist/types/eengine/src/stores.d.ts +125 -0
  59. package/dist/types/eengine/src/synced.d.ts +59 -0
  60. package/dist/types/eengine/src/tools.d.ts +79 -0
  61. package/dist/types/eengine/src/web/index.d.ts +20 -0
  62. package/dist/types/eengine/src/web/mcp.d.ts +27 -0
  63. package/dist/types/eengine/src/web/platform.d.ts +91 -0
  64. package/dist/types/eengine/src/web/stores.d.ts +125 -0
  65. package/dist/types/emcp/fixtures/automations.json +521 -0
  66. package/dist/types/emcp/fixtures/hub.json +498 -0
  67. package/dist/types/emcp/src/access.d.ts +55 -0
  68. package/dist/types/emcp/src/audit.d.ts +22 -0
  69. package/dist/types/emcp/src/automations.d.ts +111 -0
  70. package/dist/types/emcp/src/exposes.d.ts +242 -0
  71. package/dist/types/emcp/src/hub.d.ts +63 -0
  72. package/dist/types/emcp/src/index.d.ts +7 -0
  73. package/dist/types/emcp/src/pure.d.ts +11 -0
  74. package/dist/types/emcp/src/upstream.d.ts +288 -0
  75. package/dist/types/emcp/src/vault.d.ts +34 -0
  76. package/dist/web/index.js +791 -0
  77. package/dist/web/index.js.map +13 -0
  78. package/fixtures/bundle.json +1165 -0
  79. package/fixtures/capabilities.json +434 -0
  80. package/fixtures/consent.json +146 -0
  81. package/fixtures/lifecycle.json +125 -0
  82. package/fixtures/outbox.json +22 -0
  83. package/fixtures/providers.json +55 -0
  84. package/fixtures/runner.json +2167 -0
  85. package/package.json +92 -4
@@ -0,0 +1,2301 @@
1
+ import {
2
+ scopeDef2
3
+ } from "./index-tvsykgcv.js";
4
+
5
+ // packages/eengine/src/runner.ts
6
+ import { navScreens, packageScreens } from "@exponential_app/eui/host";
7
+ // packages/eengine/fixtures/runner.json
8
+ var runner_default = {
9
+ $comment: "E6 (epic VAPP-259; #VAPP-266 desktop, #VAPP-267 Android, #VAPP-268 iOS, #VAPP-269 web; docs/spec/apps.md §13.3): THE RUNNER, the prebuilt shell an exported app runs in, one per platform (macOS, Windows, Linux: gpui + eui-gpui + eengine; Android: Compose + eui-compose + eengine-ffi; iOS: SwiftUI + ExponentialUI + eengine-ffi; web: a static PWA on the TS twin + the React renderer). No app code is compiled per app (iOS links only the declared capability modules). A runner has NO CHROME OF ITS OWN: everything it paints is a genUI surface made of existing components, built here once (TS reference `runner.ts`, Rust `eengine::runner`, Swift/Kotlin over `eengine-ffi`), so the look is the same on every platform. NO OTA, NO UPDATE CHANNEL, NO TELEMETRY, NO ACCOUNT: a new package version is a rebundle; diagnostics are compiled out; the identity is `local` (providers.json). THE BUNDLE (`bundle`, the prebuilt-shell contract E7's `exponential bundle` fills): one directory `bundle.dir` holding `bundle.files.package` (the package FILE, signed: `{package, author, signature}`; a bare package runs unsigned) and `bundle.files.config` (`runner.json`), at `bundle.locations[<platform>]`; E7 copies the prebuilt runner, writes that directory, the icons and the platform manifest (Info.plist purpose strings, AndroidManifest permissions, macOS entitlements, the web manifest) from the package's capability manifest, then signs. The runner never reads the network for its package. CONFIG (`configCases`, `runnerConfig(json)` -> `{config, issues}`): `config.keys` in order, each a string; `required` ones missing = `missing`; a value failing its `pattern`, `values` or `maxLength` (non-blank) = `invalid`; an absent optional key = its `default` (`network` defaults to `off`: nothing leaves the device unless the bundle says so); extra keys sorted: one of `config.forbidden` = `forbidden` (no update feed, telemetry, crash reports or account can be configured into a runner), else `unknown`; a non-object = `[{key: \"\", code: notAnObject}]`; `config` is null whenever there is an issue. The runner refuses to start on any issue (the `config` failure). STORAGE (`storage`): per app id under the platform's app-data directory: the records in `SqliteRecordStore` (`storage.records`), files in `FsBlobStore` (`storage.files`), the Vault in the platform keystore (Keychain, Android Keystore-sealed preferences, libsecret / Credential Manager, the web: an IndexedDB store), the device uuid of the `local` identity under the Vault key `storage.deviceKey` (created once). The web keeps records in SQLite-WASM on OPFS and falls back to IndexedDB, files in OPFS. THE SHELL (`shellCases`, `runnerPackage(pkg, formFactor)` -> the package the runner's PAINTER installs; the Engine keeps the original package, its signature and grants): a package without `screens` gets one: its first template, titled with the package name. Every screen's template is wrapped in the shell when the package has two or more NAVIGATION screens (no `nav: false`) or declares an `ask` capability scope (`permissions`); otherwise its root sits alone in a full-height Box that scrolls (`overflowY: auto`), so a long single screen scrolls too. The template's `root` becomes `runner_page`; the shell owns every component id starting `runner_` and the data key `runner`. `wide` (desktop, the web at `md` and up, tablets) = a full-height horizontal Stack (align `stretch`) holding a `Sidebar` (`shell.sidebarWidth`, `collapseBelow: never`) whose `sidebar` slot is a Stack (padded `$spacing.sm`) of the package name as a `Heading` (`shell.titleLevel`), the navigation as a List of compact Rows (title, icon, `selected` on the open screen, a press = the event `runner.navigate {screen}`) and, when `permissions`, a Row `copy.permissions` with `shell.permissionsIcon` (event `runner.permissions`); the page sits in a growing Box that scrolls (`overflowY: auto`). `compact` (phones, the web below `md`) = a full-height Stack: the page in a growing Box that scrolls (`overflowY: auto`), a Separator, then a horizontal bar (padded `$spacing.sm`, end-justified) of `Tabs` in a growing column Box (`fill`, value bound to `/runner/screen`, change = `runner.navigate` with the bound value; the template's data gains `runner: {screen}`) and, when `permissions`, an icon-only ghost Button (event `runner.permissions`). The form factor follows the window: a host re-installs and re-applies when it crosses `md`. SURFACES: `app` (the open screen: genUI's `templateMessages` of the wrapped template with `{params}`; the host lays it out at the window's height, so the shell fills the window and the page scrolls inside it, never the window), `consent` (consent.json's ask sheet), `permissions` (consent.json's manage sheet), `toast` (`runnerToast(message)` = one `Toast` of type error: the refused call's reason, a broker reason code read as `copy.refusals[code]` (`toastCases`), any other message as it is, lifecycle.json's `copy.refused` for a refused link; it dismisses itself), and in place of `app` when the bundle cannot load `runnerFailure(kind)` (`failureCases`) = one `EmptyState` (`shell.failureIcon`, `copy.failed.title`, `copy.failed[kind]`; no action): `missing` (no package file), `config` (runner.json has issues), `signature` (the signature does not verify), `invalid` (genUI refuses the package), `busy` (the web only: another tab of the same origin runs the app; one tab holds the instance and its stores at a time, a Web Lock, and a second tab waits on that lock and opens the app when it frees, never on a second store). EVENTS the runner handles itself: `runner.navigate {screen}` = the Engine's `app.navigate`; `runner.permissions` = paint `permissions`; `consent.toggle {scope}` = the grant write through the ConsentUi with the gesture (granted -> revoke, else granted), then repaint `permissions`; `consent.answer {answer}` = the pending call's `answer_consent`, then close `consent`. Every other function call of the app goes to the Engine instance (`records.*`, `app.navigate`, capabilities through the broker); a refused one paints `toast`. VAPP-271: app UI reads a capability call's answer as the result's `structuredContent` (what a package action's `results.<name>` binds); an `isError` result is the call's refusal (`{code: failed, message: <its text>}`, the toast). LIFECYCLE: the platform's window or activity events drive lifecycle.json (launch, foreground, background, terminate, deep links `<config id>://<screen>?<params>`, notification taps); the WindowHost `open` applies the screen to `app`; when a plain launch opens nothing (a package without screens) the runner opens the derived package's first screen.",
10
+ bundle: {
11
+ dir: "app",
12
+ files: {
13
+ package: "package.app.json",
14
+ config: "runner.json"
15
+ },
16
+ locations: {
17
+ macos: "<name>.app/Contents/Resources/app",
18
+ windows: "<install dir>/app (beside the .exe)",
19
+ linux: "<exe dir>/app, else <exe dir>/../share/<config id>/app (AppImage, .deb)",
20
+ android: "assets/app (the APK's assets)",
21
+ ios: "<name>.app/app (the bundle's resources)",
22
+ web: "<origin>/app/ (precached by the service worker)"
23
+ }
24
+ },
25
+ config: {
26
+ keys: {
27
+ id: {
28
+ required: true,
29
+ pattern: "^[a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)+$"
30
+ },
31
+ name: {
32
+ required: true,
33
+ maxLength: 64
34
+ },
35
+ version: {
36
+ required: true,
37
+ pattern: "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
38
+ },
39
+ network: {
40
+ values: [
41
+ "off",
42
+ "upstreamsOnly",
43
+ "any"
44
+ ],
45
+ default: "off"
46
+ }
47
+ },
48
+ forbidden: [
49
+ "account",
50
+ "analytics",
51
+ "crashReports",
52
+ "diagnostics",
53
+ "feed",
54
+ "server",
55
+ "telemetry",
56
+ "update",
57
+ "updateUrl",
58
+ "updates"
59
+ ]
60
+ },
61
+ storage: {
62
+ records: "records.sqlite",
63
+ files: "files",
64
+ deviceKey: "eengine/runner/device"
65
+ },
66
+ shell: {
67
+ sidebarWidth: 240,
68
+ titleLevel: "h3",
69
+ permissionsIcon: "ui-permission",
70
+ failureIcon: "ui-error"
71
+ },
72
+ copy: {
73
+ permissions: "Permissions",
74
+ failed: {
75
+ title: "This app can't open",
76
+ missing: "Its package is missing from the bundle.",
77
+ config: "Its runner.json has a problem.",
78
+ signature: "Its signature doesn't match its author.",
79
+ invalid: "Its package isn't a valid app.",
80
+ busy: "It's already open in another tab."
81
+ },
82
+ refusals: {
83
+ consentDenied: "You didn't allow that.",
84
+ consentDismissed: "You didn't allow that.",
85
+ consentNeeded: "Open the app to allow that.",
86
+ notDeclared: "This app didn't ask for that.",
87
+ originNotDeclared: "This app didn't ask to reach that address.",
88
+ unavailable: "That isn't available on this device.",
89
+ offline: "This app is offline.",
90
+ network: "This app can't reach the network."
91
+ }
92
+ },
93
+ configCases: [
94
+ {
95
+ name: "the minimal config: network defaults to off",
96
+ input: {
97
+ id: "at.acme.album",
98
+ name: "Album",
99
+ version: "1.0.0"
100
+ },
101
+ expected: {
102
+ config: {
103
+ id: "at.acme.album",
104
+ name: "Album",
105
+ version: "1.0.0",
106
+ network: "off"
107
+ },
108
+ issues: []
109
+ }
110
+ },
111
+ {
112
+ name: "every key",
113
+ input: {
114
+ id: "at.acme.album",
115
+ name: "Album",
116
+ version: "2.10.3",
117
+ network: "upstreamsOnly"
118
+ },
119
+ expected: {
120
+ config: {
121
+ id: "at.acme.album",
122
+ name: "Album",
123
+ version: "2.10.3",
124
+ network: "upstreamsOnly"
125
+ },
126
+ issues: []
127
+ }
128
+ },
129
+ {
130
+ name: "missing keys in key order",
131
+ input: {
132
+ network: "any"
133
+ },
134
+ expected: {
135
+ config: null,
136
+ issues: [
137
+ {
138
+ key: "id",
139
+ code: "missing"
140
+ },
141
+ {
142
+ key: "name",
143
+ code: "missing"
144
+ },
145
+ {
146
+ key: "version",
147
+ code: "missing"
148
+ }
149
+ ]
150
+ }
151
+ },
152
+ {
153
+ name: "invalid values",
154
+ input: {
155
+ id: "album",
156
+ name: " ",
157
+ version: "1.0",
158
+ network: "everything"
159
+ },
160
+ expected: {
161
+ config: null,
162
+ issues: [
163
+ {
164
+ key: "id",
165
+ code: "invalid"
166
+ },
167
+ {
168
+ key: "name",
169
+ code: "invalid"
170
+ },
171
+ {
172
+ key: "version",
173
+ code: "invalid"
174
+ },
175
+ {
176
+ key: "network",
177
+ code: "invalid"
178
+ }
179
+ ]
180
+ }
181
+ },
182
+ {
183
+ name: "an id segment starting with a digit",
184
+ input: {
185
+ id: "at.1acme",
186
+ name: "Album",
187
+ version: "1.0.0"
188
+ },
189
+ expected: {
190
+ config: null,
191
+ issues: [
192
+ {
193
+ key: "id",
194
+ code: "invalid"
195
+ }
196
+ ]
197
+ }
198
+ },
199
+ {
200
+ name: "a name past 64 characters",
201
+ input: {
202
+ id: "at.acme.album",
203
+ name: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
204
+ version: "1.0.0"
205
+ },
206
+ expected: {
207
+ config: null,
208
+ issues: [
209
+ {
210
+ key: "name",
211
+ code: "invalid"
212
+ }
213
+ ]
214
+ }
215
+ },
216
+ {
217
+ name: "no update feed, telemetry or account; other keys unknown (sorted)",
218
+ input: {
219
+ id: "at.acme.album",
220
+ name: "Album",
221
+ version: "1.0.0",
222
+ updates: "https://x",
223
+ telemetry: true,
224
+ theme: "dark",
225
+ account: "a@b"
226
+ },
227
+ expected: {
228
+ config: null,
229
+ issues: [
230
+ {
231
+ key: "account",
232
+ code: "forbidden"
233
+ },
234
+ {
235
+ key: "telemetry",
236
+ code: "forbidden"
237
+ },
238
+ {
239
+ key: "theme",
240
+ code: "unknown"
241
+ },
242
+ {
243
+ key: "updates",
244
+ code: "forbidden"
245
+ }
246
+ ]
247
+ }
248
+ },
249
+ {
250
+ name: "a version with a leading zero",
251
+ input: {
252
+ id: "at.acme.album",
253
+ name: "Album",
254
+ version: "01.0.0"
255
+ },
256
+ expected: {
257
+ config: null,
258
+ issues: [
259
+ {
260
+ key: "version",
261
+ code: "invalid"
262
+ }
263
+ ]
264
+ }
265
+ },
266
+ {
267
+ name: "not an object",
268
+ input: [
269
+ "at.acme.album"
270
+ ],
271
+ expected: {
272
+ config: null,
273
+ issues: [
274
+ {
275
+ key: "",
276
+ code: "notAnObject"
277
+ }
278
+ ]
279
+ }
280
+ }
281
+ ],
282
+ shellCases: [
283
+ {
284
+ name: "two navigation screens, wide: the Sidebar",
285
+ package: {
286
+ formatVersion: 6,
287
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
288
+ functions: [
289
+ "records_*"
290
+ ],
291
+ records: {
292
+ entry: {
293
+ schema: {
294
+ type: "object",
295
+ properties: {
296
+ text: {
297
+ type: "string"
298
+ }
299
+ }
300
+ }
301
+ }
302
+ },
303
+ id: "acme.album",
304
+ name: "Album",
305
+ version: "1.0.0",
306
+ templates: {
307
+ albums: {
308
+ data: {
309
+ note: ""
310
+ },
311
+ bindings: [
312
+ {
313
+ path: "/rows",
314
+ source: "records:entry?limit=50"
315
+ }
316
+ ],
317
+ components: [
318
+ {
319
+ id: "root",
320
+ component: "Page",
321
+ title: "Albums",
322
+ children: [
323
+ "body"
324
+ ]
325
+ },
326
+ {
327
+ id: "body",
328
+ component: "Text",
329
+ text: "Albums"
330
+ }
331
+ ]
332
+ },
333
+ pages: {
334
+ data: {
335
+ note: ""
336
+ },
337
+ bindings: [
338
+ {
339
+ path: "/rows",
340
+ source: "records:entry?limit=50"
341
+ }
342
+ ],
343
+ components: [
344
+ {
345
+ id: "root",
346
+ component: "Page",
347
+ title: "Pages",
348
+ children: [
349
+ "body"
350
+ ]
351
+ },
352
+ {
353
+ id: "body",
354
+ component: "Text",
355
+ text: "Pages"
356
+ }
357
+ ]
358
+ },
359
+ page: {
360
+ data: {
361
+ note: ""
362
+ },
363
+ bindings: [
364
+ {
365
+ path: "/rows",
366
+ source: "records:entry?limit=50"
367
+ }
368
+ ],
369
+ components: [
370
+ {
371
+ id: "root",
372
+ component: "Page",
373
+ title: "Page",
374
+ children: [
375
+ "body"
376
+ ]
377
+ },
378
+ {
379
+ id: "body",
380
+ component: "Text",
381
+ text: "Page"
382
+ }
383
+ ]
384
+ }
385
+ },
386
+ screens: [
387
+ {
388
+ template: "albums",
389
+ title: "Albums",
390
+ icon: "image"
391
+ },
392
+ {
393
+ template: "pages",
394
+ title: "Pages"
395
+ },
396
+ {
397
+ template: "page",
398
+ title: "Page",
399
+ nav: false
400
+ }
401
+ ]
402
+ },
403
+ formFactor: "wide",
404
+ expected: {
405
+ formatVersion: 6,
406
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
407
+ functions: [
408
+ "records_*"
409
+ ],
410
+ records: {
411
+ entry: {
412
+ schema: {
413
+ type: "object",
414
+ properties: {
415
+ text: {
416
+ type: "string"
417
+ }
418
+ }
419
+ }
420
+ }
421
+ },
422
+ id: "acme.album",
423
+ name: "Album",
424
+ version: "1.0.0",
425
+ templates: {
426
+ albums: {
427
+ data: {
428
+ note: ""
429
+ },
430
+ bindings: [
431
+ {
432
+ path: "/rows",
433
+ source: "records:entry?limit=50"
434
+ }
435
+ ],
436
+ components: [
437
+ {
438
+ id: "root",
439
+ component: "Stack",
440
+ direction: "horizontal",
441
+ gap: "none",
442
+ align: "stretch",
443
+ style: {
444
+ height: "100%"
445
+ },
446
+ children: [
447
+ "runner_sidebar"
448
+ ]
449
+ },
450
+ {
451
+ id: "runner_sidebar",
452
+ component: "Sidebar",
453
+ width: 240,
454
+ collapseBelow: "never",
455
+ children: [
456
+ "runner_scroll"
457
+ ],
458
+ slots: {
459
+ sidebar: "runner_nav"
460
+ }
461
+ },
462
+ {
463
+ id: "runner_scroll",
464
+ component: "Box",
465
+ style: {
466
+ display: "flex",
467
+ flexDirection: "column",
468
+ flexGrow: 1,
469
+ minHeight: 0,
470
+ overflowY: "auto"
471
+ },
472
+ children: [
473
+ "runner_page"
474
+ ]
475
+ },
476
+ {
477
+ id: "runner_nav",
478
+ component: "Stack",
479
+ gap: "sm",
480
+ style: {
481
+ padding: "$spacing.sm"
482
+ },
483
+ children: [
484
+ "runner_title",
485
+ "runner_screens"
486
+ ]
487
+ },
488
+ {
489
+ id: "runner_title",
490
+ component: "Heading",
491
+ text: "Album",
492
+ level: "h3"
493
+ },
494
+ {
495
+ id: "runner_screens",
496
+ component: "List",
497
+ children: [
498
+ "runner_screen_0",
499
+ "runner_screen_1"
500
+ ]
501
+ },
502
+ {
503
+ id: "runner_screen_0",
504
+ component: "Row",
505
+ title: "Albums",
506
+ icon: "image",
507
+ selected: true,
508
+ density: "compact",
509
+ pressable: true,
510
+ on: {
511
+ press: {
512
+ event: {
513
+ name: "runner.navigate",
514
+ context: {
515
+ screen: "albums"
516
+ }
517
+ }
518
+ }
519
+ }
520
+ },
521
+ {
522
+ id: "runner_screen_1",
523
+ component: "Row",
524
+ title: "Pages",
525
+ selected: false,
526
+ density: "compact",
527
+ pressable: true,
528
+ on: {
529
+ press: {
530
+ event: {
531
+ name: "runner.navigate",
532
+ context: {
533
+ screen: "pages"
534
+ }
535
+ }
536
+ }
537
+ }
538
+ },
539
+ {
540
+ id: "runner_page",
541
+ component: "Page",
542
+ title: "Albums",
543
+ children: [
544
+ "body"
545
+ ]
546
+ },
547
+ {
548
+ id: "body",
549
+ component: "Text",
550
+ text: "Albums"
551
+ }
552
+ ]
553
+ },
554
+ pages: {
555
+ data: {
556
+ note: ""
557
+ },
558
+ bindings: [
559
+ {
560
+ path: "/rows",
561
+ source: "records:entry?limit=50"
562
+ }
563
+ ],
564
+ components: [
565
+ {
566
+ id: "root",
567
+ component: "Stack",
568
+ direction: "horizontal",
569
+ gap: "none",
570
+ align: "stretch",
571
+ style: {
572
+ height: "100%"
573
+ },
574
+ children: [
575
+ "runner_sidebar"
576
+ ]
577
+ },
578
+ {
579
+ id: "runner_sidebar",
580
+ component: "Sidebar",
581
+ width: 240,
582
+ collapseBelow: "never",
583
+ children: [
584
+ "runner_scroll"
585
+ ],
586
+ slots: {
587
+ sidebar: "runner_nav"
588
+ }
589
+ },
590
+ {
591
+ id: "runner_scroll",
592
+ component: "Box",
593
+ style: {
594
+ display: "flex",
595
+ flexDirection: "column",
596
+ flexGrow: 1,
597
+ minHeight: 0,
598
+ overflowY: "auto"
599
+ },
600
+ children: [
601
+ "runner_page"
602
+ ]
603
+ },
604
+ {
605
+ id: "runner_nav",
606
+ component: "Stack",
607
+ gap: "sm",
608
+ style: {
609
+ padding: "$spacing.sm"
610
+ },
611
+ children: [
612
+ "runner_title",
613
+ "runner_screens"
614
+ ]
615
+ },
616
+ {
617
+ id: "runner_title",
618
+ component: "Heading",
619
+ text: "Album",
620
+ level: "h3"
621
+ },
622
+ {
623
+ id: "runner_screens",
624
+ component: "List",
625
+ children: [
626
+ "runner_screen_0",
627
+ "runner_screen_1"
628
+ ]
629
+ },
630
+ {
631
+ id: "runner_screen_0",
632
+ component: "Row",
633
+ title: "Albums",
634
+ icon: "image",
635
+ selected: false,
636
+ density: "compact",
637
+ pressable: true,
638
+ on: {
639
+ press: {
640
+ event: {
641
+ name: "runner.navigate",
642
+ context: {
643
+ screen: "albums"
644
+ }
645
+ }
646
+ }
647
+ }
648
+ },
649
+ {
650
+ id: "runner_screen_1",
651
+ component: "Row",
652
+ title: "Pages",
653
+ selected: true,
654
+ density: "compact",
655
+ pressable: true,
656
+ on: {
657
+ press: {
658
+ event: {
659
+ name: "runner.navigate",
660
+ context: {
661
+ screen: "pages"
662
+ }
663
+ }
664
+ }
665
+ }
666
+ },
667
+ {
668
+ id: "runner_page",
669
+ component: "Page",
670
+ title: "Pages",
671
+ children: [
672
+ "body"
673
+ ]
674
+ },
675
+ {
676
+ id: "body",
677
+ component: "Text",
678
+ text: "Pages"
679
+ }
680
+ ]
681
+ },
682
+ page: {
683
+ data: {
684
+ note: ""
685
+ },
686
+ bindings: [
687
+ {
688
+ path: "/rows",
689
+ source: "records:entry?limit=50"
690
+ }
691
+ ],
692
+ components: [
693
+ {
694
+ id: "root",
695
+ component: "Stack",
696
+ direction: "horizontal",
697
+ gap: "none",
698
+ align: "stretch",
699
+ style: {
700
+ height: "100%"
701
+ },
702
+ children: [
703
+ "runner_sidebar"
704
+ ]
705
+ },
706
+ {
707
+ id: "runner_sidebar",
708
+ component: "Sidebar",
709
+ width: 240,
710
+ collapseBelow: "never",
711
+ children: [
712
+ "runner_scroll"
713
+ ],
714
+ slots: {
715
+ sidebar: "runner_nav"
716
+ }
717
+ },
718
+ {
719
+ id: "runner_scroll",
720
+ component: "Box",
721
+ style: {
722
+ display: "flex",
723
+ flexDirection: "column",
724
+ flexGrow: 1,
725
+ minHeight: 0,
726
+ overflowY: "auto"
727
+ },
728
+ children: [
729
+ "runner_page"
730
+ ]
731
+ },
732
+ {
733
+ id: "runner_nav",
734
+ component: "Stack",
735
+ gap: "sm",
736
+ style: {
737
+ padding: "$spacing.sm"
738
+ },
739
+ children: [
740
+ "runner_title",
741
+ "runner_screens"
742
+ ]
743
+ },
744
+ {
745
+ id: "runner_title",
746
+ component: "Heading",
747
+ text: "Album",
748
+ level: "h3"
749
+ },
750
+ {
751
+ id: "runner_screens",
752
+ component: "List",
753
+ children: [
754
+ "runner_screen_0",
755
+ "runner_screen_1"
756
+ ]
757
+ },
758
+ {
759
+ id: "runner_screen_0",
760
+ component: "Row",
761
+ title: "Albums",
762
+ icon: "image",
763
+ selected: false,
764
+ density: "compact",
765
+ pressable: true,
766
+ on: {
767
+ press: {
768
+ event: {
769
+ name: "runner.navigate",
770
+ context: {
771
+ screen: "albums"
772
+ }
773
+ }
774
+ }
775
+ }
776
+ },
777
+ {
778
+ id: "runner_screen_1",
779
+ component: "Row",
780
+ title: "Pages",
781
+ selected: false,
782
+ density: "compact",
783
+ pressable: true,
784
+ on: {
785
+ press: {
786
+ event: {
787
+ name: "runner.navigate",
788
+ context: {
789
+ screen: "pages"
790
+ }
791
+ }
792
+ }
793
+ }
794
+ },
795
+ {
796
+ id: "runner_page",
797
+ component: "Page",
798
+ title: "Page",
799
+ children: [
800
+ "body"
801
+ ]
802
+ },
803
+ {
804
+ id: "body",
805
+ component: "Text",
806
+ text: "Page"
807
+ }
808
+ ]
809
+ }
810
+ },
811
+ screens: [
812
+ {
813
+ template: "albums",
814
+ title: "Albums",
815
+ icon: "image"
816
+ },
817
+ {
818
+ template: "pages",
819
+ title: "Pages"
820
+ },
821
+ {
822
+ template: "page",
823
+ title: "Page",
824
+ nav: false
825
+ }
826
+ ]
827
+ }
828
+ },
829
+ {
830
+ name: "two navigation screens, compact: the Tabs bar",
831
+ package: {
832
+ formatVersion: 6,
833
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
834
+ functions: [
835
+ "records_*"
836
+ ],
837
+ records: {
838
+ entry: {
839
+ schema: {
840
+ type: "object",
841
+ properties: {
842
+ text: {
843
+ type: "string"
844
+ }
845
+ }
846
+ }
847
+ }
848
+ },
849
+ id: "acme.album",
850
+ name: "Album",
851
+ version: "1.0.0",
852
+ templates: {
853
+ albums: {
854
+ data: {
855
+ note: ""
856
+ },
857
+ bindings: [
858
+ {
859
+ path: "/rows",
860
+ source: "records:entry?limit=50"
861
+ }
862
+ ],
863
+ components: [
864
+ {
865
+ id: "root",
866
+ component: "Page",
867
+ title: "Albums",
868
+ children: [
869
+ "body"
870
+ ]
871
+ },
872
+ {
873
+ id: "body",
874
+ component: "Text",
875
+ text: "Albums"
876
+ }
877
+ ]
878
+ },
879
+ pages: {
880
+ data: {
881
+ note: ""
882
+ },
883
+ bindings: [
884
+ {
885
+ path: "/rows",
886
+ source: "records:entry?limit=50"
887
+ }
888
+ ],
889
+ components: [
890
+ {
891
+ id: "root",
892
+ component: "Page",
893
+ title: "Pages",
894
+ children: [
895
+ "body"
896
+ ]
897
+ },
898
+ {
899
+ id: "body",
900
+ component: "Text",
901
+ text: "Pages"
902
+ }
903
+ ]
904
+ },
905
+ page: {
906
+ data: {
907
+ note: ""
908
+ },
909
+ bindings: [
910
+ {
911
+ path: "/rows",
912
+ source: "records:entry?limit=50"
913
+ }
914
+ ],
915
+ components: [
916
+ {
917
+ id: "root",
918
+ component: "Page",
919
+ title: "Page",
920
+ children: [
921
+ "body"
922
+ ]
923
+ },
924
+ {
925
+ id: "body",
926
+ component: "Text",
927
+ text: "Page"
928
+ }
929
+ ]
930
+ }
931
+ },
932
+ screens: [
933
+ {
934
+ template: "albums",
935
+ title: "Albums",
936
+ icon: "image"
937
+ },
938
+ {
939
+ template: "pages",
940
+ title: "Pages"
941
+ },
942
+ {
943
+ template: "page",
944
+ title: "Page",
945
+ nav: false
946
+ }
947
+ ]
948
+ },
949
+ formFactor: "compact",
950
+ expected: {
951
+ formatVersion: 6,
952
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
953
+ functions: [
954
+ "records_*"
955
+ ],
956
+ records: {
957
+ entry: {
958
+ schema: {
959
+ type: "object",
960
+ properties: {
961
+ text: {
962
+ type: "string"
963
+ }
964
+ }
965
+ }
966
+ }
967
+ },
968
+ id: "acme.album",
969
+ name: "Album",
970
+ version: "1.0.0",
971
+ templates: {
972
+ albums: {
973
+ data: {
974
+ note: "",
975
+ runner: {
976
+ screen: "albums"
977
+ }
978
+ },
979
+ bindings: [
980
+ {
981
+ path: "/rows",
982
+ source: "records:entry?limit=50"
983
+ }
984
+ ],
985
+ components: [
986
+ {
987
+ id: "root",
988
+ component: "Stack",
989
+ gap: "none",
990
+ style: {
991
+ height: "100%"
992
+ },
993
+ children: [
994
+ "runner_main",
995
+ "runner_separator",
996
+ "runner_bar"
997
+ ]
998
+ },
999
+ {
1000
+ id: "runner_main",
1001
+ component: "Box",
1002
+ style: {
1003
+ display: "flex",
1004
+ flexDirection: "column",
1005
+ flexGrow: 1,
1006
+ minHeight: 0,
1007
+ overflowY: "auto"
1008
+ },
1009
+ children: [
1010
+ "runner_page"
1011
+ ]
1012
+ },
1013
+ {
1014
+ id: "runner_separator",
1015
+ component: "Separator"
1016
+ },
1017
+ {
1018
+ id: "runner_bar",
1019
+ component: "Stack",
1020
+ direction: "horizontal",
1021
+ gap: "sm",
1022
+ align: "center",
1023
+ justify: "end",
1024
+ style: {
1025
+ padding: "$spacing.sm"
1026
+ },
1027
+ children: [
1028
+ "runner_tabs_box"
1029
+ ]
1030
+ },
1031
+ {
1032
+ id: "runner_tabs_box",
1033
+ component: "Box",
1034
+ style: {
1035
+ display: "flex",
1036
+ flexDirection: "column",
1037
+ flexGrow: 1,
1038
+ minWidth: 0
1039
+ },
1040
+ children: [
1041
+ "runner_tabs"
1042
+ ]
1043
+ },
1044
+ {
1045
+ id: "runner_tabs",
1046
+ component: "Tabs",
1047
+ tabs: [
1048
+ {
1049
+ label: "Albums",
1050
+ value: "albums",
1051
+ icon: "image"
1052
+ },
1053
+ {
1054
+ label: "Pages",
1055
+ value: "pages"
1056
+ }
1057
+ ],
1058
+ value: {
1059
+ "@path": "/runner/screen"
1060
+ },
1061
+ fill: true,
1062
+ on: {
1063
+ change: {
1064
+ event: {
1065
+ name: "runner.navigate",
1066
+ context: {
1067
+ screen: {
1068
+ "@path": "/runner/screen"
1069
+ }
1070
+ }
1071
+ }
1072
+ }
1073
+ }
1074
+ },
1075
+ {
1076
+ id: "runner_page",
1077
+ component: "Page",
1078
+ title: "Albums",
1079
+ children: [
1080
+ "body"
1081
+ ]
1082
+ },
1083
+ {
1084
+ id: "body",
1085
+ component: "Text",
1086
+ text: "Albums"
1087
+ }
1088
+ ]
1089
+ },
1090
+ pages: {
1091
+ data: {
1092
+ note: "",
1093
+ runner: {
1094
+ screen: "pages"
1095
+ }
1096
+ },
1097
+ bindings: [
1098
+ {
1099
+ path: "/rows",
1100
+ source: "records:entry?limit=50"
1101
+ }
1102
+ ],
1103
+ components: [
1104
+ {
1105
+ id: "root",
1106
+ component: "Stack",
1107
+ gap: "none",
1108
+ style: {
1109
+ height: "100%"
1110
+ },
1111
+ children: [
1112
+ "runner_main",
1113
+ "runner_separator",
1114
+ "runner_bar"
1115
+ ]
1116
+ },
1117
+ {
1118
+ id: "runner_main",
1119
+ component: "Box",
1120
+ style: {
1121
+ display: "flex",
1122
+ flexDirection: "column",
1123
+ flexGrow: 1,
1124
+ minHeight: 0,
1125
+ overflowY: "auto"
1126
+ },
1127
+ children: [
1128
+ "runner_page"
1129
+ ]
1130
+ },
1131
+ {
1132
+ id: "runner_separator",
1133
+ component: "Separator"
1134
+ },
1135
+ {
1136
+ id: "runner_bar",
1137
+ component: "Stack",
1138
+ direction: "horizontal",
1139
+ gap: "sm",
1140
+ align: "center",
1141
+ justify: "end",
1142
+ style: {
1143
+ padding: "$spacing.sm"
1144
+ },
1145
+ children: [
1146
+ "runner_tabs_box"
1147
+ ]
1148
+ },
1149
+ {
1150
+ id: "runner_tabs_box",
1151
+ component: "Box",
1152
+ style: {
1153
+ display: "flex",
1154
+ flexDirection: "column",
1155
+ flexGrow: 1,
1156
+ minWidth: 0
1157
+ },
1158
+ children: [
1159
+ "runner_tabs"
1160
+ ]
1161
+ },
1162
+ {
1163
+ id: "runner_tabs",
1164
+ component: "Tabs",
1165
+ tabs: [
1166
+ {
1167
+ label: "Albums",
1168
+ value: "albums",
1169
+ icon: "image"
1170
+ },
1171
+ {
1172
+ label: "Pages",
1173
+ value: "pages"
1174
+ }
1175
+ ],
1176
+ value: {
1177
+ "@path": "/runner/screen"
1178
+ },
1179
+ fill: true,
1180
+ on: {
1181
+ change: {
1182
+ event: {
1183
+ name: "runner.navigate",
1184
+ context: {
1185
+ screen: {
1186
+ "@path": "/runner/screen"
1187
+ }
1188
+ }
1189
+ }
1190
+ }
1191
+ }
1192
+ },
1193
+ {
1194
+ id: "runner_page",
1195
+ component: "Page",
1196
+ title: "Pages",
1197
+ children: [
1198
+ "body"
1199
+ ]
1200
+ },
1201
+ {
1202
+ id: "body",
1203
+ component: "Text",
1204
+ text: "Pages"
1205
+ }
1206
+ ]
1207
+ },
1208
+ page: {
1209
+ data: {
1210
+ note: "",
1211
+ runner: {
1212
+ screen: "page"
1213
+ }
1214
+ },
1215
+ bindings: [
1216
+ {
1217
+ path: "/rows",
1218
+ source: "records:entry?limit=50"
1219
+ }
1220
+ ],
1221
+ components: [
1222
+ {
1223
+ id: "root",
1224
+ component: "Stack",
1225
+ gap: "none",
1226
+ style: {
1227
+ height: "100%"
1228
+ },
1229
+ children: [
1230
+ "runner_main",
1231
+ "runner_separator",
1232
+ "runner_bar"
1233
+ ]
1234
+ },
1235
+ {
1236
+ id: "runner_main",
1237
+ component: "Box",
1238
+ style: {
1239
+ display: "flex",
1240
+ flexDirection: "column",
1241
+ flexGrow: 1,
1242
+ minHeight: 0,
1243
+ overflowY: "auto"
1244
+ },
1245
+ children: [
1246
+ "runner_page"
1247
+ ]
1248
+ },
1249
+ {
1250
+ id: "runner_separator",
1251
+ component: "Separator"
1252
+ },
1253
+ {
1254
+ id: "runner_bar",
1255
+ component: "Stack",
1256
+ direction: "horizontal",
1257
+ gap: "sm",
1258
+ align: "center",
1259
+ justify: "end",
1260
+ style: {
1261
+ padding: "$spacing.sm"
1262
+ },
1263
+ children: [
1264
+ "runner_tabs_box"
1265
+ ]
1266
+ },
1267
+ {
1268
+ id: "runner_tabs_box",
1269
+ component: "Box",
1270
+ style: {
1271
+ display: "flex",
1272
+ flexDirection: "column",
1273
+ flexGrow: 1,
1274
+ minWidth: 0
1275
+ },
1276
+ children: [
1277
+ "runner_tabs"
1278
+ ]
1279
+ },
1280
+ {
1281
+ id: "runner_tabs",
1282
+ component: "Tabs",
1283
+ tabs: [
1284
+ {
1285
+ label: "Albums",
1286
+ value: "albums",
1287
+ icon: "image"
1288
+ },
1289
+ {
1290
+ label: "Pages",
1291
+ value: "pages"
1292
+ }
1293
+ ],
1294
+ value: {
1295
+ "@path": "/runner/screen"
1296
+ },
1297
+ fill: true,
1298
+ on: {
1299
+ change: {
1300
+ event: {
1301
+ name: "runner.navigate",
1302
+ context: {
1303
+ screen: {
1304
+ "@path": "/runner/screen"
1305
+ }
1306
+ }
1307
+ }
1308
+ }
1309
+ }
1310
+ },
1311
+ {
1312
+ id: "runner_page",
1313
+ component: "Page",
1314
+ title: "Page",
1315
+ children: [
1316
+ "body"
1317
+ ]
1318
+ },
1319
+ {
1320
+ id: "body",
1321
+ component: "Text",
1322
+ text: "Page"
1323
+ }
1324
+ ]
1325
+ }
1326
+ },
1327
+ screens: [
1328
+ {
1329
+ template: "albums",
1330
+ title: "Albums",
1331
+ icon: "image"
1332
+ },
1333
+ {
1334
+ template: "pages",
1335
+ title: "Pages"
1336
+ },
1337
+ {
1338
+ template: "page",
1339
+ title: "Page",
1340
+ nav: false
1341
+ }
1342
+ ]
1343
+ }
1344
+ },
1345
+ {
1346
+ name: "one screen with an ask scope, wide: the title and Permissions",
1347
+ package: {
1348
+ formatVersion: 6,
1349
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
1350
+ functions: [
1351
+ "records_*"
1352
+ ],
1353
+ records: {
1354
+ entry: {
1355
+ schema: {
1356
+ type: "object",
1357
+ properties: {
1358
+ text: {
1359
+ type: "string"
1360
+ }
1361
+ }
1362
+ }
1363
+ }
1364
+ },
1365
+ id: "acme.notes",
1366
+ name: "Notes",
1367
+ version: "1.0.0",
1368
+ templates: {
1369
+ main: {
1370
+ data: {
1371
+ note: ""
1372
+ },
1373
+ bindings: [
1374
+ {
1375
+ path: "/rows",
1376
+ source: "records:entry?limit=50"
1377
+ }
1378
+ ],
1379
+ components: [
1380
+ {
1381
+ id: "root",
1382
+ component: "Page",
1383
+ title: "Notes",
1384
+ children: [
1385
+ "body"
1386
+ ]
1387
+ },
1388
+ {
1389
+ id: "body",
1390
+ component: "Text",
1391
+ text: "Notes"
1392
+ }
1393
+ ]
1394
+ }
1395
+ },
1396
+ screens: [
1397
+ {
1398
+ template: "main",
1399
+ title: "Notes"
1400
+ }
1401
+ ],
1402
+ capabilities: [
1403
+ {
1404
+ id: "photos.read",
1405
+ why: "To put photos on a page."
1406
+ },
1407
+ {
1408
+ id: "share.send",
1409
+ why: "To share a page."
1410
+ }
1411
+ ]
1412
+ },
1413
+ formFactor: "wide",
1414
+ expected: {
1415
+ formatVersion: 6,
1416
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
1417
+ functions: [
1418
+ "records_*"
1419
+ ],
1420
+ records: {
1421
+ entry: {
1422
+ schema: {
1423
+ type: "object",
1424
+ properties: {
1425
+ text: {
1426
+ type: "string"
1427
+ }
1428
+ }
1429
+ }
1430
+ }
1431
+ },
1432
+ id: "acme.notes",
1433
+ name: "Notes",
1434
+ version: "1.0.0",
1435
+ templates: {
1436
+ main: {
1437
+ data: {
1438
+ note: ""
1439
+ },
1440
+ bindings: [
1441
+ {
1442
+ path: "/rows",
1443
+ source: "records:entry?limit=50"
1444
+ }
1445
+ ],
1446
+ components: [
1447
+ {
1448
+ id: "root",
1449
+ component: "Stack",
1450
+ direction: "horizontal",
1451
+ gap: "none",
1452
+ align: "stretch",
1453
+ style: {
1454
+ height: "100%"
1455
+ },
1456
+ children: [
1457
+ "runner_sidebar"
1458
+ ]
1459
+ },
1460
+ {
1461
+ id: "runner_sidebar",
1462
+ component: "Sidebar",
1463
+ width: 240,
1464
+ collapseBelow: "never",
1465
+ children: [
1466
+ "runner_scroll"
1467
+ ],
1468
+ slots: {
1469
+ sidebar: "runner_nav"
1470
+ }
1471
+ },
1472
+ {
1473
+ id: "runner_scroll",
1474
+ component: "Box",
1475
+ style: {
1476
+ display: "flex",
1477
+ flexDirection: "column",
1478
+ flexGrow: 1,
1479
+ minHeight: 0,
1480
+ overflowY: "auto"
1481
+ },
1482
+ children: [
1483
+ "runner_page"
1484
+ ]
1485
+ },
1486
+ {
1487
+ id: "runner_nav",
1488
+ component: "Stack",
1489
+ gap: "sm",
1490
+ style: {
1491
+ padding: "$spacing.sm"
1492
+ },
1493
+ children: [
1494
+ "runner_title",
1495
+ "runner_permissions"
1496
+ ]
1497
+ },
1498
+ {
1499
+ id: "runner_title",
1500
+ component: "Heading",
1501
+ text: "Notes",
1502
+ level: "h3"
1503
+ },
1504
+ {
1505
+ id: "runner_permissions",
1506
+ component: "Row",
1507
+ title: "Permissions",
1508
+ icon: "ui-permission",
1509
+ density: "compact",
1510
+ pressable: true,
1511
+ on: {
1512
+ press: {
1513
+ event: {
1514
+ name: "runner.permissions"
1515
+ }
1516
+ }
1517
+ }
1518
+ },
1519
+ {
1520
+ id: "runner_page",
1521
+ component: "Page",
1522
+ title: "Notes",
1523
+ children: [
1524
+ "body"
1525
+ ]
1526
+ },
1527
+ {
1528
+ id: "body",
1529
+ component: "Text",
1530
+ text: "Notes"
1531
+ }
1532
+ ]
1533
+ }
1534
+ },
1535
+ screens: [
1536
+ {
1537
+ template: "main",
1538
+ title: "Notes"
1539
+ }
1540
+ ],
1541
+ capabilities: [
1542
+ {
1543
+ id: "photos.read",
1544
+ why: "To put photos on a page."
1545
+ },
1546
+ {
1547
+ id: "share.send",
1548
+ why: "To share a page."
1549
+ }
1550
+ ]
1551
+ }
1552
+ },
1553
+ {
1554
+ name: "one screen with an ask scope, compact: the Permissions button only",
1555
+ package: {
1556
+ formatVersion: 6,
1557
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
1558
+ functions: [
1559
+ "records_*"
1560
+ ],
1561
+ records: {
1562
+ entry: {
1563
+ schema: {
1564
+ type: "object",
1565
+ properties: {
1566
+ text: {
1567
+ type: "string"
1568
+ }
1569
+ }
1570
+ }
1571
+ }
1572
+ },
1573
+ id: "acme.notes",
1574
+ name: "Notes",
1575
+ version: "1.0.0",
1576
+ templates: {
1577
+ main: {
1578
+ data: {
1579
+ note: ""
1580
+ },
1581
+ bindings: [
1582
+ {
1583
+ path: "/rows",
1584
+ source: "records:entry?limit=50"
1585
+ }
1586
+ ],
1587
+ components: [
1588
+ {
1589
+ id: "root",
1590
+ component: "Page",
1591
+ title: "Notes",
1592
+ children: [
1593
+ "body"
1594
+ ]
1595
+ },
1596
+ {
1597
+ id: "body",
1598
+ component: "Text",
1599
+ text: "Notes"
1600
+ }
1601
+ ]
1602
+ }
1603
+ },
1604
+ screens: [
1605
+ {
1606
+ template: "main",
1607
+ title: "Notes"
1608
+ }
1609
+ ],
1610
+ capabilities: [
1611
+ {
1612
+ id: "photos.read",
1613
+ why: "To put photos on a page."
1614
+ },
1615
+ {
1616
+ id: "share.send",
1617
+ why: "To share a page."
1618
+ }
1619
+ ]
1620
+ },
1621
+ formFactor: "compact",
1622
+ expected: {
1623
+ formatVersion: 6,
1624
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
1625
+ functions: [
1626
+ "records_*"
1627
+ ],
1628
+ records: {
1629
+ entry: {
1630
+ schema: {
1631
+ type: "object",
1632
+ properties: {
1633
+ text: {
1634
+ type: "string"
1635
+ }
1636
+ }
1637
+ }
1638
+ }
1639
+ },
1640
+ id: "acme.notes",
1641
+ name: "Notes",
1642
+ version: "1.0.0",
1643
+ templates: {
1644
+ main: {
1645
+ data: {
1646
+ note: ""
1647
+ },
1648
+ bindings: [
1649
+ {
1650
+ path: "/rows",
1651
+ source: "records:entry?limit=50"
1652
+ }
1653
+ ],
1654
+ components: [
1655
+ {
1656
+ id: "root",
1657
+ component: "Stack",
1658
+ gap: "none",
1659
+ style: {
1660
+ height: "100%"
1661
+ },
1662
+ children: [
1663
+ "runner_main",
1664
+ "runner_separator",
1665
+ "runner_bar"
1666
+ ]
1667
+ },
1668
+ {
1669
+ id: "runner_main",
1670
+ component: "Box",
1671
+ style: {
1672
+ display: "flex",
1673
+ flexDirection: "column",
1674
+ flexGrow: 1,
1675
+ minHeight: 0,
1676
+ overflowY: "auto"
1677
+ },
1678
+ children: [
1679
+ "runner_page"
1680
+ ]
1681
+ },
1682
+ {
1683
+ id: "runner_separator",
1684
+ component: "Separator"
1685
+ },
1686
+ {
1687
+ id: "runner_bar",
1688
+ component: "Stack",
1689
+ direction: "horizontal",
1690
+ gap: "sm",
1691
+ align: "center",
1692
+ justify: "end",
1693
+ style: {
1694
+ padding: "$spacing.sm"
1695
+ },
1696
+ children: [
1697
+ "runner_permissions"
1698
+ ]
1699
+ },
1700
+ {
1701
+ id: "runner_permissions",
1702
+ component: "Button",
1703
+ label: "Permissions",
1704
+ icon: "ui-permission",
1705
+ size: "icon",
1706
+ variant: "ghost",
1707
+ on: {
1708
+ press: {
1709
+ event: {
1710
+ name: "runner.permissions"
1711
+ }
1712
+ }
1713
+ }
1714
+ },
1715
+ {
1716
+ id: "runner_page",
1717
+ component: "Page",
1718
+ title: "Notes",
1719
+ children: [
1720
+ "body"
1721
+ ]
1722
+ },
1723
+ {
1724
+ id: "body",
1725
+ component: "Text",
1726
+ text: "Notes"
1727
+ }
1728
+ ]
1729
+ }
1730
+ },
1731
+ screens: [
1732
+ {
1733
+ template: "main",
1734
+ title: "Notes"
1735
+ }
1736
+ ],
1737
+ capabilities: [
1738
+ {
1739
+ id: "photos.read",
1740
+ why: "To put photos on a page."
1741
+ },
1742
+ {
1743
+ id: "share.send",
1744
+ why: "To share a page."
1745
+ }
1746
+ ]
1747
+ }
1748
+ },
1749
+ {
1750
+ name: "one screen, picker scopes only: no shell",
1751
+ package: {
1752
+ formatVersion: 6,
1753
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
1754
+ functions: [
1755
+ "records_*"
1756
+ ],
1757
+ records: {
1758
+ entry: {
1759
+ schema: {
1760
+ type: "object",
1761
+ properties: {
1762
+ text: {
1763
+ type: "string"
1764
+ }
1765
+ }
1766
+ }
1767
+ }
1768
+ },
1769
+ id: "acme.pickers",
1770
+ name: "Pickers",
1771
+ version: "1.0.0",
1772
+ templates: {
1773
+ main: {
1774
+ data: {
1775
+ note: ""
1776
+ },
1777
+ bindings: [
1778
+ {
1779
+ path: "/rows",
1780
+ source: "records:entry?limit=50"
1781
+ }
1782
+ ],
1783
+ components: [
1784
+ {
1785
+ id: "root",
1786
+ component: "Page",
1787
+ title: "Main",
1788
+ children: [
1789
+ "body"
1790
+ ]
1791
+ },
1792
+ {
1793
+ id: "body",
1794
+ component: "Text",
1795
+ text: "Main"
1796
+ }
1797
+ ]
1798
+ }
1799
+ },
1800
+ screens: [
1801
+ {
1802
+ template: "main",
1803
+ title: "Main"
1804
+ }
1805
+ ],
1806
+ capabilities: [
1807
+ {
1808
+ id: "files.read",
1809
+ why: "To open a file."
1810
+ }
1811
+ ]
1812
+ },
1813
+ formFactor: "wide",
1814
+ expected: {
1815
+ formatVersion: 6,
1816
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
1817
+ functions: [
1818
+ "records_*"
1819
+ ],
1820
+ records: {
1821
+ entry: {
1822
+ schema: {
1823
+ type: "object",
1824
+ properties: {
1825
+ text: {
1826
+ type: "string"
1827
+ }
1828
+ }
1829
+ }
1830
+ }
1831
+ },
1832
+ id: "acme.pickers",
1833
+ name: "Pickers",
1834
+ version: "1.0.0",
1835
+ templates: {
1836
+ main: {
1837
+ data: {
1838
+ note: ""
1839
+ },
1840
+ bindings: [
1841
+ {
1842
+ path: "/rows",
1843
+ source: "records:entry?limit=50"
1844
+ }
1845
+ ],
1846
+ components: [
1847
+ {
1848
+ id: "root",
1849
+ component: "Box",
1850
+ style: {
1851
+ display: "flex",
1852
+ flexDirection: "column",
1853
+ height: "100%",
1854
+ overflowY: "auto"
1855
+ },
1856
+ children: [
1857
+ "runner_page"
1858
+ ]
1859
+ },
1860
+ {
1861
+ id: "runner_page",
1862
+ component: "Page",
1863
+ title: "Main",
1864
+ children: [
1865
+ "body"
1866
+ ]
1867
+ },
1868
+ {
1869
+ id: "body",
1870
+ component: "Text",
1871
+ text: "Main"
1872
+ }
1873
+ ]
1874
+ }
1875
+ },
1876
+ screens: [
1877
+ {
1878
+ template: "main",
1879
+ title: "Main"
1880
+ }
1881
+ ],
1882
+ capabilities: [
1883
+ {
1884
+ id: "files.read",
1885
+ why: "To open a file."
1886
+ }
1887
+ ]
1888
+ }
1889
+ },
1890
+ {
1891
+ name: "no screens: the first template becomes the one screen, no shell",
1892
+ package: {
1893
+ formatVersion: 6,
1894
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
1895
+ functions: [
1896
+ "records_*"
1897
+ ],
1898
+ records: {
1899
+ entry: {
1900
+ schema: {
1901
+ type: "object",
1902
+ properties: {
1903
+ text: {
1904
+ type: "string"
1905
+ }
1906
+ }
1907
+ }
1908
+ }
1909
+ },
1910
+ id: "acme.bare",
1911
+ name: "Bare",
1912
+ version: "1.0.0",
1913
+ templates: {
1914
+ start: {
1915
+ data: {
1916
+ note: ""
1917
+ },
1918
+ bindings: [
1919
+ {
1920
+ path: "/rows",
1921
+ source: "records:entry?limit=50"
1922
+ }
1923
+ ],
1924
+ components: [
1925
+ {
1926
+ id: "root",
1927
+ component: "Page",
1928
+ title: "Start",
1929
+ children: [
1930
+ "body"
1931
+ ]
1932
+ },
1933
+ {
1934
+ id: "body",
1935
+ component: "Text",
1936
+ text: "Start"
1937
+ }
1938
+ ]
1939
+ },
1940
+ other: {
1941
+ data: {
1942
+ note: ""
1943
+ },
1944
+ bindings: [
1945
+ {
1946
+ path: "/rows",
1947
+ source: "records:entry?limit=50"
1948
+ }
1949
+ ],
1950
+ components: [
1951
+ {
1952
+ id: "root",
1953
+ component: "Page",
1954
+ title: "Other",
1955
+ children: [
1956
+ "body"
1957
+ ]
1958
+ },
1959
+ {
1960
+ id: "body",
1961
+ component: "Text",
1962
+ text: "Other"
1963
+ }
1964
+ ]
1965
+ }
1966
+ }
1967
+ },
1968
+ formFactor: "compact",
1969
+ expected: {
1970
+ formatVersion: 6,
1971
+ catalogId: "https://ui.exponential.at/catalogs/core/v1",
1972
+ functions: [
1973
+ "records_*"
1974
+ ],
1975
+ records: {
1976
+ entry: {
1977
+ schema: {
1978
+ type: "object",
1979
+ properties: {
1980
+ text: {
1981
+ type: "string"
1982
+ }
1983
+ }
1984
+ }
1985
+ }
1986
+ },
1987
+ id: "acme.bare",
1988
+ name: "Bare",
1989
+ version: "1.0.0",
1990
+ templates: {
1991
+ start: {
1992
+ data: {
1993
+ note: ""
1994
+ },
1995
+ bindings: [
1996
+ {
1997
+ path: "/rows",
1998
+ source: "records:entry?limit=50"
1999
+ }
2000
+ ],
2001
+ components: [
2002
+ {
2003
+ id: "root",
2004
+ component: "Box",
2005
+ style: {
2006
+ display: "flex",
2007
+ flexDirection: "column",
2008
+ height: "100%",
2009
+ overflowY: "auto"
2010
+ },
2011
+ children: [
2012
+ "runner_page"
2013
+ ]
2014
+ },
2015
+ {
2016
+ id: "runner_page",
2017
+ component: "Page",
2018
+ title: "Start",
2019
+ children: [
2020
+ "body"
2021
+ ]
2022
+ },
2023
+ {
2024
+ id: "body",
2025
+ component: "Text",
2026
+ text: "Start"
2027
+ }
2028
+ ]
2029
+ },
2030
+ other: {
2031
+ data: {
2032
+ note: ""
2033
+ },
2034
+ bindings: [
2035
+ {
2036
+ path: "/rows",
2037
+ source: "records:entry?limit=50"
2038
+ }
2039
+ ],
2040
+ components: [
2041
+ {
2042
+ id: "root",
2043
+ component: "Page",
2044
+ title: "Other",
2045
+ children: [
2046
+ "body"
2047
+ ]
2048
+ },
2049
+ {
2050
+ id: "body",
2051
+ component: "Text",
2052
+ text: "Other"
2053
+ }
2054
+ ]
2055
+ }
2056
+ },
2057
+ screens: [
2058
+ {
2059
+ template: "start",
2060
+ title: "Bare"
2061
+ }
2062
+ ]
2063
+ }
2064
+ }
2065
+ ],
2066
+ failureCases: [
2067
+ {
2068
+ kind: "missing",
2069
+ expected: [
2070
+ {
2071
+ id: "root",
2072
+ component: "EmptyState",
2073
+ icon: "ui-error",
2074
+ title: "This app can't open",
2075
+ description: "Its package is missing from the bundle."
2076
+ }
2077
+ ]
2078
+ },
2079
+ {
2080
+ kind: "config",
2081
+ expected: [
2082
+ {
2083
+ id: "root",
2084
+ component: "EmptyState",
2085
+ icon: "ui-error",
2086
+ title: "This app can't open",
2087
+ description: "Its runner.json has a problem."
2088
+ }
2089
+ ]
2090
+ },
2091
+ {
2092
+ kind: "signature",
2093
+ expected: [
2094
+ {
2095
+ id: "root",
2096
+ component: "EmptyState",
2097
+ icon: "ui-error",
2098
+ title: "This app can't open",
2099
+ description: "Its signature doesn't match its author."
2100
+ }
2101
+ ]
2102
+ },
2103
+ {
2104
+ kind: "invalid",
2105
+ expected: [
2106
+ {
2107
+ id: "root",
2108
+ component: "EmptyState",
2109
+ icon: "ui-error",
2110
+ title: "This app can't open",
2111
+ description: "Its package isn't a valid app."
2112
+ }
2113
+ ]
2114
+ },
2115
+ {
2116
+ kind: "busy",
2117
+ expected: [
2118
+ {
2119
+ id: "root",
2120
+ component: "EmptyState",
2121
+ icon: "ui-error",
2122
+ title: "This app can't open",
2123
+ description: "It's already open in another tab."
2124
+ }
2125
+ ]
2126
+ }
2127
+ ],
2128
+ toastCases: [
2129
+ {
2130
+ message: "Couldn't open the link",
2131
+ expected: [
2132
+ {
2133
+ id: "root",
2134
+ component: "Toast",
2135
+ title: "Couldn't open the link",
2136
+ type: "error"
2137
+ }
2138
+ ]
2139
+ },
2140
+ {
2141
+ message: "consentDenied",
2142
+ expected: [
2143
+ {
2144
+ id: "root",
2145
+ component: "Toast",
2146
+ title: "You didn't allow that.",
2147
+ type: "error"
2148
+ }
2149
+ ]
2150
+ },
2151
+ {
2152
+ message: "originNotDeclared",
2153
+ expected: [
2154
+ {
2155
+ id: "root",
2156
+ component: "Toast",
2157
+ title: "This app didn't ask to reach that address.",
2158
+ type: "error"
2159
+ }
2160
+ ]
2161
+ },
2162
+ {
2163
+ message: "FORBIDDEN",
2164
+ expected: [
2165
+ {
2166
+ id: "root",
2167
+ component: "Toast",
2168
+ title: "FORBIDDEN",
2169
+ type: "error"
2170
+ }
2171
+ ]
2172
+ }
2173
+ ]
2174
+ };
2175
+
2176
+ // packages/eengine/src/runner.ts
2177
+ var RUNNER_COPY2 = runner_default.copy;
2178
+ var RUNNER_BUNDLE2 = runner_default.bundle;
2179
+ var RUNNER_SHELL2 = runner_default.shell;
2180
+ var CONFIG_KEYS = Object.keys(runner_default.config.keys);
2181
+ function runnerConfig2(input) {
2182
+ if (!input || typeof input !== `object` || Array.isArray(input))
2183
+ return { config: null, issues: [{ key: ``, code: `notAnObject` }] };
2184
+ const raw = input;
2185
+ const issues = [];
2186
+ const out = {};
2187
+ for (const key of CONFIG_KEYS) {
2188
+ const def = runner_default.config.keys[key];
2189
+ const value = raw[key];
2190
+ if (value === undefined) {
2191
+ if (def.required)
2192
+ issues.push({ key, code: `missing` });
2193
+ else
2194
+ out[key] = def.default;
2195
+ continue;
2196
+ }
2197
+ const ok = typeof value === `string` && (!def.pattern || new RegExp(def.pattern).test(value)) && (!def.maxLength || [...value].length <= def.maxLength && value.trim().length > 0) && (!def.values || def.values.includes(value));
2198
+ if (ok)
2199
+ out[key] = value;
2200
+ else
2201
+ issues.push({ key, code: `invalid` });
2202
+ }
2203
+ const extra = Object.keys(raw).filter((k) => !CONFIG_KEYS.includes(k)).sort();
2204
+ for (const key of extra)
2205
+ issues.push({ key, code: runner_default.config.forbidden.includes(key) ? `forbidden` : `unknown` });
2206
+ return { config: issues.length ? null : out, issues };
2207
+ }
2208
+ var manifestOf = (pkg) => Array.isArray(pkg.capabilities) ? pkg.capabilities : [];
2209
+ function runnerHasPermissions2(pkg) {
2210
+ return manifestOf(pkg).some((m) => {
2211
+ const def = scopeDef2(m.id);
2212
+ return !!def && !def.scope.hostOnly && def.scope.consent === `ask`;
2213
+ });
2214
+ }
2215
+ var event = (name, context) => ({ event: context ? { name, context } : { name } });
2216
+ function shellComponents(pkg, screen, components, formFactor) {
2217
+ const nav = navScreens(pkg);
2218
+ const permissions = runnerHasPermissions2(pkg);
2219
+ const tabs = nav.length >= 2;
2220
+ const page = components.map((c) => c.id === `root` ? { ...c, id: `runner_page` } : c);
2221
+ if (!tabs && !permissions)
2222
+ return { components: [{ id: `root`, component: `Box`, style: { display: `flex`, flexDirection: `column`, height: `100%`, overflowY: `auto` }, children: [`runner_page`] }, ...page], data: null };
2223
+ const S = runner_default.shell;
2224
+ if (formFactor === `wide`) {
2225
+ const nav_children = [`runner_title`, ...tabs ? [`runner_screens`] : [], ...permissions ? [`runner_permissions`] : []];
2226
+ const shell = [
2227
+ { id: `root`, component: `Stack`, direction: `horizontal`, gap: `none`, align: `stretch`, style: { height: `100%` }, children: [`runner_sidebar`] },
2228
+ { id: `runner_sidebar`, component: `Sidebar`, width: S.sidebarWidth, collapseBelow: `never`, children: [`runner_scroll`], slots: { sidebar: `runner_nav` } },
2229
+ { id: `runner_scroll`, component: `Box`, style: { display: `flex`, flexDirection: `column`, flexGrow: 1, minHeight: 0, overflowY: `auto` }, children: [`runner_page`] },
2230
+ { id: `runner_nav`, component: `Stack`, gap: `sm`, style: { padding: `$spacing.sm` }, children: nav_children },
2231
+ { id: `runner_title`, component: `Heading`, text: typeof pkg.name === `string` ? pkg.name : ``, level: S.titleLevel }
2232
+ ];
2233
+ if (tabs)
2234
+ shell.push({ id: `runner_screens`, component: `List`, children: nav.map((_, i) => `runner_screen_${i}`) }, ...nav.map((s, i) => ({
2235
+ id: `runner_screen_${i}`,
2236
+ component: `Row`,
2237
+ title: s.title ?? s.template,
2238
+ ...s.icon ? { icon: s.icon } : {},
2239
+ selected: s.template === screen,
2240
+ density: `compact`,
2241
+ pressable: true,
2242
+ on: { press: event(`runner.navigate`, { screen: s.template }) }
2243
+ })));
2244
+ if (permissions)
2245
+ shell.push({ id: `runner_permissions`, component: `Row`, title: runner_default.copy.permissions, icon: S.permissionsIcon, density: `compact`, pressable: true, on: { press: event(`runner.permissions`) } });
2246
+ return { components: [...shell, ...page], data: null };
2247
+ }
2248
+ const bar = [...tabs ? [`runner_tabs_box`] : [], ...permissions ? [`runner_permissions`] : []];
2249
+ const shell = [
2250
+ { id: `root`, component: `Stack`, gap: `none`, style: { height: `100%` }, children: [`runner_main`, `runner_separator`, `runner_bar`] },
2251
+ { id: `runner_main`, component: `Box`, style: { display: `flex`, flexDirection: `column`, flexGrow: 1, minHeight: 0, overflowY: `auto` }, children: [`runner_page`] },
2252
+ { id: `runner_separator`, component: `Separator` },
2253
+ { id: `runner_bar`, component: `Stack`, direction: `horizontal`, gap: `sm`, align: `center`, justify: `end`, style: { padding: `$spacing.sm` }, children: bar }
2254
+ ];
2255
+ if (tabs)
2256
+ shell.push({ id: `runner_tabs_box`, component: `Box`, style: { display: `flex`, flexDirection: `column`, flexGrow: 1, minWidth: 0 }, children: [`runner_tabs`] }, {
2257
+ id: `runner_tabs`,
2258
+ component: `Tabs`,
2259
+ tabs: nav.map((s) => ({ label: s.title ?? s.template, value: s.template, ...s.icon ? { icon: s.icon } : {} })),
2260
+ value: { "@path": `/runner/screen` },
2261
+ fill: true,
2262
+ on: { change: event(`runner.navigate`, { screen: { "@path": `/runner/screen` } }) }
2263
+ });
2264
+ if (permissions)
2265
+ shell.push({ id: `runner_permissions`, component: `Button`, label: runner_default.copy.permissions, icon: S.permissionsIcon, size: `icon`, variant: `ghost`, on: { press: event(`runner.permissions`) } });
2266
+ return { components: [...shell, ...page], data: tabs ? { runner: { screen } } : null };
2267
+ }
2268
+ function runnerPackage2(pkg, formFactor) {
2269
+ const templates = pkg.templates && typeof pkg.templates === `object` ? pkg.templates : {};
2270
+ let out = pkg;
2271
+ if (packageScreens(pkg).length === 0) {
2272
+ const first = Object.keys(templates)[0];
2273
+ if (!first)
2274
+ return pkg;
2275
+ out = { ...pkg, screens: [{ template: first, title: typeof pkg.name === `string` ? pkg.name : first }] };
2276
+ }
2277
+ const wrapped = { ...templates };
2278
+ for (const s of packageScreens(out)) {
2279
+ const t = templates[s.template];
2280
+ if (!t || !Array.isArray(t.components))
2281
+ continue;
2282
+ const { components, data } = shellComponents(out, s.template, t.components, formFactor);
2283
+ if (components === t.components)
2284
+ continue;
2285
+ const base = t.data && typeof t.data === `object` && !Array.isArray(t.data) ? t.data : {};
2286
+ wrapped[s.template] = { ...t, components, ...data ? { data: { ...base, ...data } } : {} };
2287
+ }
2288
+ return { ...out, templates: wrapped };
2289
+ }
2290
+ function runnerFailure2(kind) {
2291
+ return [{ id: `root`, component: `EmptyState`, icon: runner_default.shell.failureIcon, title: runner_default.copy.failed.title, description: runner_default.copy.failed[kind] }];
2292
+ }
2293
+ function runnerToast2(message) {
2294
+ const refusals = runner_default.copy.refusals;
2295
+ return [{ id: `root`, component: `Toast`, title: Object.hasOwn(refusals, message) ? refusals[message] : message, type: `error` }];
2296
+ }
2297
+
2298
+ export { RUNNER_COPY2, RUNNER_BUNDLE2, RUNNER_SHELL2, runnerConfig2, runnerHasPermissions2, runnerPackage2, runnerFailure2, runnerToast2 };
2299
+
2300
+ //# debugId=8614238A2FCD206064756E2164756E21
2301
+ //# sourceMappingURL=index-q4zws02e.js.map