@object-ui/plugin-form 17.6.0 → 18.0.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +4991 -0
  2. package/README.md +522 -80
  3. package/dist/DrawerForm.d.ts +42 -3
  4. package/dist/DrawerForm.d.ts.map +1 -1
  5. package/dist/EmbeddableForm.d.ts.map +1 -1
  6. package/dist/FormAnalytics.d.ts.map +1 -1
  7. package/dist/LineItemsPanel.d.ts +6 -3
  8. package/dist/LineItemsPanel.d.ts.map +1 -1
  9. package/dist/MasterDetailForm.d.ts +79 -38
  10. package/dist/MasterDetailForm.d.ts.map +1 -1
  11. package/dist/ModalForm.d.ts +60 -5
  12. package/dist/ModalForm.d.ts.map +1 -1
  13. package/dist/ObjectForm.d.ts.map +1 -1
  14. package/dist/SplitForm.d.ts +49 -3
  15. package/dist/SplitForm.d.ts.map +1 -1
  16. package/dist/TabbedForm.d.ts +75 -5
  17. package/dist/TabbedForm.d.ts.map +1 -1
  18. package/dist/WizardForm.d.ts +145 -38
  19. package/dist/WizardForm.d.ts.map +1 -1
  20. package/dist/autoLayout.d.ts +113 -8
  21. package/dist/autoLayout.d.ts.map +1 -1
  22. package/dist/closedAffordanceNotice.d.ts +14 -0
  23. package/dist/closedAffordanceNotice.d.ts.map +1 -0
  24. package/dist/customFieldsMerge.d.ts +29 -0
  25. package/dist/customFieldsMerge.d.ts.map +1 -0
  26. package/dist/deriveMasterDetail.d.ts +82 -29
  27. package/dist/deriveMasterDetail.d.ts.map +1 -1
  28. package/dist/fieldGroups.d.ts +189 -0
  29. package/dist/fieldGroups.d.ts.map +1 -1
  30. package/dist/fieldWriteGate.d.ts +136 -0
  31. package/dist/fieldWriteGate.d.ts.map +1 -0
  32. package/dist/flatFields.d.ts +11 -2
  33. package/dist/flatFields.d.ts.map +1 -1
  34. package/dist/formChrome.d.ts +55 -0
  35. package/dist/formChrome.d.ts.map +1 -0
  36. package/dist/index.d.ts +149 -26
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +3984 -2332
  39. package/dist/index.umd.cjs +1 -1
  40. package/dist/initialFieldFocus.d.ts +23 -0
  41. package/dist/initialFieldFocus.d.ts.map +1 -0
  42. package/dist/initialRecord.d.ts +72 -0
  43. package/dist/initialRecord.d.ts.map +1 -0
  44. package/dist/loadFailure.d.ts +28 -0
  45. package/dist/loadFailure.d.ts.map +1 -0
  46. package/dist/masterDetailTx.d.ts.map +1 -1
  47. package/dist/occSave.d.ts +6 -1
  48. package/dist/occSave.d.ts.map +1 -1
  49. package/dist/recordInvalidation.d.ts +33 -0
  50. package/dist/recordInvalidation.d.ts.map +1 -0
  51. package/dist/recordReadGate.d.ts +34 -0
  52. package/dist/recordReadGate.d.ts.map +1 -0
  53. package/dist/sanitize.d.ts +272 -5
  54. package/dist/sanitize.d.ts.map +1 -1
  55. package/dist/schemaDefaults.d.ts +104 -9
  56. package/dist/schemaDefaults.d.ts.map +1 -1
  57. package/dist/sectionFields.d.ts +67 -2
  58. package/dist/sectionFields.d.ts.map +1 -1
  59. package/dist/sectionGroups.d.ts +62 -0
  60. package/dist/sectionGroups.d.ts.map +1 -0
  61. package/dist/sectionPredicateDiagnostic.d.ts +38 -0
  62. package/dist/sectionPredicateDiagnostic.d.ts.map +1 -0
  63. package/dist/submitTarget.d.ts +136 -0
  64. package/dist/submitTarget.d.ts.map +1 -0
  65. package/dist/successBehavior.d.ts +44 -25
  66. package/dist/successBehavior.d.ts.map +1 -1
  67. package/dist/thankYouRedirectCountdown.d.ts +1 -1
  68. package/dist/uploadGate.d.ts +35 -0
  69. package/dist/uploadGate.d.ts.map +1 -0
  70. package/dist/writePayload.d.ts +87 -0
  71. package/dist/writePayload.d.ts.map +1 -0
  72. package/package.json +13 -12
@@ -1,14 +1,75 @@
1
1
  import { isRuntimeDefault } from '@object-ui/core';
2
2
  export { isRuntimeDefault };
3
- /** An object schema as the data source serves it (`{ fields: { [name]: def } }`). */
4
- interface ObjectSchemaLike {
3
+ /**
4
+ * An object schema as the data source serves it (`{ fields: { [name]: def } }`),
5
+ * narrowed to `defaultValue` and the two members the `current_user` seed reads
6
+ * (`type`, `reference`). The keys the option-list fallback reads
7
+ * (objectui#11914) sit on the package-local `SeedFieldLike` below instead: the
8
+ * one published function that reads them, `optionListDefault`, names its own
9
+ * `OptionListFieldLike` (objectui#13049), and every member added here is
10
+ * published surface somebody then has to keep.
11
+ *
12
+ * Exported and published (objectui#7324) because `omitServerResolvedDefaults`
13
+ * is published and this is its second parameter: a host with its own form
14
+ * renderer has to hold that schema in a variable or a prop, and until now it
15
+ * could not name the variable's type. Structural typing meant such a host
16
+ * still compiled by writing the shape out by hand — which is a copy of a
17
+ * producer-owned shape in every consumer, invisible to every gate until the
18
+ * producer's shape moves.
19
+ *
20
+ * `FieldDefaults`, not `ObjectSchema`: `deriveMasterDetail.ts` holds a
21
+ * different shape that used to carry the same `ObjectSchemaLike` name, and the
22
+ * two are NOT interchangeable — see `ChildObjectSchemaLike` there. This one is
23
+ * the stricter of the pair (its field values are pinned, not `any`), so a
24
+ * value legal here is legal there but not the reverse.
25
+ *
26
+ * NOT `@object-ui/types`' `ObjectSchemaMetadata`: that type requires `name`
27
+ * and requires a `type` on every field. `isCurrentUserSeedField` below reads
28
+ * the target as `reference`, the one spelling the spec declares and ObjectUI
29
+ * reads (objectui#11070 round 4).
30
+ */
31
+ export interface FieldDefaultsSchemaLike {
5
32
  fields?: Record<string, {
6
33
  defaultValue?: unknown;
7
34
  type?: unknown;
8
35
  reference?: unknown;
9
- reference_to?: unknown;
10
36
  } | undefined>;
11
37
  }
38
+ /**
39
+ * One field as the SEEDING half of this module reads it: the published
40
+ * {@link FieldDefaultsSchemaLike} member set plus the two keys the option-list
41
+ * fallback reads (objectui#11914) — `options`, and `multiple` for the arity.
42
+ *
43
+ * Package-local, and derived from the published type rather than restating
44
+ * it: seeding is not on the package entry (`schemaDefaultValues` and
45
+ * `seedCreateValues` are withheld, see `index.tsx`), so the keys only it reads
46
+ * stay off the published shape, whose members are pinned to what the
47
+ * published pair reads (`parameterTypesNameable-7324.test.ts`). Every value of
48
+ * the published type is still a value of this one.
49
+ */
50
+ type SeedFieldLike = NonNullable<NonNullable<FieldDefaultsSchemaLike['fields']>[string]> & {
51
+ options?: unknown;
52
+ multiple?: unknown;
53
+ };
54
+ /**
55
+ * One field as {@link optionListDefault} reads it: the option list and the two
56
+ * arity inputs, nothing else (objectui#13049).
57
+ *
58
+ * Published because `optionListDefault` is, and a consumer must be able to
59
+ * name what it passes (objectui#7324). Its own shape rather than a widening of
60
+ * {@link FieldDefaultsSchemaLike}, whose members are pinned to what the
61
+ * published `omitServerResolvedDefaults` / `isRequiredInForm` pair reads. A
62
+ * served object field is assignable to it as it arrives.
63
+ */
64
+ export interface OptionListFieldLike {
65
+ type?: unknown;
66
+ multiple?: unknown;
67
+ options?: unknown;
68
+ }
69
+ /** An object schema as the seeding half reads it — see {@link SeedFieldLike}. */
70
+ interface SeedSchemaLike {
71
+ fields?: Record<string, SeedFieldLike | undefined>;
72
+ }
12
73
  /**
13
74
  * The session facts create-form seeding may draw on (#5683). Callers thread it
14
75
  * from `usePermissions()`; every key is optional so existing call sites keep
@@ -76,8 +137,10 @@ export declare function isCreateFormMode(form: {
76
137
  *
77
138
  * Note this drops the required MARKER (and `aria-required`) too, since both are
78
139
  * driven by this one boolean — which is the honest reading: in create mode the
79
- * user really is not required to provide the value. Surfacing what the server
80
- * WILL supply is issue #4069's option B, a separate follow-up card.
140
+ * user really is not required to provide the value. The form renderer says
141
+ * THAT the server supplies it — "Set automatically when saved." under the
142
+ * empty control (objectui#12108), keyed on the same classifier. Previewing
143
+ * WHAT it will supply is issue #4069's option B, still a separate card.
81
144
  *
82
145
  * The CONDITIONAL spelling (`requiredWhen`) reaches the same verdict, ruled
83
146
  * identically in #4085 — but it cannot be decided here, because it is resolved
@@ -95,12 +158,44 @@ export declare function isRequiredInForm(field: {
95
158
  defaultValue?: unknown;
96
159
  } | null | undefined, isCreateForm: boolean): boolean;
97
160
  /**
98
- * The static `defaultValue`s an object schema declares, as a form-values patch.
161
+ * The static defaults an object schema declares, as a form-values patch: each
162
+ * field's `defaultValue`, or — for a field that declares none — the option its
163
+ * option list marks `default: true` ({@link optionListDefault}).
99
164
  *
100
165
  * Returns a fresh object (never shared), and `{}` for a missing/!object schema
101
166
  * so callers can spread it unconditionally.
102
167
  */
103
- export declare function schemaDefaultValues(objectSchema: ObjectSchemaLike | null | undefined, ctx?: SeedContext): Record<string, unknown>;
168
+ export declare function schemaDefaultValues(objectSchema: SeedSchemaLike | null | undefined, ctx?: SeedContext): Record<string, unknown>;
169
+ /**
170
+ * The value a field's option list declares as its initial value — the option
171
+ * marked `default: true` — or `undefined` when it marks none (objectui#11914).
172
+ *
173
+ * The client reading of `ObjectQL.resolveOptionDefault` (objectstack#7246),
174
+ * rule for rule, so a create form preselects exactly what omitting the field
175
+ * would store:
176
+ *
177
+ * - **Only `default === true`, on the canonical key.** The authoring aliases
178
+ * (`isDefault`, `selected`) are normalized by `SelectOptionSchema` before
179
+ * metadata is served, and the engine reads the canonical key alone.
180
+ * - **A marked option with no value declares nothing** and is skipped.
181
+ * - **Single-valued: the FIRST marked option wins** — the schema does not
182
+ * dedupe the flag, so declaration order decides.
183
+ * - **Multi-valued: every marked option, as an array** — even one, since the
184
+ * field stores an array. Arity is `@objectstack/spec`'s `isMultiValueField`,
185
+ * the one predicate the engine asks too (`declaredMultiValued`), ⛔ never a
186
+ * raw `multiple` read: `multiselect` stores an array without the flag.
187
+ *
188
+ * Not gated on `type: 'select'`, as the engine's is not: `options` is a
189
+ * field-level key, and a field the server defaults must not open empty here.
190
+ *
191
+ * Applies only to a field that declares no `defaultValue` — the caller's gate,
192
+ * not this function's: {@link schemaDefaultValues} reaches it through
193
+ * `dv == null`, the engine's own presence test, and so must every other
194
+ * reader. Published on the package entry for the SECOND form renderer, the
195
+ * console's public form page (objectui#13049), so both renderers seed from
196
+ * this one reading instead of two.
197
+ */
198
+ export declare function optionListDefault(field: OptionListFieldLike | null | undefined): unknown;
104
199
  /**
105
200
  * The initial values a CREATE form opens with: the schema's static defaults,
106
201
  * overlaid with whatever the caller supplied.
@@ -110,7 +205,7 @@ export declare function schemaDefaultValues(objectSchema: ObjectSchemaLike | nul
110
205
  * carrying state forward), and an explicit `null` from a caller is a real
111
206
  * "leave this blank", not an absence.
112
207
  */
113
- export declare function seedCreateValues(objectSchema: ObjectSchemaLike | null | undefined, initial?: Record<string, unknown> | null, ctx?: SeedContext): Record<string, unknown>;
208
+ export declare function seedCreateValues(objectSchema: SeedSchemaLike | null | undefined, initial?: Record<string, unknown> | null, ctx?: SeedContext): Record<string, unknown>;
114
209
  /**
115
210
  * Drop the fields a CREATE payload must leave to the producer (#4069).
116
211
  *
@@ -138,5 +233,5 @@ export declare function seedCreateValues(objectSchema: ObjectSchemaLike | null |
138
233
  * column there is a deliberate removal, and dropping the key would silently
139
234
  * discard the user's edit.
140
235
  */
141
- export declare function omitServerResolvedDefaults(data: Record<string, unknown>, objectSchema: ObjectSchemaLike | null | undefined): Record<string, unknown>;
236
+ export declare function omitServerResolvedDefaults(data: Record<string, unknown>, objectSchema: FieldDefaultsSchemaLike | null | undefined): Record<string, unknown>;
142
237
  //# sourceMappingURL=schemaDefaults.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schemaDefaults.d.ts","sourceRoot":"","sources":["../src/schemaDefaults.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AAEH,OAAO,EAAwB,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAQzE,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,qFAAqF;AACrF,UAAU,gBAAgB;IACxB,MAAM,CAAC,EAAE,MAAM,CACb,MAAM,EACN;QAAE,YAAY,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CACpG,CAAC;CACH;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAGrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GACpE,OAAO,CAET;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACxE,YAAY,EAAE,OAAO,GACpB,OAAO,CAIT;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EACjD,GAAG,CAAC,EAAE,WAAW,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyBzB;AAqBD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EACjD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACxC,GAAG,CAAC,EAAE,WAAW,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,YAAY,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAChD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB"}
1
+ {"version":3,"file":"schemaDefaults.d.ts","sourceRoot":"","sources":["../src/schemaDefaults.ts"],"names":[],"mappings":"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"}
@@ -26,7 +26,57 @@ export interface SectionFieldsContext {
26
26
  * passes it.
27
27
  */
28
28
  fieldLabel: (objectName: string, fieldName: string, fallback: string) => string;
29
+ /**
30
+ * The authored inline members (`schema.customFields`). A member naming the
31
+ * field a section entry names is that entry's BASE definition, in place of
32
+ * the one generated from the object schema — the precedence `ObjectForm`'s
33
+ * default arm resolves its section members with (`findCustomFieldMember`,
34
+ * objectui#10254). Only the base moves: the entry's own overrides still
35
+ * apply on top, by the rules below. Omitted or empty → every base is
36
+ * generated, as before.
37
+ */
38
+ customFields?: readonly FormField[] | null;
39
+ /**
40
+ * The parent field POOL the section resolves its members against, for the
41
+ * one caller that has one: `ObjectForm`'s default arm (`SimpleObjectForm`),
42
+ * whose pool is built from top-level `fields` (or the object) merged with
43
+ * `customFields` (objectui#10475).
44
+ *
45
+ * When set, {@link buildSectionFields} still walks the section's entries in
46
+ * AUTHORED order and applies each entry's overrides by the same rules as
47
+ * every other arm; for a NAME-ONLY entry — shape (1) or (2), which names a
48
+ * field and leaves its definition to be resolved — two things change, both
49
+ * the pool's to decide:
50
+ *
51
+ * - membership — an entry naming a field the pool does not hold is
52
+ * dropped. That is objectui#9884's INTERSECTION of `fields` and
53
+ * `sections`; the caller emits its warning
54
+ * (`warnSectionMemberExcludedByFields`), this module only drops;
55
+ * - the BASE — the pooled field is what a name string draws and what a
56
+ * spec entry's overrides are written onto (a copy), in place of
57
+ * `fromObjectSchema` / the member lookup. The pool already resolved the
58
+ * member-over-generated precedence, and its generated fields carry the
59
+ * default arm's own per-field facts (the lock on a computed `formula` /
60
+ * `summary` / `auto_number` field, the input type, the step), which only
61
+ * LAYOUT and per-entry overrides may move. The managed-object lock is
62
+ * not one of them: every arm applies it after this builder, in
63
+ * `gateFormFields` (objectui#10612).
64
+ *
65
+ * An already-built runtime FormField entry (shape 3) carrying its own
66
+ * `name` is its own definition, and the pool decides NOTHING about it: it is
67
+ * drawn whatever the pool holds, exactly as the pool-less arms draw it
68
+ * (objectui#11615). The intersection catches a NAME the object does not
69
+ * declare or `fields` does not list — a typo, a stale name — and an entry
70
+ * that declares itself names nothing to resolve, so dropping it guarded
71
+ * nothing and made this arm the one that skipped what the other five drew.
72
+ * "Self-describing" is `isInlineFieldDef` (`submitTarget.ts`), the one
73
+ * predicate the submit-target rule already reads.
74
+ * Omitted → no pool: every entry is drawn, from the bases above.
75
+ */
76
+ pool?: readonly FormField[] | null;
29
77
  }
78
+ export declare function warnUnresolvedTopLevelField(entry: unknown, objectName: string): void;
79
+ export declare function warnSectionMemberExcludedByFields(memberName: string, objectName: string, sectionLabel?: string): void;
30
80
  /**
31
81
  * Build a runtime FormField from object-schema metadata for `fieldName`.
32
82
  *
@@ -36,13 +86,28 @@ export interface SectionFieldsContext {
36
86
  * costs.
37
87
  */
38
88
  export declare function fromObjectSchema(fieldName: string, ctx: SectionFieldsContext): FormField;
89
+ /**
90
+ * The field name a section entry resolves to — the identity rule of the three
91
+ * shapes above, spelled once: a string is the name; a spec entry's identity is
92
+ * its `field` STRING; an already-built runtime FormField's is its `name`.
93
+ * `undefined` when the entry names nothing.
94
+ */
95
+ export declare function sectionEntryName(fieldDef: unknown): string | undefined;
39
96
  /**
40
97
  * Normalize one section field definition (string | spec object | runtime
41
98
  * FormField) into a runtime FormField with a guaranteed `name`.
42
99
  */
43
100
  export declare function normalizeSectionField(fieldDef: string | Record<string, any>, ctx: SectionFieldsContext): FormField;
44
- /** Normalize every field def in a section. */
101
+ /**
102
+ * Normalize every field def in a section, in the section's AUTHORED order.
103
+ *
104
+ * With a {@link SectionFieldsContext.pool}, a name-only entry naming a field
105
+ * the pool does not hold is dropped, and a pooled field is the base the entry
106
+ * starts from; a self-describing inline entry is drawn as it stands, pooled
107
+ * or not (objectui#11615); the order stays the section's own either way
108
+ * (objectui#10475).
109
+ */
45
110
  export declare function buildSectionFields(section: {
46
- fields: Array<string | Record<string, any>>;
111
+ fields?: Array<string | Record<string, any>>;
47
112
  }, ctx: SectionFieldsContext): FormField[];
48
113
  //# sourceMappingURL=sectionFields.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sectionFields.d.ts","sourceRoot":"","sources":["../src/sectionFields.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAIlD,MAAM,WAAW,oBAAoB;IACnC,2EAA2E;IAC3E,YAAY,EAAE,GAAG,CAAC;IAClB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sDAAsD;IACtD,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CACjF;AA2CD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,SAAS,CAgCxF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACtC,GAAG,EAAE,oBAAoB,GACxB,SAAS,CA2FX;AAED,8CAA8C;AAC9C,wBAAgB,kBAAkB,CAChC,OAAO,EAAE;IAAE,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;CAAE,EACxD,GAAG,EAAE,oBAAoB,GACxB,SAAS,EAAE,CAEb"}
1
+ {"version":3,"file":"sectionFields.d.ts","sourceRoot":"","sources":["../src/sectionFields.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAOlD,MAAM,WAAW,oBAAoB;IACnC,2EAA2E;IAC3E,YAAY,EAAE,GAAG,CAAC;IAClB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sDAAsD;IACtD,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAChF;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAAC;IAC3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoCG;IACH,IAAI,CAAC,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAAC;CACpC;AA0FD,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAepF;AAkCD,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,GACpB,IAAI,CAaN;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,SAAS,CAmCxF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMtE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACtC,GAAG,EAAE,oBAAoB,GACxB,SAAS,CAEX;AA+HD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;CAAE,EACzD,GAAG,EAAE,oBAAoB,GACxB,SAAS,EAAE,CAyBb"}
@@ -0,0 +1,62 @@
1
+ import { ObjectFormSection } from '@object-ui/types';
2
+ /**
3
+ * The keys `@objectstack/spec` REFUSES beside `group`, measured against
4
+ * `FormSectionSchema` in `@objectstack/spec` 17.3.0 and pinned in
5
+ * `__tests__/formSectionGroupReference-7051.test.tsx`.
6
+ *
7
+ * There are no override semantics: each of these is something the object's
8
+ * `fieldGroups` entry declares, so restating one on the referencing section
9
+ * would be a second writable spelling of one fact. Authored metadata carrying
10
+ * one never reaches this renderer (the spec door rejects it); a programmatic
11
+ * SDUI caller, which does not pass that door, is reported instead of quietly
12
+ * having its key honoured or quietly having it dropped.
13
+ */
14
+ export declare const GROUP_OWNED_SECTION_KEYS: readonly ["name", "label", "description", "collapsible", "collapsed", "visibleWhen"];
15
+ /**
16
+ * The keys the spec PERMITS beside `group`. They describe how THIS form lays
17
+ * the section out, which is the form's business and not the group's, so the
18
+ * authored value wins over the derived section.
19
+ */
20
+ export declare const SECTION_LAYOUT_KEYS: readonly ["columns", "pane"];
21
+ /** Test seam — forget which section-group diagnostics have been reported. */
22
+ export declare function resetSectionGroupReports(): void;
23
+ /** Does any section in this list use the reference form? */
24
+ export declare function hasSectionGroupReference(sections: unknown): boolean;
25
+ export interface ResolveSectionGroupsOptions {
26
+ /** Object name — diagnostics say WHICH object is missing the group. */
27
+ objectName: string;
28
+ /** The form's layout. `wizard` is the one the spec bans `group` on. */
29
+ formType?: string;
30
+ /**
31
+ * The object definition (`{ fields, fieldGroups }`), or `null`/`undefined`
32
+ * while it is still loading. A `group` section is rendered EMPTY until it
33
+ * arrives — never dropped, see the note on the return value.
34
+ */
35
+ objectDef: any;
36
+ /**
37
+ * `false` when this form can never load an object definition (no
38
+ * DataSource), so an unresolvable reference is reported as such instead of
39
+ * being left to look like a load that never finished.
40
+ */
41
+ resolvable?: boolean;
42
+ }
43
+ /**
44
+ * Replace every `{ group }` section with the section that group declares.
45
+ *
46
+ * Returns the SAME array reference when nothing uses the reference form, so a
47
+ * caller can keep this in a `useMemo` without perturbing any existing form.
48
+ *
49
+ * ⚠️ A reference that cannot be resolved yields `{ fields: [] }` — an empty
50
+ * section — and NOT a dropped one. Both render nothing, which is what the spec
51
+ * assigns to an unresolvable reference (`deriveFieldGroupLayout` already drops
52
+ * a declared group nothing references, and `@objectstack/lint`'s
53
+ * `form-section-group-unknown` is the declared reporter of a dangling key).
54
+ * The choice between them is forced by THIS surface and was measured: a form
55
+ * whose `sections` array empties out stops being a sectioned form at all and
56
+ * falls back to the flat every-field layout, so dropping would make a single
57
+ * mistyped key render MORE than the author asked for. Emptying renders
58
+ * exactly nothing, and leaves the array length — which is the wizard's step
59
+ * count and the container routing's condition — untouched.
60
+ */
61
+ export declare function resolveSectionGroupReferences(sections: ObjectFormSection[] | undefined, opts: ResolveSectionGroupsOptions): ObjectFormSection[] | undefined;
62
+ //# sourceMappingURL=sectionGroups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sectionGroups.d.ts","sourceRoot":"","sources":["../src/sectionGroups.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,sFAO3B,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,8BAA+B,CAAC;AAWhE,6EAA6E;AAC7E,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,4DAA4D;AAC5D,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAKnE;AAcD,MAAM,WAAW,2BAA2B;IAC1C,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,SAAS,EAAE,GAAG,CAAC;IACf;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,iBAAiB,EAAE,GAAG,SAAS,EACzC,IAAI,EAAE,2BAA2B,GAChC,iBAAiB,EAAE,GAAG,SAAS,CAyEjC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /**
9
+ * The one layout arm that still DROPS an authored `FormSection.visibleWhen`
10
+ * (objectui#6237).
11
+ *
12
+ * Of the six layout routes in `ObjectForm`, five honour the predicate:
13
+ * `split` / `drawer` / `modal` rebuild each section key by key and copy it
14
+ * (#6111), the flat arm carries it on the `section-divider` pseudo-field
15
+ * (#6236), and `tabbed` joined them by copying it onto the tab `TabbedForm`
16
+ * synthesises, where the renderer evaluates it (#6237). `wizard` is the
17
+ * remainder.
18
+ *
19
+ * ⛔ The wizard's gap is a DESIGN boundary, not an oversight, and it is not one
20
+ * copy line away. A step predicate would be step-boundary reactive against the
21
+ * ruled live-record reactivity, and it needs navigation, indicator, final-gate
22
+ * and re-selection semantics that the tab arm's machinery does not supply —
23
+ * `WizardStepConfig` carries the full measurement. Until that contract is ruled,
24
+ * an author who writes the key on a wizard is told so rather than watching it do
25
+ * nothing.
26
+ *
27
+ * Since objectstack#13622 (ruled 2026-08-31) the boundary is ALSO enforced at
28
+ * the author door: `@objectstack/spec` refuses `visibleWhen` on a wizard step
29
+ * at parse, so authored form-view metadata can no longer arrive here carrying
30
+ * it. This report is deliberately KEPT (Card R of that ruling): programmatic
31
+ * SDUI callers do not pass the spec door, and metadata published before the
32
+ * tightening still round-trips — for both, this warning is the only signal.
33
+ *
34
+ * Single-sourced so the runtime report and the pin that holds its wording cannot
35
+ * drift apart.
36
+ */
37
+ export declare function sectionPredicateUnsupportedWarning(layout: 'wizard', sectionNames: string): string;
38
+ //# sourceMappingURL=sectionPredicateDiagnostic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sectionPredicateDiagnostic.d.ts","sourceRoot":"","sources":["../src/sectionPredicateDiagnostic.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,QAAQ,EAChB,YAAY,EAAE,MAAM,GACnB,MAAM,CASR"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /**
9
+ * Does a form have anything to submit TO? — the ONE answer for every renderer
10
+ * `ObjectForm` routes to (objectui#6300).
11
+ *
12
+ * ## What went wrong
13
+ *
14
+ * Each of the five variant containers opened `handleSubmit` with
15
+ *
16
+ * if (!dataSource) { await schema.onSuccess?.(data); return data; }
17
+ *
18
+ * — a SUCCESS signal emitted without consulting `submitHandler` and without
19
+ * persisting anything. Measured on `main` with a `submitHandler` supplied and no
20
+ * `dataSource`, all five read `onSuccess 1 / submitHandler 0`. Through
21
+ * `MasterDetailForm` — whose parent schema declares BOTH
22
+ * `submitHandler: submitViaBatch` and `onSuccess: handleSaved` — that early
23
+ * return reached `handleSaved` directly: a success toast (measured:
24
+ * `toast.success("Created")`) and, in create mode, a form reset clearing the
25
+ * line items nobody wrote. Silent data loss in the worst direction, because the
26
+ * submitter is told it worked.
27
+ *
28
+ * ## The rule this module carries
29
+ *
30
+ * A form has a submit target when it has EITHER a `dataSource` (it writes) or a
31
+ * declared `submitHandler` (the host writes — objectui#6176's seam, which needs
32
+ * no adapter of its own). With neither, exactly one shape is still legitimate:
33
+ * fields authored INLINE, where the author's `onSuccess` IS the write. Anything
34
+ * else must refuse, loudly, instead of confirming.
35
+ *
36
+ * ## What counts as inline, and why it has two limbs
37
+ *
38
+ * Neither limb is a new predicate. Both name a shape this package already
39
+ * documents as working with no adapter, and the test refuses everything else.
40
+ *
41
+ * **(a) `customFields` is non-empty.** `SimpleObjectForm` (`ObjectForm.tsx`)
42
+ * gates its own carve-out on exactly this, the `object-form` element gate
43
+ * (`index.tsx`) declares `requiresDataSource={!(schema?.customFields?.length > 0)
44
+ * && …}` and says so in as many words — *"The one escape hatch is inline
45
+ * `customFields`, which is exactly what `hasInlineFields` gates on inside the
46
+ * component, so the two stay in step"* — and `ObjectFormSchema` glosses the key
47
+ * as *"when used with inline field definitions (without dataSource), this
48
+ * becomes the primary field source"*.
49
+ *
50
+ * **(b) every section field is an inline runtime `FormField`.** This limb is not
51
+ * optional and not an invention: it is how a sectioned form expresses the same
52
+ * thing with no `customFields` at all — its sections carry the definitions.
53
+ * This package's README documents the shape and ships an example of
54
+ * it, `<WizardForm schema={wizard} />` under the comment *"dataSource omitted:
55
+ * every step lists inline fields"*, introduced by *"The inline shape is what
56
+ * lets a wizard run with no data source at all"*. Measured before this module
57
+ * existed: that example renders and submits. A `customFields`-only test would
58
+ * have made the repo's own published example throw — which is why the limb is
59
+ * here.
60
+ *
61
+ * The limb is deliberately ALL-or-nothing, and it is the taxonomy
62
+ * `normalizeSectionField` already keeps: a section field is a name string
63
+ * (shape 1), a spec `FormFieldSchema` whose identity key `field` is a STRING
64
+ * (shape 2), or an inline runtime `FormField` carrying its own `name` (shape 3).
65
+ * Only shape 3 is self-describing; shapes 1 and 2 name fields that only an
66
+ * object schema — and therefore only an adapter — can resolve. So one bare name
67
+ * anywhere in the sections means the form DID need metadata it could not get,
68
+ * and it refuses. Erring that way is the loud direction; the silent one is the
69
+ * defect.
70
+ *
71
+ * `TabbedForm` / `SplitForm` / `WizardForm` receive `customFields` through
72
+ * `ObjectForm`'s `{...schema}` spread. Until objectui#10254 they neither
73
+ * declared nor rendered it, and limb (a) read it there only as a SIGNAL; they
74
+ * now declare it and draw a member a section names, as the drawer and modal do.
75
+ */
76
+ /** The shape this module reads — deliberately narrower than any form schema. */
77
+ export interface InlineFieldSource {
78
+ customFields?: unknown;
79
+ sections?: unknown;
80
+ }
81
+ /**
82
+ * `normalizeSectionField`'s shape (3): an already-built runtime `FormField`,
83
+ * carrying its own `name`, needing no object schema to resolve.
84
+ *
85
+ * A STRING `field` is the disambiguator for shape (2), the spec
86
+ * `FormFieldSchema` — there `field` is the field NAME; on a runtime `FormField`
87
+ * the `field` slot holds the metadata OBJECT. A missing `name` is not treated as
88
+ * inline either: shape 3 without one is what used to crash the form renderer on
89
+ * `name.split('.')`, so it is malformed rather than self-describing.
90
+ *
91
+ * Exported because it is also what `buildSectionFields` (`sectionFields.ts`)
92
+ * asks of a section entry before its parent field pool may drop it
93
+ * (objectui#11615): a self-describing entry is drawn whatever the pool holds,
94
+ * as the pool-less arms draw it. One predicate, so "inline enough to need no
95
+ * adapter" and "inline enough to need no pool" cannot drift apart.
96
+ */
97
+ export declare function isInlineFieldDef(def: unknown): boolean;
98
+ /**
99
+ * Whether the author declared the form's fields inline, i.e. the form is a
100
+ * self-contained collector whose `onSuccess` is the write.
101
+ *
102
+ * Literally `SimpleObjectForm`'s test, kept in one place so the six renderers
103
+ * cannot answer it six ways.
104
+ *
105
+ * The parameter is `object`, not `InlineFieldSource`: TypeScript's weak-type
106
+ * check rejects an argument sharing no property with a wholly-optional
107
+ * interface, so a caller whose schema type declares neither key could not pass
108
+ * it. `TabbedFormSchema` / `SplitFormSchema` / `WizardFormSchema` declared no
109
+ * `customFields` while they did not render it, since declaring a field source a
110
+ * renderer ignores is the declared-≠-enforced shape objectui#6300 set out to
111
+ * undo; they declare it since objectui#10254, which made them render it.
112
+ */
113
+ export declare function hasInlineFieldSource(schema: object | null | undefined): boolean;
114
+ /**
115
+ * The refusal message. `SimpleObjectForm`'s own wording, verbatim, so a host
116
+ * that already matches on it sees one string from all six renderers.
117
+ */
118
+ export declare const NO_SUBMIT_TARGET_MESSAGE = "DataSource is required for form submission (inline mode not configured)";
119
+ /**
120
+ * The refusal itself.
121
+ *
122
+ * Thrown from INSIDE each container's persistence chain — as the branch taken
123
+ * when none of the real routes (host seam / create / OCC-guarded edit) applies
124
+ * — rather than from a pre-`try` guard, for two reasons:
125
+ *
126
+ * 1. it is where the fact belongs: "there is no route to persist by" is a
127
+ * verdict about the persistence chain, and expressing it there is also what
128
+ * lets TypeScript narrow `dataSource` for the routes that use it, with no
129
+ * assertion;
130
+ * 2. the container's `catch` runs `schema.onError?.(err)` and RETHROWS, so the
131
+ * host learns why. That is what turns the `MasterDetailForm` reading above
132
+ * from `toast.success("Created")` into `toast.error(<this message>)` with
133
+ * the form left intact — the visible half of the fix.
134
+ */
135
+ export declare function noSubmitTargetError(): Error;
136
+ //# sourceMappingURL=submitTarget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"submitTarget.d.ts","sourceRoot":"","sources":["../src/submitTarget.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AAEH,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAItD;AAkBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAI/E;AAED;;;GAGG;AACH,eAAO,MAAM,wBAAwB,4EACsC,CAAC;AAE5E;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,IAAI,KAAK,CAE3C"}
@@ -9,34 +9,53 @@
9
9
  * for another entry (`resetOnSuccess`).
10
10
  *
11
11
  * Kept dependency-free on purpose: importing the redirect guard from
12
- * EmbeddableForm would create a cycle (EmbeddableForm → ObjectForm → WizardForm
13
- * → here), so the same-origin check is inlined.
12
+ * EmbeddableForm would create a cycle (EmbeddableForm -> ObjectForm ->
13
+ * WizardForm -> here), so the destination test below is local to this module.
14
14
  */
15
15
  export type { SubmitBehavior } from '@object-ui/types';
16
16
  /**
17
- * Same-origin guard for declarative navigation (relative or absolute URLs).
18
- *
19
- * **Scope note (objectui#4989).** This guard used to gate `submitBehavior:
20
- * { kind: 'redirect' }` too. It no longer does: objectstack#7496 ruled that key
21
- * RELATIVE-ONLY, and `submitRedirect.ts` gets that verdict from the spec's own
22
- * `FormViewSchema`. Relative-only is strictly stronger than same-origin — a
23
- * rooted relative path is same-origin by construction — so keeping this call in
24
- * that arm as defence-in-depth would add no defence, while its converse (it says
25
- * yes to `https://own-host/thanks`) was exactly the defect: this consumer
26
- * accepted a spelling the authoring door now refuses, which is how a rejected
27
- * spelling stays alive in a corpus.
28
- *
29
- * It survives because {@link resolveSuccessNavigate} still needs it. That serves
30
- * `navigateOnSuccess` — a DIFFERENT declared key with its own `{id}` /
31
- * `{recordId}` dialect, no relative-only ruling, and its own contract question
32
- * still open. Deleting the guard would silently widen that key.
33
- */
34
- export declare function isSameOriginUrl(rawUrl: string): boolean;
35
- /**
36
- * Resolve a `navigateOnSuccess` template into a safe URL: interpolate
37
- * `{id}` / `{recordId}` from the created/updated record and same-origin-guard it.
38
- * Returns null when there's no template, no usable id, or the URL fails the
39
- * guard — callers then fall back to a toast.
17
+ * Resolve a `navigateOnSuccess` template into the relative destination to
18
+ * navigate to, or refuse it. Returns null when there is no template, no usable
19
+ * id, or the resolved value is out of contract — callers then show the success
20
+ * toast carrying `NAVIGATE_ON_SUCCESS_REFUSED_NOTE`.
21
+ *
22
+ * ## The contract (maintainer ruling, 2026-08-17, objectui#5034)
23
+ *
24
+ * `navigateOnSuccess` is the pre-ruling ancestor of the `submitBehavior` family,
25
+ * not a second dialect, and it is **deprecated in favour of `submitBehavior`**
26
+ * (which already takes precedence over it, pinned). As a compat alias it runs
27
+ * under the semantics objectstack#7496 ruled for that family:
28
+ *
29
+ * 1. **relative paths only.** A same-origin ABSOLUTE value is refused like any
30
+ * other out-of-contract value — not routed differently, refused. The
31
+ * destination is authored metadata, which is exactly where an address
32
+ * somebody else chose gets copied in.
33
+ * 2. **the interpolated value is URL-escaped** when the destination is built.
34
+ * 3. the `{id}` / `{recordId}` dialect **stays** for existing authors of this
35
+ * key. It is the one part deliberately not converged: the ruling keeps the
36
+ * spelling working and points new authoring at `submitBehavior` instead.
37
+ *
38
+ * ## Why both halves are needed, and neither substitutes for the other
39
+ *
40
+ * The escape is what stops a token from adding path STRUCTURE. Interpolated raw,
41
+ * `{id}` with an address in it becomes the whole destination, and `/r/{id}` with
42
+ * `a/b` in it silently grows a path segment. Relative-only is a rule about where
43
+ * a destination STARTS, so it has nothing to say about structure injected
44
+ * further along — the argument `submitRedirect.ts` records for the ruled sibling,
45
+ * which holds here verbatim.
46
+ *
47
+ * Conversely the escape alone would not refuse an authored absolute, because the
48
+ * template is the AUTHOR's and only the substituted value passes through
49
+ * `encodeURIComponent`. So: escape the data, judge the result.
50
+ *
51
+ * ## This can only narrow what is reachable
52
+ *
53
+ * Every destination this returns is a relative reference, and every relative
54
+ * reference was already accepted by the same-origin guard it replaces (a
55
+ * relative reference cannot carry an authority, RFC 3986, so it always resolves
56
+ * to the current origin). Escaping only ever maps an id onto one opaque segment.
57
+ * So the set of addresses reachable through this key is a strict subset of the
58
+ * set reachable before — there is no value that was refused and is now followed.
40
59
  */
41
60
  export declare function resolveSuccessNavigate(template: string | undefined, record: any): string | null;
42
61
  //# sourceMappingURL=successBehavior.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"successBehavior.d.ts","sourceRoot":"","sources":["../src/successBehavior.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAQvD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,GAAG,GACV,MAAM,GAAG,IAAI,CAMf"}
1
+ {"version":3,"file":"successBehavior.d.ts","sourceRoot":"","sources":["../src/successBehavior.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AA0DvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,GAAG,GACV,MAAM,GAAG,IAAI,CAWf"}
@@ -1,4 +1,4 @@
1
- import { PendingThankYouRedirect } from './thankYouRedirectNavigation';
1
+ import { PendingThankYouRedirect } from './thankYouRedirectNavigation.js';
2
2
  /** One tick per displayed second — matches the `{{seconds}}` unit the copy promises. */
3
3
  export declare const REDIRECT_COUNTDOWN_TICK_MS = 1000;
4
4
  /**