@cloudcrane/app-spec 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,6 +28,8 @@ export declare const CodedComponent: z.ZodEffects<z.ZodEffects<z.ZodObject<{
28
28
  kind: z.ZodLiteral<"coded">;
29
29
  /** game: a canvas, a loop and touch, the code a script; page: a whole HTML page, drawn full screen (what an app written as HTML becomes). */
30
30
  kit: z.ZodEnum<["game", "page"]>;
31
+ /** Optional: the native widget or screen it stands in for, like "CupertinoSlidingSegmentedControl", so `blackbox coverage` can say which the catalog lacks. */
32
+ replaces: z.ZodOptional<z.ZodString>;
31
33
  inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<["number", "string", "boolean", "list", "map"]>>>;
32
34
  events: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodEnum<["number", "string", "boolean", "list", "map"]>>>>;
33
35
  code: z.ZodString;
@@ -37,10 +39,12 @@ export declare const CodedComponent: z.ZodEffects<z.ZodEffects<z.ZodObject<{
37
39
  kit: "game" | "page";
38
40
  inputs: Record<string, "string" | "number" | "boolean" | "map" | "list">;
39
41
  events: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">>;
42
+ replaces?: string | undefined;
40
43
  }, {
41
44
  code: string;
42
45
  kind: "coded";
43
46
  kit: "game" | "page";
47
+ replaces?: string | undefined;
44
48
  inputs?: Record<string, "string" | "number" | "boolean" | "map" | "list"> | undefined;
45
49
  events?: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">> | undefined;
46
50
  }>, {
@@ -49,10 +53,12 @@ export declare const CodedComponent: z.ZodEffects<z.ZodEffects<z.ZodObject<{
49
53
  kit: "game" | "page";
50
54
  inputs: Record<string, "string" | "number" | "boolean" | "map" | "list">;
51
55
  events: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">>;
56
+ replaces?: string | undefined;
52
57
  }, {
53
58
  code: string;
54
59
  kind: "coded";
55
60
  kit: "game" | "page";
61
+ replaces?: string | undefined;
56
62
  inputs?: Record<string, "string" | "number" | "boolean" | "map" | "list"> | undefined;
57
63
  events?: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">> | undefined;
58
64
  }>, {
@@ -61,13 +67,17 @@ export declare const CodedComponent: z.ZodEffects<z.ZodEffects<z.ZodObject<{
61
67
  kit: "game" | "page";
62
68
  inputs: Record<string, "string" | "number" | "boolean" | "map" | "list">;
63
69
  events: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">>;
70
+ replaces?: string | undefined;
64
71
  }, {
65
72
  code: string;
66
73
  kind: "coded";
67
74
  kit: "game" | "page";
75
+ replaces?: string | undefined;
68
76
  inputs?: Record<string, "string" | "number" | "boolean" | "map" | "list"> | undefined;
69
77
  events?: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">> | undefined;
70
78
  }>;
79
+ /** The kinds of thing a ported app may not do that its original did. */
80
+ export declare const GAP_KINDS: readonly ["background", "widget", "notifications", "purchases", "accounts", "sync", "device", "other"];
71
81
  export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
72
82
  v: z.ZodLiteral<1>;
73
83
  manifest: z.ZodObject<{
@@ -167,6 +177,11 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
167
177
  to: number;
168
178
  cel: string;
169
179
  }>, "many">;
180
+ /**
181
+ * What it keeps: the saved fields that are personal (health, money, people's names), as pointers into the saved
182
+ * data, like /days or /people. They stay on the phone; CloudCrane never puts them in a remix, screenshot or report.
183
+ */
184
+ personal: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
170
185
  }, "strict", z.ZodTypeAny, {
171
186
  version: number;
172
187
  key: string;
@@ -182,6 +197,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
182
197
  cel: string;
183
198
  }[];
184
199
  initial?: unknown;
200
+ personal?: string[] | undefined;
185
201
  }, {
186
202
  version: number;
187
203
  key: string;
@@ -197,6 +213,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
197
213
  path: string;
198
214
  source: string;
199
215
  }[] | undefined;
216
+ personal?: string[] | undefined;
200
217
  }>;
201
218
  logic: z.ZodObject<{
202
219
  consts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodNumber, z.ZodString, z.ZodBoolean]>>>;
@@ -213,18 +230,22 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
213
230
  /** Values the screens read, kept up to date: a data-model pointer and the CEL that computes it. */
214
231
  computed: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
215
232
  /** Examples each function must reproduce: recorded from the source's own tests, or given. */
233
+ /**
234
+ * Examples each function must reproduce: `recorded` from the module's own tests, `app` from calls recorded while
235
+ * the original app ran, `hint` written by hand.
236
+ */
216
237
  vectors: z.ZodDefault<z.ZodArray<z.ZodObject<{
217
238
  fn: z.ZodString;
218
239
  in: z.ZodArray<z.ZodUnknown, "many">;
219
240
  out: z.ZodUnknown;
220
- from: z.ZodEnum<["recorded", "hint"]>;
241
+ from: z.ZodEnum<["recorded", "app", "hint"]>;
221
242
  }, "strict", z.ZodTypeAny, {
222
- from: "recorded" | "hint";
243
+ from: "recorded" | "app" | "hint";
223
244
  fn: string;
224
245
  in: unknown[];
225
246
  out?: unknown;
226
247
  }, {
227
- from: "recorded" | "hint";
248
+ from: "recorded" | "app" | "hint";
228
249
  fn: string;
229
250
  in: unknown[];
230
251
  out?: unknown;
@@ -237,7 +258,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
237
258
  }>;
238
259
  computed: Record<string, string>;
239
260
  vectors: {
240
- from: "recorded" | "hint";
261
+ from: "recorded" | "app" | "hint";
241
262
  fn: string;
242
263
  in: unknown[];
243
264
  out?: unknown;
@@ -250,21 +271,28 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
250
271
  }> | undefined;
251
272
  computed?: Record<string, string> | undefined;
252
273
  vectors?: {
253
- from: "recorded" | "hint";
274
+ from: "recorded" | "app" | "hint";
254
275
  fn: string;
255
276
  in: unknown[];
256
277
  out?: unknown;
257
278
  }[] | undefined;
258
279
  }>;
280
+ /**
281
+ * What it can reach: each site, why, and what it's sent, in the person's words ("the city you typed"). Shown to the
282
+ * person before the app opens; a site the code names but this doesn't list is refused.
283
+ */
259
284
  network: z.ZodDefault<z.ZodArray<z.ZodObject<{
260
285
  site: z.ZodString;
261
286
  why: z.ZodString;
287
+ sends: z.ZodString;
262
288
  }, "strict", z.ZodTypeAny, {
263
289
  site: string;
264
290
  why: string;
291
+ sends: string;
265
292
  }, {
266
293
  site: string;
267
294
  why: string;
295
+ sends: string;
268
296
  }>, "many">>;
269
297
  /** The device features the app uses, by CloudCrane's names, each once with why; anything not listed is blocked. */
270
298
  uses: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -277,6 +305,59 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
277
305
  why: string;
278
306
  what: "camera" | "microphone" | "location" | "motion" | "screen-awake" | "gamepad" | "voice" | "contacts" | "nfc";
279
307
  }>, "many">>;
308
+ /**
309
+ * What it does: each screen in words, and what it shows when there's nothing yet, while it loads, offline, and when
310
+ * something fails. Optional; without it the blueprint describes the one surface from its words.
311
+ */
312
+ screens: z.ZodOptional<z.ZodArray<z.ZodObject<{
313
+ id: z.ZodString;
314
+ says: z.ZodString;
315
+ states: z.ZodOptional<z.ZodObject<{
316
+ empty: z.ZodOptional<z.ZodString>;
317
+ loading: z.ZodOptional<z.ZodString>;
318
+ offline: z.ZodOptional<z.ZodString>;
319
+ error: z.ZodOptional<z.ZodString>;
320
+ }, "strict", z.ZodTypeAny, {
321
+ error?: string | undefined;
322
+ empty?: string | undefined;
323
+ loading?: string | undefined;
324
+ offline?: string | undefined;
325
+ }, {
326
+ error?: string | undefined;
327
+ empty?: string | undefined;
328
+ loading?: string | undefined;
329
+ offline?: string | undefined;
330
+ }>>;
331
+ }, "strict", z.ZodTypeAny, {
332
+ id: string;
333
+ says: string;
334
+ states?: {
335
+ error?: string | undefined;
336
+ empty?: string | undefined;
337
+ loading?: string | undefined;
338
+ offline?: string | undefined;
339
+ } | undefined;
340
+ }, {
341
+ id: string;
342
+ says: string;
343
+ states?: {
344
+ error?: string | undefined;
345
+ empty?: string | undefined;
346
+ loading?: string | undefined;
347
+ offline?: string | undefined;
348
+ } | undefined;
349
+ }>, "many">>;
350
+ /** How it looks on the phone: the orientation it holds and whether it fills the screen (Android honours both). */
351
+ screen: z.ZodOptional<z.ZodObject<{
352
+ orientation: z.ZodOptional<z.ZodEnum<["portrait", "landscape", "any"]>>;
353
+ fullscreen: z.ZodOptional<z.ZodBoolean>;
354
+ }, "strict", z.ZodTypeAny, {
355
+ orientation?: "any" | "portrait" | "landscape" | undefined;
356
+ fullscreen?: boolean | undefined;
357
+ }, {
358
+ orientation?: "any" | "portrait" | "landscape" | undefined;
359
+ fullscreen?: boolean | undefined;
360
+ }>>;
280
361
  journeys: z.ZodArray<z.ZodObject<{
281
362
  id: z.ZodString;
282
363
  title: z.ZodString;
@@ -325,6 +406,8 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
325
406
  kind: z.ZodLiteral<"coded">;
326
407
  /** game: a canvas, a loop and touch, the code a script; page: a whole HTML page, drawn full screen (what an app written as HTML becomes). */
327
408
  kit: z.ZodEnum<["game", "page"]>;
409
+ /** Optional: the native widget or screen it stands in for, like "CupertinoSlidingSegmentedControl", so `blackbox coverage` can say which the catalog lacks. */
410
+ replaces: z.ZodOptional<z.ZodString>;
328
411
  inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<["number", "string", "boolean", "list", "map"]>>>;
329
412
  events: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodEnum<["number", "string", "boolean", "list", "map"]>>>>;
330
413
  code: z.ZodString;
@@ -334,10 +417,12 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
334
417
  kit: "game" | "page";
335
418
  inputs: Record<string, "string" | "number" | "boolean" | "map" | "list">;
336
419
  events: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">>;
420
+ replaces?: string | undefined;
337
421
  }, {
338
422
  code: string;
339
423
  kind: "coded";
340
424
  kit: "game" | "page";
425
+ replaces?: string | undefined;
341
426
  inputs?: Record<string, "string" | "number" | "boolean" | "map" | "list"> | undefined;
342
427
  events?: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">> | undefined;
343
428
  }>, {
@@ -346,10 +431,12 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
346
431
  kit: "game" | "page";
347
432
  inputs: Record<string, "string" | "number" | "boolean" | "map" | "list">;
348
433
  events: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">>;
434
+ replaces?: string | undefined;
349
435
  }, {
350
436
  code: string;
351
437
  kind: "coded";
352
438
  kit: "game" | "page";
439
+ replaces?: string | undefined;
353
440
  inputs?: Record<string, "string" | "number" | "boolean" | "map" | "list"> | undefined;
354
441
  events?: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">> | undefined;
355
442
  }>, {
@@ -358,10 +445,12 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
358
445
  kit: "game" | "page";
359
446
  inputs: Record<string, "string" | "number" | "boolean" | "map" | "list">;
360
447
  events: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">>;
448
+ replaces?: string | undefined;
361
449
  }, {
362
450
  code: string;
363
451
  kind: "coded";
364
452
  kit: "game" | "page";
453
+ replaces?: string | undefined;
365
454
  inputs?: Record<string, "string" | "number" | "boolean" | "map" | "list"> | undefined;
366
455
  events?: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">> | undefined;
367
456
  }>>, Record<string, {
@@ -370,15 +459,34 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
370
459
  kit: "game" | "page";
371
460
  inputs: Record<string, "string" | "number" | "boolean" | "map" | "list">;
372
461
  events: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">>;
462
+ replaces?: string | undefined;
373
463
  }>, Record<string, {
374
464
  code: string;
375
465
  kind: "coded";
376
466
  kit: "game" | "page";
467
+ replaces?: string | undefined;
377
468
  inputs?: Record<string, "string" | "number" | "boolean" | "map" | "list"> | undefined;
378
469
  events?: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">> | undefined;
379
470
  }>>>;
380
471
  /** The page-kit component that is the whole app: the spec is then that page, compiled to its HTML as written (see page.ts). */
381
472
  page: z.ZodOptional<z.ZodString>;
473
+ /**
474
+ * What the original app does that this one doesn't, said plainly so no one is promised it: a native feature with
475
+ * no web equivalent yet (background jobs, home-screen widgets, push notifications, in-app purchases...).
476
+ */
477
+ gaps: z.ZodOptional<z.ZodArray<z.ZodObject<{
478
+ kind: z.ZodEnum<["background", "widget", "notifications", "purchases", "accounts", "sync", "device", "other"]>;
479
+ what: z.ZodString;
480
+ why: z.ZodOptional<z.ZodString>;
481
+ }, "strict", z.ZodTypeAny, {
482
+ kind: "notifications" | "other" | "background" | "widget" | "purchases" | "accounts" | "sync" | "device";
483
+ what: string;
484
+ why?: string | undefined;
485
+ }, {
486
+ kind: "notifications" | "other" | "background" | "widget" | "purchases" | "accounts" | "sync" | "device";
487
+ what: string;
488
+ why?: string | undefined;
489
+ }>, "many">>;
382
490
  /** What a remix may change and what stays as the partner made it. */
383
491
  mods: z.ZodDefault<z.ZodObject<{
384
492
  open: z.ZodArray<z.ZodString, "many">;
@@ -397,6 +505,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
397
505
  kit: "game" | "page";
398
506
  inputs: Record<string, "string" | "number" | "boolean" | "map" | "list">;
399
507
  events: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">>;
508
+ replaces?: string | undefined;
400
509
  }>;
401
510
  surface: {
402
511
  catalog: string;
@@ -434,6 +543,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
434
543
  cel: string;
435
544
  }[];
436
545
  initial?: unknown;
546
+ personal?: string[] | undefined;
437
547
  };
438
548
  logic: {
439
549
  consts: Record<string, string | number | boolean>;
@@ -443,7 +553,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
443
553
  }>;
444
554
  computed: Record<string, string>;
445
555
  vectors: {
446
- from: "recorded" | "hint";
556
+ from: "recorded" | "app" | "hint";
447
557
  fn: string;
448
558
  in: unknown[];
449
559
  out?: unknown;
@@ -452,6 +562,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
452
562
  network: {
453
563
  site: string;
454
564
  why: string;
565
+ sends: string;
455
566
  }[];
456
567
  uses: {
457
568
  why: string;
@@ -476,6 +587,25 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
476
587
  locked: string[];
477
588
  };
478
589
  page?: string | undefined;
590
+ screens?: {
591
+ id: string;
592
+ says: string;
593
+ states?: {
594
+ error?: string | undefined;
595
+ empty?: string | undefined;
596
+ loading?: string | undefined;
597
+ offline?: string | undefined;
598
+ } | undefined;
599
+ }[] | undefined;
600
+ screen?: {
601
+ orientation?: "any" | "portrait" | "landscape" | undefined;
602
+ fullscreen?: boolean | undefined;
603
+ } | undefined;
604
+ gaps?: {
605
+ kind: "notifications" | "other" | "background" | "widget" | "purchases" | "accounts" | "sync" | "device";
606
+ what: string;
607
+ why?: string | undefined;
608
+ }[] | undefined;
479
609
  }, {
480
610
  surface: {
481
611
  catalog: string;
@@ -513,6 +643,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
513
643
  path: string;
514
644
  source: string;
515
645
  }[] | undefined;
646
+ personal?: string[] | undefined;
516
647
  };
517
648
  logic: {
518
649
  consts?: Record<string, string | number | boolean> | undefined;
@@ -522,7 +653,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
522
653
  }> | undefined;
523
654
  computed?: Record<string, string> | undefined;
524
655
  vectors?: {
525
- from: "recorded" | "hint";
656
+ from: "recorded" | "app" | "hint";
526
657
  fn: string;
527
658
  in: unknown[];
528
659
  out?: unknown;
@@ -540,6 +671,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
540
671
  code: string;
541
672
  kind: "coded";
542
673
  kit: "game" | "page";
674
+ replaces?: string | undefined;
543
675
  inputs?: Record<string, "string" | "number" | "boolean" | "map" | "list"> | undefined;
544
676
  events?: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">> | undefined;
545
677
  }> | undefined;
@@ -547,17 +679,37 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
547
679
  network?: {
548
680
  site: string;
549
681
  why: string;
682
+ sends: string;
550
683
  }[] | undefined;
551
684
  uses?: {
552
685
  why: string;
553
686
  what: "camera" | "microphone" | "location" | "motion" | "screen-awake" | "gamepad" | "voice" | "contacts" | "nfc";
554
687
  }[] | undefined;
688
+ screens?: {
689
+ id: string;
690
+ says: string;
691
+ states?: {
692
+ error?: string | undefined;
693
+ empty?: string | undefined;
694
+ loading?: string | undefined;
695
+ offline?: string | undefined;
696
+ } | undefined;
697
+ }[] | undefined;
698
+ screen?: {
699
+ orientation?: "any" | "portrait" | "landscape" | undefined;
700
+ fullscreen?: boolean | undefined;
701
+ } | undefined;
555
702
  promises?: {
556
703
  text: string;
557
704
  id: string;
558
705
  kind?: "data" | undefined;
559
706
  }[] | undefined;
560
707
  tests?: Record<string, unknown>[] | undefined;
708
+ gaps?: {
709
+ kind: "notifications" | "other" | "background" | "widget" | "purchases" | "accounts" | "sync" | "device";
710
+ what: string;
711
+ why?: string | undefined;
712
+ }[] | undefined;
561
713
  mods?: {
562
714
  open: string[];
563
715
  locked: string[];
@@ -569,6 +721,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
569
721
  kit: "game" | "page";
570
722
  inputs: Record<string, "string" | "number" | "boolean" | "map" | "list">;
571
723
  events: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">>;
724
+ replaces?: string | undefined;
572
725
  }>;
573
726
  surface: {
574
727
  catalog: string;
@@ -606,6 +759,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
606
759
  cel: string;
607
760
  }[];
608
761
  initial?: unknown;
762
+ personal?: string[] | undefined;
609
763
  };
610
764
  logic: {
611
765
  consts: Record<string, string | number | boolean>;
@@ -615,7 +769,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
615
769
  }>;
616
770
  computed: Record<string, string>;
617
771
  vectors: {
618
- from: "recorded" | "hint";
772
+ from: "recorded" | "app" | "hint";
619
773
  fn: string;
620
774
  in: unknown[];
621
775
  out?: unknown;
@@ -624,6 +778,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
624
778
  network: {
625
779
  site: string;
626
780
  why: string;
781
+ sends: string;
627
782
  }[];
628
783
  uses: {
629
784
  why: string;
@@ -648,6 +803,25 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
648
803
  locked: string[];
649
804
  };
650
805
  page?: string | undefined;
806
+ screens?: {
807
+ id: string;
808
+ says: string;
809
+ states?: {
810
+ error?: string | undefined;
811
+ empty?: string | undefined;
812
+ loading?: string | undefined;
813
+ offline?: string | undefined;
814
+ } | undefined;
815
+ }[] | undefined;
816
+ screen?: {
817
+ orientation?: "any" | "portrait" | "landscape" | undefined;
818
+ fullscreen?: boolean | undefined;
819
+ } | undefined;
820
+ gaps?: {
821
+ kind: "notifications" | "other" | "background" | "widget" | "purchases" | "accounts" | "sync" | "device";
822
+ what: string;
823
+ why?: string | undefined;
824
+ }[] | undefined;
651
825
  }, {
652
826
  surface: {
653
827
  catalog: string;
@@ -685,6 +859,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
685
859
  path: string;
686
860
  source: string;
687
861
  }[] | undefined;
862
+ personal?: string[] | undefined;
688
863
  };
689
864
  logic: {
690
865
  consts?: Record<string, string | number | boolean> | undefined;
@@ -694,7 +869,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
694
869
  }> | undefined;
695
870
  computed?: Record<string, string> | undefined;
696
871
  vectors?: {
697
- from: "recorded" | "hint";
872
+ from: "recorded" | "app" | "hint";
698
873
  fn: string;
699
874
  in: unknown[];
700
875
  out?: unknown;
@@ -712,6 +887,7 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
712
887
  code: string;
713
888
  kind: "coded";
714
889
  kit: "game" | "page";
890
+ replaces?: string | undefined;
715
891
  inputs?: Record<string, "string" | "number" | "boolean" | "map" | "list"> | undefined;
716
892
  events?: Record<string, Record<string, "string" | "number" | "boolean" | "map" | "list">> | undefined;
717
893
  }> | undefined;
@@ -719,17 +895,37 @@ export declare const SpecSchema: z.ZodEffects<z.ZodObject<{
719
895
  network?: {
720
896
  site: string;
721
897
  why: string;
898
+ sends: string;
722
899
  }[] | undefined;
723
900
  uses?: {
724
901
  why: string;
725
902
  what: "camera" | "microphone" | "location" | "motion" | "screen-awake" | "gamepad" | "voice" | "contacts" | "nfc";
726
903
  }[] | undefined;
904
+ screens?: {
905
+ id: string;
906
+ says: string;
907
+ states?: {
908
+ error?: string | undefined;
909
+ empty?: string | undefined;
910
+ loading?: string | undefined;
911
+ offline?: string | undefined;
912
+ } | undefined;
913
+ }[] | undefined;
914
+ screen?: {
915
+ orientation?: "any" | "portrait" | "landscape" | undefined;
916
+ fullscreen?: boolean | undefined;
917
+ } | undefined;
727
918
  promises?: {
728
919
  text: string;
729
920
  id: string;
730
921
  kind?: "data" | undefined;
731
922
  }[] | undefined;
732
923
  tests?: Record<string, unknown>[] | undefined;
924
+ gaps?: {
925
+ kind: "notifications" | "other" | "background" | "widget" | "purchases" | "accounts" | "sync" | "device";
926
+ what: string;
927
+ why?: string | undefined;
928
+ }[] | undefined;
733
929
  mods?: {
734
930
  open: string[];
735
931
  locked: string[];
@@ -61,6 +61,8 @@ export const CodedComponent = z
61
61
  kind: z.literal("coded"),
62
62
  /** game: a canvas, a loop and touch, the code a script; page: a whole HTML page, drawn full screen (what an app written as HTML becomes). */
63
63
  kit: z.enum(["game", "page"]),
64
+ /** Optional: the native widget or screen it stands in for, like "CupertinoSlidingSegmentedControl", so `blackbox coverage` can say which the catalog lacks. */
65
+ replaces: Text(120).optional(),
64
66
  inputs: z.record(CelName, CodedType).default({}),
65
67
  events: z.record(CelName, z.record(CelName, CodedType)).default({}),
66
68
  code: z
@@ -80,6 +82,8 @@ export const CodedComponent = z
80
82
  ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["code"], message: "a game kit's code is at most 64 KB" });
81
83
  })
82
84
  .refine((c) => Object.keys(c.inputs).length <= 10 && Object.keys(c.events).length <= 10, "at most 10 inputs and 10 events");
85
+ /** The kinds of thing a ported app may not do that its original did. */
86
+ export const GAP_KINDS = ["background", "widget", "notifications", "purchases", "accounts", "sync", "device", "other"];
83
87
  export const SpecSchema = z
84
88
  .object({
85
89
  v: z.literal(1),
@@ -138,6 +142,11 @@ export const SpecSchema = z
138
142
  .default([]),
139
143
  /** Each step turns version `from` into `to`, as a CEL expression over `saved`. */
140
144
  migrations: z.array(z.object({ from: z.number().int().min(1), to: z.number().int().min(2), cel: Cel }).strict()),
145
+ /**
146
+ * What it keeps: the saved fields that are personal (health, money, people's names), as pointers into the saved
147
+ * data, like /days or /people. They stay on the phone; CloudCrane never puts them in a remix, screenshot or report.
148
+ */
149
+ personal: z.array(Pointer).max(20).optional(),
141
150
  })
142
151
  .strict(),
143
152
  logic: z
@@ -147,11 +156,21 @@ export const SpecSchema = z
147
156
  /** Values the screens read, kept up to date: a data-model pointer and the CEL that computes it. */
148
157
  computed: z.record(Pointer, Cel).default({}),
149
158
  /** Examples each function must reproduce: recorded from the source's own tests, or given. */
150
- vectors: z.array(z.object({ fn: CelName, in: z.array(z.unknown()).max(8), out: z.unknown(), from: z.enum(["recorded", "hint"]) }).strict()).default([]),
159
+ /**
160
+ * Examples each function must reproduce: `recorded` from the module's own tests, `app` from calls recorded while
161
+ * the original app ran, `hint` written by hand.
162
+ */
163
+ vectors: z
164
+ .array(z.object({ fn: CelName, in: z.array(z.unknown()).max(8), out: z.unknown(), from: z.enum(["recorded", "app", "hint"]) }).strict())
165
+ .default([]),
151
166
  })
152
167
  .strict(),
168
+ /**
169
+ * What it can reach: each site, why, and what it's sent, in the person's words ("the city you typed"). Shown to the
170
+ * person before the app opens; a site the code names but this doesn't list is refused.
171
+ */
153
172
  network: z
154
- .array(z.object({ site: z.string().regex(/^[a-z0-9.-]+\.[a-z]{2,}$/), why: Text(300) }).strict())
173
+ .array(z.object({ site: z.string().regex(/^[a-z0-9.-]+\.[a-z]{2,}$/), why: Text(300), sends: Text(200) }).strict())
155
174
  .max(5)
156
175
  .default([]),
157
176
  /** The device features the app uses, by CloudCrane's names, each once with why; anything not listed is blocked. */
@@ -159,6 +178,29 @@ export const SpecSchema = z
159
178
  .array(z.object({ what: z.enum(USE_NAMES), why: Text(300) }).strict())
160
179
  .max(USE_NAMES.length)
161
180
  .default([]),
181
+ /**
182
+ * What it does: each screen in words, and what it shows when there's nothing yet, while it loads, offline, and when
183
+ * something fails. Optional; without it the blueprint describes the one surface from its words.
184
+ */
185
+ screens: z
186
+ .array(z
187
+ .object({
188
+ id: Id,
189
+ says: Text(300),
190
+ states: z
191
+ .object({ empty: Text(200).optional(), loading: Text(200).optional(), offline: Text(200).optional(), error: Text(200).optional() })
192
+ .strict()
193
+ .optional(),
194
+ })
195
+ .strict())
196
+ .min(1)
197
+ .max(20)
198
+ .optional(),
199
+ /** How it looks on the phone: the orientation it holds and whether it fills the screen (Android honours both). */
200
+ screen: z
201
+ .object({ orientation: z.enum(["portrait", "landscape", "any"]).optional(), fullscreen: z.boolean().optional() })
202
+ .strict()
203
+ .optional(),
162
204
  journeys: z
163
205
  .array(z
164
206
  .object({
@@ -178,6 +220,14 @@ export const SpecSchema = z
178
220
  .default({}),
179
221
  /** The page-kit component that is the whole app: the spec is then that page, compiled to its HTML as written (see page.ts). */
180
222
  page: Id.optional(),
223
+ /**
224
+ * What the original app does that this one doesn't, said plainly so no one is promised it: a native feature with
225
+ * no web equivalent yet (background jobs, home-screen widgets, push notifications, in-app purchases...).
226
+ */
227
+ gaps: z
228
+ .array(z.object({ kind: z.enum(GAP_KINDS), what: Text(120), why: Text(300).optional() }).strict())
229
+ .max(20)
230
+ .optional(),
181
231
  /** What a remix may change and what stays as the partner made it. */
182
232
  mods: z
183
233
  .object({ open: z.array(PointerPattern), locked: z.array(PointerPattern) })
@@ -3,4 +3,5 @@ import { type Spec } from "./schema.js";
3
3
  export declare function validateSpec(input: unknown, catalog?: Catalog): {
4
4
  spec: Spec | null;
5
5
  problems: Problem[];
6
+ warnings: Problem[];
6
7
  };
@@ -4,8 +4,14 @@ import { leakProblems } from "./leak.js";
4
4
  import { logicProblems } from "./logic.js";
5
5
  import { SpecSchema } from "./schema.js";
6
6
  import { pageProblems } from "./page.js";
7
+ import { designWarnings, statesWarnings } from "./design.js";
8
+ import { memoryProblems } from "./memory.js";
9
+ import { reachProblems } from "./reach.js";
10
+ import { rulesWarnings } from "./fidelity.js";
7
11
  // Everything wrong with a spec, collected rather than stopping at the first: the
8
- // envelope's shape, then the A2UI surface, then references between parts.
12
+ // envelope's shape, then the A2UI surface, then references between parts, what it keeps (memory.ts) and what its code
13
+ // reaches (reach.ts). Problems refuse it. Warnings (how it looks and what each screen shows when empty, offline or
14
+ // failing: design.ts) don't: they say what would make it better.
9
15
  const pathOf = (path) => path
10
16
  .map((p) => (typeof p === "number" ? `[${p}]` : `.${p}`))
11
17
  .join("")
@@ -13,12 +19,12 @@ const pathOf = (path) => path
13
19
  export function validateSpec(input, catalog) {
14
20
  const parsed = SpecSchema.safeParse(input);
15
21
  if (!parsed.success)
16
- return { spec: null, problems: parsed.error.issues.map((i) => ({ at: pathOf(i.path) || "spec", problem: i.message })) };
22
+ return { spec: null, problems: parsed.error.issues.map((i) => ({ at: pathOf(i.path) || "spec", problem: i.message })), warnings: [] };
17
23
  const spec = parsed.data;
18
24
  // The surface is checked against the catalog it names (CloudCrane's, or A2UI's basic one).
19
25
  const named = catalog ?? catalogFor(spec.surface.catalog);
20
26
  if (!named)
21
- return { spec: null, problems: [{ at: "surface.catalog", problem: "not a catalog this runtime has" }] };
27
+ return { spec: null, problems: [{ at: "surface.catalog", problem: "not a catalog this runtime has" }], warnings: [] };
22
28
  const problems = [...surfaceProblems(spec.surface.messages, named)];
23
29
  const catalogId = spec.surface.messages[0].createSurface?.catalogId;
24
30
  if (catalogId && catalogId !== spec.surface.catalog)
@@ -47,11 +53,12 @@ export function validateSpec(input, catalog) {
47
53
  for (const m of spec.data.migrations)
48
54
  if (m.to !== m.from + 1 || m.to > spec.data.version)
49
55
  problems.push({ at: `data.migrations ${m.from}`, problem: "steps go one version at a time, up to data.version" });
50
- problems.push(...codedProblems(spec), ...pageProblems(spec), ...logicProblems(spec), ...leakProblems(spec));
56
+ problems.push(...codedProblems(spec), ...pageProblems(spec), ...logicProblems(spec), ...leakProblems(spec), ...memoryProblems(spec), ...reachProblems(spec));
51
57
  for (const locked of spec.mods.locked)
52
58
  if (spec.mods.open.includes(locked))
53
59
  problems.push({ at: `mods ${locked}`, problem: "both open and locked" });
54
- return { spec: problems.length ? null : spec, problems };
60
+ const warnings = [...designWarnings(spec), ...statesWarnings(spec), ...rulesWarnings(spec)];
61
+ return { spec: problems.length ? null : spec, problems, warnings };
55
62
  }
56
63
  /** Each Coded placement names a component the spec defines, and only its declared inputs and events; each component is placed. */
57
64
  function codedProblems(spec) {