@webstir-io/module-contract 0.1.69 → 0.3.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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Electric Coding LLC and contributors
3
+ Copyright (c) 2025-2026 Sqware Logics, LLC and contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The wire protocol between the client-nav feature and the backend runtime. Constants only, so a
3
+ * browser bundle can import them without the rest of the contract.
4
+ */
5
+ export declare const CLIENT_NAV_HEADERS: {
6
+ /** Sent on every request client-nav makes itself. */
7
+ readonly request: 'x-webstir-client-nav';
8
+ /**
9
+ * Sent when client-nav can follow a redirect itself: the backend then answers a redirect with
10
+ * 204 and `location` instead of 3xx, so the destination keeps its #fragment.
11
+ */
12
+ readonly acceptLocation: 'x-webstir-accept-location';
13
+ /** Where to go next, in place of a redirect's Location. */
14
+ readonly location: 'x-webstir-location';
15
+ /** Identifies one form submission, so the same post sent again gets the first answer. */
16
+ readonly submission: 'x-webstir-submission';
17
+ };
18
+ /** The form field carrying the submission id when client-nav falls back to a normal post. */
19
+ export declare const CLIENT_NAV_SUBMISSION_FIELD = "_webstir_submission";
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The wire protocol between the client-nav feature and the backend runtime. Constants only, so a
3
+ * browser bundle can import them without the rest of the contract.
4
+ */
5
+ export const CLIENT_NAV_HEADERS = {
6
+ /** Sent on every request client-nav makes itself. */
7
+ request: 'x-webstir-client-nav',
8
+ /**
9
+ * Sent when client-nav can follow a redirect itself: the backend then answers a redirect with
10
+ * 204 and `location` instead of 3xx, so the destination keeps its #fragment.
11
+ */
12
+ acceptLocation: 'x-webstir-accept-location',
13
+ /** Where to go next, in place of a redirect's Location. */
14
+ location: 'x-webstir-location',
15
+ /** Identifies one form submission, so the same post sent again gets the first answer. */
16
+ submission: 'x-webstir-submission',
17
+ };
18
+ /** The form field carrying the submission id when client-nav falls back to a normal post. */
19
+ export const CLIENT_NAV_SUBMISSION_FIELD = '_webstir_submission';
package/dist/index.d.ts CHANGED
@@ -85,6 +85,24 @@ export interface RequestContext<TRequest = unknown, TReply = unknown, TAuth = un
85
85
  readonly requestId: string;
86
86
  readonly now: () => Date;
87
87
  }
88
+ export type FormIssueCode = 'validation' | 'auth' | 'csrf';
89
+ export interface FormIssue {
90
+ readonly code?: FormIssueCode;
91
+ readonly field?: string;
92
+ readonly message: string;
93
+ }
94
+ export type FormValues = Readonly<Record<string, string | string[]>>;
95
+ /** A form's last failed submission, as a view loader reads it. Empty when there is none. */
96
+ export interface FormState {
97
+ readonly submitted: boolean;
98
+ readonly values: FormValues;
99
+ readonly issues: readonly FormIssue[];
100
+ /** The first message per field, plus `form` for issues without a field. */
101
+ readonly errors: Readonly<Record<string, string>>;
102
+ }
103
+ export interface FormStateReader {
104
+ read(formId: string): FormState;
105
+ }
88
106
  export interface SSRContext<TParams = Record<string, string>, TAuth = unknown, TSession = unknown, TEnv extends EnvAccessor = EnvAccessor, TLogger extends Logger = Logger> {
89
107
  readonly url: URL;
90
108
  readonly params: TParams;
@@ -96,6 +114,7 @@ export interface SSRContext<TParams = Record<string, string>, TAuth = unknown, T
96
114
  readonly logger: TLogger;
97
115
  readonly requestId?: string;
98
116
  readonly now: () => Date;
117
+ readonly forms: FormStateReader;
99
118
  }
100
119
  export interface AuthSession<TData = Record<string, unknown>> {
101
120
  readonly id: string;
@@ -353,14 +372,17 @@ export type FlashPublishCondition = z.infer<typeof flashPublishConditionSchema>;
353
372
  export declare const routeFlashMessageSchema: z.ZodObject<{
354
373
  key: z.ZodString;
355
374
  level: z.ZodOptional<z.ZodEnum<["info", "success", "warning", "error"]>>;
375
+ message: z.ZodOptional<z.ZodString>;
356
376
  when: z.ZodOptional<z.ZodEnum<["always", "success", "error"]>>;
357
377
  }, "strict", z.ZodTypeAny, {
358
378
  key: string;
359
379
  level?: "error" | "info" | "success" | "warning" | undefined;
380
+ message?: string | undefined;
360
381
  when?: "always" | "error" | "success" | undefined;
361
382
  }, {
362
383
  key: string;
363
384
  level?: "error" | "info" | "success" | "warning" | undefined;
385
+ message?: string | undefined;
364
386
  when?: "always" | "error" | "success" | undefined;
365
387
  }>;
366
388
  export type RouteFlashMessageDefinition = z.infer<typeof routeFlashMessageSchema>;
@@ -369,14 +391,17 @@ export declare const routeFlashSchema: z.ZodObject<{
369
391
  publish: z.ZodOptional<z.ZodArray<z.ZodObject<{
370
392
  key: z.ZodString;
371
393
  level: z.ZodOptional<z.ZodEnum<["info", "success", "warning", "error"]>>;
394
+ message: z.ZodOptional<z.ZodString>;
372
395
  when: z.ZodOptional<z.ZodEnum<["always", "success", "error"]>>;
373
396
  }, "strict", z.ZodTypeAny, {
374
397
  key: string;
375
398
  level?: "error" | "info" | "success" | "warning" | undefined;
399
+ message?: string | undefined;
376
400
  when?: "always" | "error" | "success" | undefined;
377
401
  }, {
378
402
  key: string;
379
403
  level?: "error" | "info" | "success" | "warning" | undefined;
404
+ message?: string | undefined;
380
405
  when?: "always" | "error" | "success" | undefined;
381
406
  }>, "many">>;
382
407
  }, "strict", z.ZodTypeAny, {
@@ -384,6 +409,7 @@ export declare const routeFlashSchema: z.ZodObject<{
384
409
  publish?: {
385
410
  key: string;
386
411
  level?: "error" | "info" | "success" | "warning" | undefined;
412
+ message?: string | undefined;
387
413
  when?: "always" | "error" | "success" | undefined;
388
414
  }[] | undefined;
389
415
  }, {
@@ -391,6 +417,7 @@ export declare const routeFlashSchema: z.ZodObject<{
391
417
  publish?: {
392
418
  key: string;
393
419
  level?: "error" | "info" | "success" | "warning" | undefined;
420
+ message?: string | undefined;
394
421
  when?: "always" | "error" | "success" | undefined;
395
422
  }[] | undefined;
396
423
  }>;
@@ -413,14 +440,17 @@ export declare const routeFormSchema: z.ZodObject<{
413
440
  publish: z.ZodOptional<z.ZodArray<z.ZodObject<{
414
441
  key: z.ZodString;
415
442
  level: z.ZodOptional<z.ZodEnum<["info", "success", "warning", "error"]>>;
443
+ message: z.ZodOptional<z.ZodString>;
416
444
  when: z.ZodOptional<z.ZodEnum<["always", "success", "error"]>>;
417
445
  }, "strict", z.ZodTypeAny, {
418
446
  key: string;
419
447
  level?: "error" | "info" | "success" | "warning" | undefined;
448
+ message?: string | undefined;
420
449
  when?: "always" | "error" | "success" | undefined;
421
450
  }, {
422
451
  key: string;
423
452
  level?: "error" | "info" | "success" | "warning" | undefined;
453
+ message?: string | undefined;
424
454
  when?: "always" | "error" | "success" | undefined;
425
455
  }>, "many">>;
426
456
  }, "strict", z.ZodTypeAny, {
@@ -428,6 +458,7 @@ export declare const routeFormSchema: z.ZodObject<{
428
458
  publish?: {
429
459
  key: string;
430
460
  level?: "error" | "info" | "success" | "warning" | undefined;
461
+ message?: string | undefined;
431
462
  when?: "always" | "error" | "success" | undefined;
432
463
  }[] | undefined;
433
464
  }, {
@@ -435,6 +466,7 @@ export declare const routeFormSchema: z.ZodObject<{
435
466
  publish?: {
436
467
  key: string;
437
468
  level?: "error" | "info" | "success" | "warning" | undefined;
469
+ message?: string | undefined;
438
470
  when?: "always" | "error" | "success" | undefined;
439
471
  }[] | undefined;
440
472
  }>>;
@@ -450,6 +482,7 @@ export declare const routeFormSchema: z.ZodObject<{
450
482
  publish?: {
451
483
  key: string;
452
484
  level?: "error" | "info" | "success" | "warning" | undefined;
485
+ message?: string | undefined;
453
486
  when?: "always" | "error" | "success" | undefined;
454
487
  }[] | undefined;
455
488
  } | undefined;
@@ -465,6 +498,7 @@ export declare const routeFormSchema: z.ZodObject<{
465
498
  publish?: {
466
499
  key: string;
467
500
  level?: "error" | "info" | "success" | "warning" | undefined;
501
+ message?: string | undefined;
468
502
  when?: "always" | "error" | "success" | undefined;
469
503
  }[] | undefined;
470
504
  } | undefined;
@@ -1579,14 +1613,17 @@ export declare const routeDefinitionSchema: z.ZodObject<{
1579
1613
  publish: z.ZodOptional<z.ZodArray<z.ZodObject<{
1580
1614
  key: z.ZodString;
1581
1615
  level: z.ZodOptional<z.ZodEnum<["info", "success", "warning", "error"]>>;
1616
+ message: z.ZodOptional<z.ZodString>;
1582
1617
  when: z.ZodOptional<z.ZodEnum<["always", "success", "error"]>>;
1583
1618
  }, "strict", z.ZodTypeAny, {
1584
1619
  key: string;
1585
1620
  level?: "error" | "info" | "success" | "warning" | undefined;
1621
+ message?: string | undefined;
1586
1622
  when?: "always" | "error" | "success" | undefined;
1587
1623
  }, {
1588
1624
  key: string;
1589
1625
  level?: "error" | "info" | "success" | "warning" | undefined;
1626
+ message?: string | undefined;
1590
1627
  when?: "always" | "error" | "success" | undefined;
1591
1628
  }>, "many">>;
1592
1629
  }, "strict", z.ZodTypeAny, {
@@ -1594,6 +1631,7 @@ export declare const routeDefinitionSchema: z.ZodObject<{
1594
1631
  publish?: {
1595
1632
  key: string;
1596
1633
  level?: "error" | "info" | "success" | "warning" | undefined;
1634
+ message?: string | undefined;
1597
1635
  when?: "always" | "error" | "success" | undefined;
1598
1636
  }[] | undefined;
1599
1637
  }, {
@@ -1601,6 +1639,7 @@ export declare const routeDefinitionSchema: z.ZodObject<{
1601
1639
  publish?: {
1602
1640
  key: string;
1603
1641
  level?: "error" | "info" | "success" | "warning" | undefined;
1642
+ message?: string | undefined;
1604
1643
  when?: "always" | "error" | "success" | undefined;
1605
1644
  }[] | undefined;
1606
1645
  }>>;
@@ -1622,14 +1661,17 @@ export declare const routeDefinitionSchema: z.ZodObject<{
1622
1661
  publish: z.ZodOptional<z.ZodArray<z.ZodObject<{
1623
1662
  key: z.ZodString;
1624
1663
  level: z.ZodOptional<z.ZodEnum<["info", "success", "warning", "error"]>>;
1664
+ message: z.ZodOptional<z.ZodString>;
1625
1665
  when: z.ZodOptional<z.ZodEnum<["always", "success", "error"]>>;
1626
1666
  }, "strict", z.ZodTypeAny, {
1627
1667
  key: string;
1628
1668
  level?: "error" | "info" | "success" | "warning" | undefined;
1669
+ message?: string | undefined;
1629
1670
  when?: "always" | "error" | "success" | undefined;
1630
1671
  }, {
1631
1672
  key: string;
1632
1673
  level?: "error" | "info" | "success" | "warning" | undefined;
1674
+ message?: string | undefined;
1633
1675
  when?: "always" | "error" | "success" | undefined;
1634
1676
  }>, "many">>;
1635
1677
  }, "strict", z.ZodTypeAny, {
@@ -1637,6 +1679,7 @@ export declare const routeDefinitionSchema: z.ZodObject<{
1637
1679
  publish?: {
1638
1680
  key: string;
1639
1681
  level?: "error" | "info" | "success" | "warning" | undefined;
1682
+ message?: string | undefined;
1640
1683
  when?: "always" | "error" | "success" | undefined;
1641
1684
  }[] | undefined;
1642
1685
  }, {
@@ -1644,6 +1687,7 @@ export declare const routeDefinitionSchema: z.ZodObject<{
1644
1687
  publish?: {
1645
1688
  key: string;
1646
1689
  level?: "error" | "info" | "success" | "warning" | undefined;
1690
+ message?: string | undefined;
1647
1691
  when?: "always" | "error" | "success" | undefined;
1648
1692
  }[] | undefined;
1649
1693
  }>>;
@@ -1659,6 +1703,7 @@ export declare const routeDefinitionSchema: z.ZodObject<{
1659
1703
  publish?: {
1660
1704
  key: string;
1661
1705
  level?: "error" | "info" | "success" | "warning" | undefined;
1706
+ message?: string | undefined;
1662
1707
  when?: "always" | "error" | "success" | undefined;
1663
1708
  }[] | undefined;
1664
1709
  } | undefined;
@@ -1674,6 +1719,7 @@ export declare const routeDefinitionSchema: z.ZodObject<{
1674
1719
  publish?: {
1675
1720
  key: string;
1676
1721
  level?: "error" | "info" | "success" | "warning" | undefined;
1722
+ message?: string | undefined;
1677
1723
  when?: "always" | "error" | "success" | undefined;
1678
1724
  }[] | undefined;
1679
1725
  } | undefined;
@@ -2270,6 +2316,7 @@ export declare const routeDefinitionSchema: z.ZodObject<{
2270
2316
  publish?: {
2271
2317
  key: string;
2272
2318
  level?: "error" | "info" | "success" | "warning" | undefined;
2319
+ message?: string | undefined;
2273
2320
  when?: "always" | "error" | "success" | undefined;
2274
2321
  }[] | undefined;
2275
2322
  } | undefined;
@@ -2285,6 +2332,7 @@ export declare const routeDefinitionSchema: z.ZodObject<{
2285
2332
  publish?: {
2286
2333
  key: string;
2287
2334
  level?: "error" | "info" | "success" | "warning" | undefined;
2335
+ message?: string | undefined;
2288
2336
  when?: "always" | "error" | "success" | undefined;
2289
2337
  }[] | undefined;
2290
2338
  } | undefined;
@@ -2431,6 +2479,7 @@ export declare const routeDefinitionSchema: z.ZodObject<{
2431
2479
  publish?: {
2432
2480
  key: string;
2433
2481
  level?: "error" | "info" | "success" | "warning" | undefined;
2482
+ message?: string | undefined;
2434
2483
  when?: "always" | "error" | "success" | undefined;
2435
2484
  }[] | undefined;
2436
2485
  } | undefined;
@@ -2446,6 +2495,7 @@ export declare const routeDefinitionSchema: z.ZodObject<{
2446
2495
  publish?: {
2447
2496
  key: string;
2448
2497
  level?: "error" | "info" | "success" | "warning" | undefined;
2498
+ message?: string | undefined;
2449
2499
  when?: "always" | "error" | "success" | undefined;
2450
2500
  }[] | undefined;
2451
2501
  } | undefined;
@@ -2604,11 +2654,32 @@ export interface RouteFragmentResponse<TResponse extends z.ZodTypeAny> {
2604
2654
  };
2605
2655
  readonly headers?: Record<string, string>;
2606
2656
  }
2657
+ export interface RouteFlashMessage {
2658
+ readonly key?: string;
2659
+ readonly level?: FlashLevel;
2660
+ readonly message: string;
2661
+ }
2607
2662
  export interface RouteRedirectResponse {
2608
2663
  readonly status?: RedirectStatus;
2609
2664
  readonly redirect: {
2610
2665
  readonly location: string;
2611
2666
  };
2667
+ /** Messages the next rendered page receives in its `flash`. */
2668
+ readonly flash?: readonly RouteFlashMessage[];
2669
+ readonly headers?: Record<string, string>;
2670
+ }
2671
+ /** Renders a view in place of the action's response, with the failed form in its loader. */
2672
+ export interface RouteRerenderResponse {
2673
+ readonly status?: number;
2674
+ readonly rerender: {
2675
+ readonly view: string;
2676
+ readonly params?: Record<string, string>;
2677
+ readonly form: {
2678
+ readonly id: string;
2679
+ readonly values: FormValues;
2680
+ readonly issues: readonly FormIssue[];
2681
+ };
2682
+ };
2612
2683
  readonly headers?: Record<string, string>;
2613
2684
  }
2614
2685
  export interface RouteErrorResponse {
@@ -2617,8 +2688,8 @@ export interface RouteErrorResponse {
2617
2688
  readonly headers?: Record<string, string>;
2618
2689
  }
2619
2690
  export type RouteNavigationResult<TResponse extends z.ZodTypeAny> = RouteBodyResponse<TResponse> | RouteRedirectResponse | RouteErrorResponse;
2620
- export type RouteMutationResult<TResponse extends z.ZodTypeAny> = RouteBodyResponse<TResponse> | RouteFragmentResponse<TResponse> | RouteRedirectResponse | RouteErrorResponse;
2621
- export type RouteHandlerResult<TResponse extends z.ZodTypeAny> = RouteBodyResponse<TResponse> | RouteFragmentResponse<TResponse> | RouteRedirectResponse | RouteErrorResponse;
2691
+ export type RouteMutationResult<TResponse extends z.ZodTypeAny> = RouteBodyResponse<TResponse> | RouteFragmentResponse<TResponse> | RouteRedirectResponse | RouteRerenderResponse | RouteErrorResponse;
2692
+ export type RouteHandlerResult<TResponse extends z.ZodTypeAny> = RouteBodyResponse<TResponse> | RouteFragmentResponse<TResponse> | RouteRedirectResponse | RouteRerenderResponse | RouteErrorResponse;
2622
2693
  export type RouteHandler<TContext extends RequestContext, TParams extends z.ZodTypeAny | undefined, TQuery extends z.ZodTypeAny | undefined, TBody extends z.ZodTypeAny | undefined, TResponse extends z.ZodTypeAny> = (context: RouteHandlerContext<TContext, TParams, TQuery, TBody>) => Promise<RouteHandlerResult<TResponse>> | RouteHandlerResult<TResponse>;
2623
2694
  export interface RouteSpec<TContext extends RequestContext = RequestContext, TParams extends z.ZodTypeAny | undefined = undefined, TQuery extends z.ZodTypeAny | undefined = undefined, TBody extends z.ZodTypeAny | undefined = undefined, TResponse extends z.ZodTypeAny = z.ZodTypeAny> {
2624
2695
  readonly definition: RouteDefinition;
@@ -2843,14 +2914,17 @@ export declare const moduleManifestSchema: z.ZodObject<{
2843
2914
  publish: z.ZodOptional<z.ZodArray<z.ZodObject<{
2844
2915
  key: z.ZodString;
2845
2916
  level: z.ZodOptional<z.ZodEnum<["info", "success", "warning", "error"]>>;
2917
+ message: z.ZodOptional<z.ZodString>;
2846
2918
  when: z.ZodOptional<z.ZodEnum<["always", "success", "error"]>>;
2847
2919
  }, "strict", z.ZodTypeAny, {
2848
2920
  key: string;
2849
2921
  level?: "error" | "info" | "success" | "warning" | undefined;
2922
+ message?: string | undefined;
2850
2923
  when?: "always" | "error" | "success" | undefined;
2851
2924
  }, {
2852
2925
  key: string;
2853
2926
  level?: "error" | "info" | "success" | "warning" | undefined;
2927
+ message?: string | undefined;
2854
2928
  when?: "always" | "error" | "success" | undefined;
2855
2929
  }>, "many">>;
2856
2930
  }, "strict", z.ZodTypeAny, {
@@ -2858,6 +2932,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
2858
2932
  publish?: {
2859
2933
  key: string;
2860
2934
  level?: "error" | "info" | "success" | "warning" | undefined;
2935
+ message?: string | undefined;
2861
2936
  when?: "always" | "error" | "success" | undefined;
2862
2937
  }[] | undefined;
2863
2938
  }, {
@@ -2865,6 +2940,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
2865
2940
  publish?: {
2866
2941
  key: string;
2867
2942
  level?: "error" | "info" | "success" | "warning" | undefined;
2943
+ message?: string | undefined;
2868
2944
  when?: "always" | "error" | "success" | undefined;
2869
2945
  }[] | undefined;
2870
2946
  }>>;
@@ -2886,14 +2962,17 @@ export declare const moduleManifestSchema: z.ZodObject<{
2886
2962
  publish: z.ZodOptional<z.ZodArray<z.ZodObject<{
2887
2963
  key: z.ZodString;
2888
2964
  level: z.ZodOptional<z.ZodEnum<["info", "success", "warning", "error"]>>;
2965
+ message: z.ZodOptional<z.ZodString>;
2889
2966
  when: z.ZodOptional<z.ZodEnum<["always", "success", "error"]>>;
2890
2967
  }, "strict", z.ZodTypeAny, {
2891
2968
  key: string;
2892
2969
  level?: "error" | "info" | "success" | "warning" | undefined;
2970
+ message?: string | undefined;
2893
2971
  when?: "always" | "error" | "success" | undefined;
2894
2972
  }, {
2895
2973
  key: string;
2896
2974
  level?: "error" | "info" | "success" | "warning" | undefined;
2975
+ message?: string | undefined;
2897
2976
  when?: "always" | "error" | "success" | undefined;
2898
2977
  }>, "many">>;
2899
2978
  }, "strict", z.ZodTypeAny, {
@@ -2901,6 +2980,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
2901
2980
  publish?: {
2902
2981
  key: string;
2903
2982
  level?: "error" | "info" | "success" | "warning" | undefined;
2983
+ message?: string | undefined;
2904
2984
  when?: "always" | "error" | "success" | undefined;
2905
2985
  }[] | undefined;
2906
2986
  }, {
@@ -2908,6 +2988,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
2908
2988
  publish?: {
2909
2989
  key: string;
2910
2990
  level?: "error" | "info" | "success" | "warning" | undefined;
2991
+ message?: string | undefined;
2911
2992
  when?: "always" | "error" | "success" | undefined;
2912
2993
  }[] | undefined;
2913
2994
  }>>;
@@ -2923,6 +3004,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
2923
3004
  publish?: {
2924
3005
  key: string;
2925
3006
  level?: "error" | "info" | "success" | "warning" | undefined;
3007
+ message?: string | undefined;
2926
3008
  when?: "always" | "error" | "success" | undefined;
2927
3009
  }[] | undefined;
2928
3010
  } | undefined;
@@ -2938,6 +3020,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
2938
3020
  publish?: {
2939
3021
  key: string;
2940
3022
  level?: "error" | "info" | "success" | "warning" | undefined;
3023
+ message?: string | undefined;
2941
3024
  when?: "always" | "error" | "success" | undefined;
2942
3025
  }[] | undefined;
2943
3026
  } | undefined;
@@ -3534,6 +3617,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
3534
3617
  publish?: {
3535
3618
  key: string;
3536
3619
  level?: "error" | "info" | "success" | "warning" | undefined;
3620
+ message?: string | undefined;
3537
3621
  when?: "always" | "error" | "success" | undefined;
3538
3622
  }[] | undefined;
3539
3623
  } | undefined;
@@ -3549,6 +3633,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
3549
3633
  publish?: {
3550
3634
  key: string;
3551
3635
  level?: "error" | "info" | "success" | "warning" | undefined;
3636
+ message?: string | undefined;
3552
3637
  when?: "always" | "error" | "success" | undefined;
3553
3638
  }[] | undefined;
3554
3639
  } | undefined;
@@ -3695,6 +3780,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
3695
3780
  publish?: {
3696
3781
  key: string;
3697
3782
  level?: "error" | "info" | "success" | "warning" | undefined;
3783
+ message?: string | undefined;
3698
3784
  when?: "always" | "error" | "success" | undefined;
3699
3785
  }[] | undefined;
3700
3786
  } | undefined;
@@ -3710,6 +3796,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
3710
3796
  publish?: {
3711
3797
  key: string;
3712
3798
  level?: "error" | "info" | "success" | "warning" | undefined;
3799
+ message?: string | undefined;
3713
3800
  when?: "always" | "error" | "success" | undefined;
3714
3801
  }[] | undefined;
3715
3802
  } | undefined;
@@ -4019,6 +4106,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
4019
4106
  publish?: {
4020
4107
  key: string;
4021
4108
  level?: "error" | "info" | "success" | "warning" | undefined;
4109
+ message?: string | undefined;
4022
4110
  when?: "always" | "error" | "success" | undefined;
4023
4111
  }[] | undefined;
4024
4112
  } | undefined;
@@ -4034,6 +4122,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
4034
4122
  publish?: {
4035
4123
  key: string;
4036
4124
  level?: "error" | "info" | "success" | "warning" | undefined;
4125
+ message?: string | undefined;
4037
4126
  when?: "always" | "error" | "success" | undefined;
4038
4127
  }[] | undefined;
4039
4128
  } | undefined;
@@ -4239,6 +4328,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
4239
4328
  publish?: {
4240
4329
  key: string;
4241
4330
  level?: "error" | "info" | "success" | "warning" | undefined;
4331
+ message?: string | undefined;
4242
4332
  when?: "always" | "error" | "success" | undefined;
4243
4333
  }[] | undefined;
4244
4334
  } | undefined;
@@ -4254,6 +4344,7 @@ export declare const moduleManifestSchema: z.ZodObject<{
4254
4344
  publish?: {
4255
4345
  key: string;
4256
4346
  level?: "error" | "info" | "success" | "warning" | undefined;
4347
+ message?: string | undefined;
4257
4348
  when?: "always" | "error" | "success" | undefined;
4258
4349
  }[] | undefined;
4259
4350
  } | undefined;
@@ -4459,3 +4550,8 @@ export declare function defineView<TContext extends SSRContext, TParams extends
4459
4550
  export declare function createModule<TRequestContext extends RequestContext, TSSRContext extends SSRContext, TRoutes extends readonly LooseRouteSpec<TRequestContext>[] = readonly LooseRouteSpec<TRequestContext>[], TViews extends readonly LooseViewSpec<TSSRContext>[] = readonly LooseViewSpec<TSSRContext>[]>(definition: ModuleDefinition<TRequestContext, TSSRContext, TRoutes, TViews>): ModuleDefinition<TRequestContext, TSSRContext, TRoutes, TViews>;
4460
4551
  export { fromTsRestRoute, fromTsRestRouter } from './adapters/ts-rest.js';
4461
4552
  export type { FromTsRestRouteOptions, FromTsRestRouterOptions, RouterRouteConfig, } from './adapters/ts-rest.js';
4553
+ export { RENDER_PROGRAM_FILE, RENDER_PROGRAM_VERSION, renderFlashMessageSchema, renderFlashSchema, renderNodeSchema, renderPathSchema, renderProgramSchema, renderSourceLocationSchema, } from './render-program.js';
4554
+ export type { RenderFlashMessage, RenderNode, RenderPath, RenderProgram, RenderSourceLocation, } from './render-program.js';
4555
+ export { executeRenderProgram, programUsesCsrf, readRenderProgram, RENDER_CSRF_FIELD, RenderProgramError, prepareViewData, schemaDeclaresField, } from './render-execute.js';
4556
+ export type { ExecuteRenderProgramOptions, PreparedViewData } from './render-execute.js';
4557
+ export { CLIENT_NAV_HEADERS, CLIENT_NAV_SUBMISSION_FIELD } from './client-nav.js';
package/dist/index.js CHANGED
@@ -84,6 +84,7 @@ export const routeFlashMessageSchema = z
84
84
  .object({
85
85
  key: z.string().min(1),
86
86
  level: flashLevelSchema.optional(),
87
+ message: z.string().min(1).optional(),
87
88
  when: flashPublishConditionSchema.optional(),
88
89
  })
89
90
  .strict();
@@ -231,3 +232,6 @@ export function createModule(definition) {
231
232
  return definition;
232
233
  }
233
234
  export { fromTsRestRoute, fromTsRestRouter } from './adapters/ts-rest.js';
235
+ export { RENDER_PROGRAM_FILE, RENDER_PROGRAM_VERSION, renderFlashMessageSchema, renderFlashSchema, renderNodeSchema, renderPathSchema, renderProgramSchema, renderSourceLocationSchema, } from './render-program.js';
236
+ export { executeRenderProgram, programUsesCsrf, readRenderProgram, RENDER_CSRF_FIELD, RenderProgramError, prepareViewData, schemaDeclaresField, } from './render-execute.js';
237
+ export { CLIENT_NAV_HEADERS, CLIENT_NAV_SUBMISSION_FIELD } from './client-nav.js';
@@ -0,0 +1,36 @@
1
+ import { type RenderProgram, type RenderSourceLocation } from './render-program.js';
2
+ export declare const RENDER_CSRF_FIELD = "_csrf";
3
+ export declare class RenderProgramError extends Error {
4
+ readonly loc: RenderSourceLocation;
5
+ constructor(loc: RenderSourceLocation, message: string);
6
+ }
7
+ export interface ExecuteRenderProgramOptions {
8
+ /**
9
+ * The session's token for POST forms. `false` renders static output, which has no session:
10
+ * POST forms render without a token field.
11
+ */
12
+ readonly csrfToken?: string | false;
13
+ }
14
+ export declare function readRenderProgram(value: unknown, source: string): RenderProgram;
15
+ export declare function programUsesCsrf(program: RenderProgram): boolean;
16
+ export declare function executeRenderProgram(program: RenderProgram, data: unknown, options?: ExecuteRenderProgramOptions): string;
17
+ export type PreparedViewData = {
18
+ readonly ok: true;
19
+ readonly data: unknown;
20
+ } | {
21
+ readonly ok: false;
22
+ readonly error: string;
23
+ };
24
+ /**
25
+ * Checks a loader's data against its view schema and supplies `flash`, which the framework
26
+ * owns: the session's messages come first, then any the loader returned. Whether the schema
27
+ * wants `flash` can depend on the branch a union picks, so the data is parsed with it and,
28
+ * failing that, without it, starting with what the schema declares.
29
+ */
30
+ export declare function prepareViewData(schema: unknown, loaded: unknown, flash: readonly unknown[]): PreparedViewData;
31
+ /**
32
+ * Whether a zod-style schema's underlying object declares `field`, looking through wrappers
33
+ * such as optional, default, refine and transform, and into unions and intersections. A schema
34
+ * with no object inside declares nothing.
35
+ */
36
+ export declare function schemaDeclaresField(schema: unknown, field: string): boolean;
@@ -0,0 +1,269 @@
1
+ import { RENDER_PROGRAM_VERSION, } from './render-program.js';
2
+ export const RENDER_CSRF_FIELD = '_csrf';
3
+ const SAFE_URL_SCHEMES = new Set(['http', 'https', 'mailto', 'tel']);
4
+ const BLOCKED_URL = 'about:invalid';
5
+ // ARIA attributes whose values include the tokens "true" and "false" (WAI-ARIA 1.2). The rest take
6
+ // text or ids, where false still leaves the attribute out.
7
+ const ARIA_TRUE_FALSE = new Set([
8
+ 'aria-atomic',
9
+ 'aria-busy',
10
+ 'aria-checked',
11
+ 'aria-current',
12
+ 'aria-disabled',
13
+ 'aria-expanded',
14
+ 'aria-grabbed',
15
+ 'aria-haspopup',
16
+ 'aria-hidden',
17
+ 'aria-invalid',
18
+ 'aria-modal',
19
+ 'aria-multiline',
20
+ 'aria-multiselectable',
21
+ 'aria-pressed',
22
+ 'aria-readonly',
23
+ 'aria-required',
24
+ 'aria-selected',
25
+ ]);
26
+ export class RenderProgramError extends Error {
27
+ loc;
28
+ constructor(loc, message) {
29
+ super(`${loc.file}:${loc.line}: ${message}`);
30
+ this.name = 'RenderProgramError';
31
+ this.loc = loc;
32
+ }
33
+ }
34
+ export function readRenderProgram(value, source) {
35
+ if (!value ||
36
+ typeof value !== 'object' ||
37
+ value.version !== RENDER_PROGRAM_VERSION ||
38
+ !Array.isArray(value.nodes)) {
39
+ throw new Error(`Render program ${source} is not a version ${RENDER_PROGRAM_VERSION} program. Rebuild the frontend.`);
40
+ }
41
+ return value;
42
+ }
43
+ export function programUsesCsrf(program) {
44
+ return nodesUseCsrf(program.nodes);
45
+ }
46
+ export function executeRenderProgram(program, data, options = {}) {
47
+ const out = [];
48
+ run(program.nodes, data, [], options, out);
49
+ return out.join('');
50
+ }
51
+ function run(nodes, data, scopes, options, out) {
52
+ for (const node of nodes) {
53
+ if (typeof node === 'string') {
54
+ out.push(node);
55
+ continue;
56
+ }
57
+ switch (node.op) {
58
+ case 'csrf':
59
+ if (options.csrfToken === false) {
60
+ break;
61
+ }
62
+ if (!options.csrfToken) {
63
+ throw new Error('A POST form needs a CSRF token, but none was supplied.');
64
+ }
65
+ out.push(`<input type="hidden" name="${RENDER_CSRF_FIELD}" value="${escapeAttribute(options.csrfToken)}">`);
66
+ break;
67
+ case 'text': {
68
+ const value = read(node.path, data, scopes);
69
+ if (value !== null && value !== undefined) {
70
+ out.push(escapeText(toText(value, node.loc, `data-text="${node.path.source}"`)));
71
+ }
72
+ break;
73
+ }
74
+ case 'attr': {
75
+ const value = read(node.path, data, scopes);
76
+ // ARIA states are the words "true" and "false", not an attribute's presence.
77
+ if (typeof value === 'boolean' && ARIA_TRUE_FALSE.has(node.name.toLowerCase())) {
78
+ out.push(` ${node.name}="${value}"`);
79
+ }
80
+ else if (value === true) {
81
+ out.push(` ${node.name}`);
82
+ }
83
+ else if (value !== false && value !== null && value !== undefined) {
84
+ const label = `data-attr-${node.name}="${node.path.source}"`;
85
+ const text = toText(value, node.loc, label);
86
+ out.push(` ${node.name}="${escapeAttribute(node.url ? safeAttributeUrl(node.name, text) : text)}"`);
87
+ }
88
+ break;
89
+ }
90
+ case 'if':
91
+ if (isTruthy(read(node.path, data, scopes)) !== node.negate) {
92
+ run(node.body, data, scopes, options, out);
93
+ }
94
+ break;
95
+ case 'each': {
96
+ const items = read(node.path, data, scopes);
97
+ if (items === null || items === undefined) {
98
+ break;
99
+ }
100
+ if (!Array.isArray(items)) {
101
+ throw new RenderProgramError(node.loc, `data-each="${node.path.source} as ${node.as}" needs an array, got ${describe(items)}`);
102
+ }
103
+ for (const item of items) {
104
+ run(node.body, data, [...scopes, item], options, out);
105
+ }
106
+ break;
107
+ }
108
+ }
109
+ }
110
+ }
111
+ function read(path, data, scopes) {
112
+ let current = path.scope === -1 ? data : scopes[path.scope];
113
+ for (const key of path.keys) {
114
+ if (current === null || typeof current !== 'object' || !Object.hasOwn(current, key)) {
115
+ return undefined;
116
+ }
117
+ current = current[key];
118
+ }
119
+ return current;
120
+ }
121
+ function isTruthy(value) {
122
+ if (Array.isArray(value)) {
123
+ return value.length > 0;
124
+ }
125
+ return Boolean(value);
126
+ }
127
+ function toText(value, loc, label) {
128
+ if (typeof value === 'string') {
129
+ return value;
130
+ }
131
+ if (typeof value === 'number' || typeof value === 'bigint') {
132
+ return String(value);
133
+ }
134
+ throw new RenderProgramError(loc, `${label} needs a string or number, got ${describe(value)}`);
135
+ }
136
+ /** Every URL in the value is checked: `srcset` lists candidates, `ping` lists URLs. */
137
+ function safeAttributeUrl(name, value) {
138
+ if (name === 'srcset' || name === 'imagesrcset') {
139
+ return value
140
+ .split(',')
141
+ .map((candidate) => {
142
+ const [url = '', ...descriptors] = candidate.trim().split(/\s+/);
143
+ return [safeUrl(url), ...descriptors].join(' ');
144
+ })
145
+ .join(', ');
146
+ }
147
+ if (name === 'ping') {
148
+ return value.split(/\s+/).filter(Boolean).map(safeUrl).join(' ');
149
+ }
150
+ return safeUrl(value);
151
+ }
152
+ /**
153
+ * Checks a loader's data against its view schema and supplies `flash`, which the framework
154
+ * owns: the session's messages come first, then any the loader returned. Whether the schema
155
+ * wants `flash` can depend on the branch a union picks, so the data is parsed with it and,
156
+ * failing that, without it, starting with what the schema declares.
157
+ */
158
+ export function prepareViewData(schema, loaded, flash) {
159
+ const withFlash = mergeFlash(loaded, flash);
160
+ if (!schema || typeof schema.safeParse !== 'function') {
161
+ return { ok: true, data: withFlash };
162
+ }
163
+ const attempts = schemaDeclaresField(schema, 'flash') ? [withFlash, loaded] : [loaded, withFlash];
164
+ let firstError;
165
+ for (const [index, input] of attempts.entries()) {
166
+ const parsed = schema.safeParse(input);
167
+ if (parsed.success) {
168
+ return { ok: true, data: ensureFlash(parsed.data, flash) };
169
+ }
170
+ if (index === 0) {
171
+ firstError = parsed.error;
172
+ }
173
+ }
174
+ return { ok: false, error: describeSchemaError(firstError) };
175
+ }
176
+ function mergeFlash(value, flash) {
177
+ if (!isPlainObject(value)) {
178
+ return value;
179
+ }
180
+ const own = value.flash;
181
+ if (own !== undefined && !Array.isArray(own)) {
182
+ return value;
183
+ }
184
+ return { ...value, flash: [...flash, ...(own ?? [])] };
185
+ }
186
+ function ensureFlash(value, flash) {
187
+ return isPlainObject(value) && !('flash' in value) ? { ...value, flash: [...flash] } : value;
188
+ }
189
+ function isPlainObject(value) {
190
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
191
+ }
192
+ function describeSchemaError(error) {
193
+ const issues = error
194
+ ?.issues;
195
+ if (!Array.isArray(issues) || issues.length === 0) {
196
+ return error instanceof Error ? error.message : String(error);
197
+ }
198
+ return issues
199
+ .map((issue) => `${(issue.path ?? []).join('.') || '(root)'}: ${issue.message ?? 'invalid'}`)
200
+ .join('; ');
201
+ }
202
+ /**
203
+ * Whether a zod-style schema's underlying object declares `field`, looking through wrappers
204
+ * such as optional, default, refine and transform, and into unions and intersections. A schema
205
+ * with no object inside declares nothing.
206
+ */
207
+ export function schemaDeclaresField(schema, field) {
208
+ return declares(schema, field, new Set());
209
+ }
210
+ function declares(schema, field, seen) {
211
+ if (!schema || typeof schema !== 'object' || seen.has(schema)) {
212
+ return false;
213
+ }
214
+ seen.add(schema);
215
+ const shape = schema.shape;
216
+ if (shape && typeof shape === 'object') {
217
+ return field in shape;
218
+ }
219
+ const def = schema._def;
220
+ if (!def) {
221
+ return false;
222
+ }
223
+ const inner = [def.innerType, def.schema, def.in, def.type, def.left, def.right];
224
+ if (typeof def.getter === 'function') {
225
+ inner.push(def.getter());
226
+ }
227
+ if (Array.isArray(def.options)) {
228
+ inner.push(...def.options);
229
+ }
230
+ return inner.some((candidate) => declares(candidate, field, seen));
231
+ }
232
+ function safeUrl(value) {
233
+ const normalized = Array.from(value)
234
+ .filter((character) => {
235
+ const code = character.charCodeAt(0);
236
+ return code > 0x20 && code !== 0x7f;
237
+ })
238
+ .join('')
239
+ .toLowerCase();
240
+ const scheme = /^([a-z][a-z0-9+.-]*):/.exec(normalized);
241
+ if (scheme && !SAFE_URL_SCHEMES.has(scheme[1])) {
242
+ return BLOCKED_URL;
243
+ }
244
+ return value;
245
+ }
246
+ function nodesUseCsrf(nodes) {
247
+ return nodes.some((node) => typeof node !== 'string' &&
248
+ (node.op === 'csrf' || ((node.op === 'if' || node.op === 'each') && nodesUseCsrf(node.body))));
249
+ }
250
+ function describe(value) {
251
+ if (Array.isArray(value)) {
252
+ return 'an array';
253
+ }
254
+ if (value === null) {
255
+ return 'null';
256
+ }
257
+ return typeof value === 'object' ? 'an object' : `a ${typeof value}`;
258
+ }
259
+ function escapeText(value) {
260
+ return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
261
+ }
262
+ function escapeAttribute(value) {
263
+ return value
264
+ .replaceAll('&', '&amp;')
265
+ .replaceAll('"', '&quot;')
266
+ .replaceAll("'", '&#39;')
267
+ .replaceAll('<', '&lt;')
268
+ .replaceAll('>', '&gt;');
269
+ }
@@ -0,0 +1,95 @@
1
+ import { z } from 'zod';
2
+ export declare const RENDER_PROGRAM_VERSION = 1;
3
+ export declare const RENDER_PROGRAM_FILE = "index.program.json";
4
+ export declare const renderSourceLocationSchema: z.ZodObject<{
5
+ file: z.ZodString;
6
+ line: z.ZodNumber;
7
+ }, "strip", z.ZodTypeAny, {
8
+ file: string;
9
+ line: number;
10
+ }, {
11
+ file: string;
12
+ line: number;
13
+ }>;
14
+ export type RenderSourceLocation = z.infer<typeof renderSourceLocationSchema>;
15
+ export declare const renderPathSchema: z.ZodObject<{
16
+ source: z.ZodString;
17
+ scope: z.ZodNumber;
18
+ keys: z.ZodArray<z.ZodString, "many">;
19
+ }, "strip", z.ZodTypeAny, {
20
+ source: string;
21
+ scope: number;
22
+ keys: string[];
23
+ }, {
24
+ source: string;
25
+ scope: number;
26
+ keys: string[];
27
+ }>;
28
+ export type RenderPath = z.infer<typeof renderPathSchema>;
29
+ export type RenderNode = string | {
30
+ readonly op: 'text';
31
+ readonly path: RenderPath;
32
+ readonly loc: RenderSourceLocation;
33
+ } | {
34
+ readonly op: 'attr';
35
+ readonly name: string;
36
+ readonly url: boolean;
37
+ readonly path: RenderPath;
38
+ readonly loc: RenderSourceLocation;
39
+ } | {
40
+ readonly op: 'if';
41
+ readonly negate: boolean;
42
+ readonly path: RenderPath;
43
+ readonly loc: RenderSourceLocation;
44
+ readonly body: readonly RenderNode[];
45
+ } | {
46
+ readonly op: 'each';
47
+ readonly as: string;
48
+ readonly path: RenderPath;
49
+ readonly loc: RenderSourceLocation;
50
+ readonly body: readonly RenderNode[];
51
+ } | {
52
+ readonly op: 'csrf';
53
+ };
54
+ export declare const renderNodeSchema: z.ZodType<RenderNode>;
55
+ export declare const renderProgramSchema: z.ZodObject<{
56
+ version: z.ZodLiteral<1>;
57
+ page: z.ZodString;
58
+ source: z.ZodString;
59
+ bindings: z.ZodNumber;
60
+ nodes: z.ZodArray<z.ZodType<RenderNode, z.ZodTypeDef, RenderNode>, "many">;
61
+ }, "strip", z.ZodTypeAny, {
62
+ version: 1;
63
+ page: string;
64
+ source: string;
65
+ bindings: number;
66
+ nodes: RenderNode[];
67
+ }, {
68
+ version: 1;
69
+ page: string;
70
+ source: string;
71
+ bindings: number;
72
+ nodes: RenderNode[];
73
+ }>;
74
+ export type RenderProgram = z.infer<typeof renderProgramSchema>;
75
+ export declare const renderFlashMessageSchema: z.ZodObject<{
76
+ level: z.ZodEnum<["info", "success", "warning", "error"]>;
77
+ message: z.ZodString;
78
+ }, "strip", z.ZodTypeAny, {
79
+ level: "error" | "info" | "success" | "warning";
80
+ message: string;
81
+ }, {
82
+ level: "error" | "info" | "success" | "warning";
83
+ message: string;
84
+ }>;
85
+ export type RenderFlashMessage = z.infer<typeof renderFlashMessageSchema>;
86
+ export declare const renderFlashSchema: z.ZodArray<z.ZodObject<{
87
+ level: z.ZodEnum<["info", "success", "warning", "error"]>;
88
+ message: z.ZodString;
89
+ }, "strip", z.ZodTypeAny, {
90
+ level: "error" | "info" | "success" | "warning";
91
+ message: string;
92
+ }, {
93
+ level: "error" | "info" | "success" | "warning";
94
+ message: string;
95
+ }>, "many">;
@@ -0,0 +1,50 @@
1
+ import { z } from 'zod';
2
+ export const RENDER_PROGRAM_VERSION = 1;
3
+ export const RENDER_PROGRAM_FILE = 'index.program.json';
4
+ export const renderSourceLocationSchema = z.object({
5
+ file: z.string().min(1),
6
+ line: z.number().int().positive(),
7
+ });
8
+ export const renderPathSchema = z.object({
9
+ source: z.string().min(1),
10
+ scope: z.number().int().min(-1),
11
+ keys: z.array(z.string().min(1)),
12
+ });
13
+ export const renderNodeSchema = z.lazy(() => z.union([
14
+ z.string(),
15
+ z.object({ op: z.literal('text'), path: renderPathSchema, loc: renderSourceLocationSchema }),
16
+ z.object({
17
+ op: z.literal('attr'),
18
+ name: z.string().min(1),
19
+ url: z.boolean(),
20
+ path: renderPathSchema,
21
+ loc: renderSourceLocationSchema,
22
+ }),
23
+ z.object({
24
+ op: z.literal('if'),
25
+ negate: z.boolean(),
26
+ path: renderPathSchema,
27
+ loc: renderSourceLocationSchema,
28
+ body: z.array(renderNodeSchema),
29
+ }),
30
+ z.object({
31
+ op: z.literal('each'),
32
+ as: z.string().min(1),
33
+ path: renderPathSchema,
34
+ loc: renderSourceLocationSchema,
35
+ body: z.array(renderNodeSchema),
36
+ }),
37
+ z.object({ op: z.literal('csrf') }),
38
+ ]));
39
+ export const renderProgramSchema = z.object({
40
+ version: z.literal(RENDER_PROGRAM_VERSION),
41
+ page: z.string().min(1),
42
+ source: z.string().min(1),
43
+ bindings: z.number().int().min(0),
44
+ nodes: z.array(renderNodeSchema),
45
+ });
46
+ export const renderFlashMessageSchema = z.object({
47
+ level: z.enum(['info', 'success', 'warning', 'error']),
48
+ message: z.string(),
49
+ });
50
+ export const renderFlashSchema = z.array(renderFlashMessageSchema);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webstir-io/module-contract",
3
- "version": "0.1.69",
3
+ "version": "0.3.0",
4
4
  "description": "Public contract definitions for Webstir frontend and backend module providers.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,6 +11,11 @@
11
11
  "import": "./dist/index.js",
12
12
  "default": "./dist/index.js"
13
13
  },
14
+ "./client-nav": {
15
+ "types": "./dist/client-nav.d.ts",
16
+ "import": "./dist/client-nav.js",
17
+ "default": "./dist/client-nav.js"
18
+ },
14
19
  "./ts-rest": {
15
20
  "types": "./dist/adapters/ts-rest.d.ts",
16
21
  "import": "./dist/adapters/ts-rest.js",
@@ -174,6 +174,10 @@
174
174
  "error"
175
175
  ]
176
176
  },
177
+ "message": {
178
+ "type": "string",
179
+ "minLength": 1
180
+ },
177
181
  "when": {
178
182
  "type": "string",
179
183
  "enum": [
@@ -103,6 +103,10 @@
103
103
  "error"
104
104
  ]
105
105
  },
106
+ "message": {
107
+ "type": "string",
108
+ "minLength": 1
109
+ },
106
110
  "when": {
107
111
  "type": "string",
108
112
  "enum": [