@10x-media/form-builder 0.1.0-beta.11 → 0.1.0-beta.13

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 (151) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/aggregation/resolveResultsRequest.d.ts +6 -0
  3. package/dist/aggregation/resolveResultsRequest.js +32 -3
  4. package/dist/aggregation/resolveResultsRequest.js.map +1 -1
  5. package/dist/calc/computeCalcFields.d.ts +5 -3
  6. package/dist/calc/computeCalcFields.js +4 -3
  7. package/dist/calc/computeCalcFields.js.map +1 -1
  8. package/dist/calc/evaluate.d.ts +26 -3
  9. package/dist/calc/evaluate.js +33 -11
  10. package/dist/calc/evaluate.js.map +1 -1
  11. package/dist/calc/formatCalc.d.ts +11 -0
  12. package/dist/calc/formatCalc.js +35 -0
  13. package/dist/calc/formatCalc.js.map +1 -0
  14. package/dist/calc/formatCalcValue.d.ts +17 -0
  15. package/dist/calc/formatCalcValue.js +17 -0
  16. package/dist/calc/formatCalcValue.js.map +1 -0
  17. package/dist/calc/normalizeCalc.d.ts +17 -3
  18. package/dist/calc/normalizeCalc.js +39 -17
  19. package/dist/calc/normalizeCalc.js.map +1 -1
  20. package/dist/calc/registry.d.ts +43 -0
  21. package/dist/calc/registry.js +27 -0
  22. package/dist/calc/registry.js.map +1 -0
  23. package/dist/calc/resolveCalcContext.d.ts +36 -0
  24. package/dist/calc/resolveCalcContext.js +87 -0
  25. package/dist/calc/resolveCalcContext.js.map +1 -0
  26. package/dist/calc/types.d.ts +18 -3
  27. package/dist/calc/types.js +2 -1
  28. package/dist/calc/types.js.map +1 -1
  29. package/dist/client/CalcExpressionBuilder.d.ts +20 -0
  30. package/dist/client/CalcExpressionBuilder.js +890 -0
  31. package/dist/client/CalcExpressionBuilder.js.map +1 -0
  32. package/dist/client/admin.css +282 -0
  33. package/dist/collections/formSubmissions.js +7 -9
  34. package/dist/collections/formSubmissions.js.map +1 -1
  35. package/dist/collections/forms.js +59 -34
  36. package/dist/collections/forms.js.map +1 -1
  37. package/dist/collections/formsEndpoints.js +4 -2
  38. package/dist/collections/formsEndpoints.js.map +1 -1
  39. package/dist/collections/settingsFields.d.ts +31 -0
  40. package/dist/collections/settingsFields.js +45 -0
  41. package/dist/collections/settingsFields.js.map +1 -0
  42. package/dist/exports/client.d.ts +2 -1
  43. package/dist/exports/client.js +2 -1
  44. package/dist/exports/react.d.ts +7 -3
  45. package/dist/exports/react.js +6 -2
  46. package/dist/exports/rsc.js +1 -1
  47. package/dist/exports/types.d.ts +1 -1
  48. package/dist/fields/buildFieldBlocks.js +2 -1
  49. package/dist/fields/buildFieldBlocks.js.map +1 -1
  50. package/dist/fields/builtin/calculation.d.ts +17 -0
  51. package/dist/fields/builtin/calculation.js +170 -115
  52. package/dist/fields/builtin/calculation.js.map +1 -1
  53. package/dist/fields/builtin/checkbox.js +15 -0
  54. package/dist/fields/builtin/checkbox.js.map +1 -1
  55. package/dist/fields/builtin/email.js +7 -0
  56. package/dist/fields/builtin/email.js.map +1 -1
  57. package/dist/fields/builtin/index.d.ts +6 -2
  58. package/dist/fields/builtin/index.js +9 -4
  59. package/dist/fields/builtin/index.js.map +1 -1
  60. package/dist/fields/builtin/select.js +20 -1
  61. package/dist/fields/builtin/select.js.map +1 -1
  62. package/dist/fields/builtin/text.js +7 -0
  63. package/dist/fields/builtin/text.js.map +1 -1
  64. package/dist/fields/instanceOptions.js +1 -1
  65. package/dist/fields/instanceOptions.js.map +1 -1
  66. package/dist/fields/sharedConfig.js +15 -11
  67. package/dist/fields/sharedConfig.js.map +1 -1
  68. package/dist/fields/types.d.ts +8 -2
  69. package/dist/form/toFormDocument.d.ts +7 -0
  70. package/dist/form/toFormDocument.js +2 -1
  71. package/dist/form/toFormDocument.js.map +1 -1
  72. package/dist/form/types.d.ts +7 -0
  73. package/dist/index.d.ts +12 -4
  74. package/dist/index.js +35 -8
  75. package/dist/index.js.map +1 -1
  76. package/dist/options.d.ts +35 -0
  77. package/dist/plugin/registerCollections.js +29 -19
  78. package/dist/plugin/registerCollections.js.map +1 -1
  79. package/dist/poll/closeJob.d.ts +4 -3
  80. package/dist/poll/closeJob.js +6 -4
  81. package/dist/poll/closeJob.js.map +1 -1
  82. package/dist/poll/pollTypeRegistry.js +1 -1
  83. package/dist/poll/resolvePollCloseRequest.js +2 -1
  84. package/dist/poll/resolvePollCloseRequest.js.map +1 -1
  85. package/dist/poll/resolvePollOutcome.d.ts +6 -0
  86. package/dist/poll/resolvePollOutcome.js +17 -3
  87. package/dist/poll/resolvePollOutcome.js.map +1 -1
  88. package/dist/poll/votes/aggregateFromVotes.d.ts +27 -0
  89. package/dist/poll/votes/aggregateFromVotes.js +53 -0
  90. package/dist/poll/votes/aggregateFromVotes.js.map +1 -0
  91. package/dist/poll/votes/bumpPollVote.js +88 -0
  92. package/dist/poll/votes/bumpPollVote.js.map +1 -0
  93. package/dist/poll/votes/recountPollVotes.d.ts +20 -0
  94. package/dist/poll/votes/recountPollVotes.js +63 -0
  95. package/dist/poll/votes/recountPollVotes.js.map +1 -0
  96. package/dist/poll/votes/voteTallyHook.js +58 -0
  97. package/dist/poll/votes/voteTallyHook.js.map +1 -0
  98. package/dist/poll/votes/votesCollection.d.ts +19 -0
  99. package/dist/poll/votes/votesCollection.js +89 -0
  100. package/dist/poll/votes/votesCollection.js.map +1 -0
  101. package/dist/react/Form.d.ts +7 -0
  102. package/dist/react/Form.js +20 -3
  103. package/dist/react/Form.js.map +1 -1
  104. package/dist/react/FormContext.d.ts +8 -1
  105. package/dist/react/FormContext.js.map +1 -1
  106. package/dist/react/primitives/Checkbox.d.ts +4 -2
  107. package/dist/react/primitives/Checkbox.js +3 -2
  108. package/dist/react/primitives/Checkbox.js.map +1 -1
  109. package/dist/react/primitives/ChoiceGroup.d.ts +40 -0
  110. package/dist/react/primitives/ChoiceGroup.js +33 -0
  111. package/dist/react/primitives/ChoiceGroup.js.map +1 -0
  112. package/dist/react/primitives/FieldShell.d.ts +11 -1
  113. package/dist/react/primitives/FieldShell.js +43 -33
  114. package/dist/react/primitives/FieldShell.js.map +1 -1
  115. package/dist/react/primitives/Input.d.ts +3 -1
  116. package/dist/react/primitives/Input.js +2 -1
  117. package/dist/react/primitives/Input.js.map +1 -1
  118. package/dist/react/renderers/calculation.js +6 -1
  119. package/dist/react/renderers/calculation.js.map +1 -1
  120. package/dist/react/renderers/checkbox.js +2 -1
  121. package/dist/react/renderers/checkbox.js.map +1 -1
  122. package/dist/react/renderers/email.js +3 -1
  123. package/dist/react/renderers/email.js.map +1 -1
  124. package/dist/react/renderers/select.js +20 -2
  125. package/dist/react/renderers/select.js.map +1 -1
  126. package/dist/react/renderers/text.js +3 -1
  127. package/dist/react/renderers/text.js.map +1 -1
  128. package/dist/react/useCalcValues.d.ts +10 -0
  129. package/dist/react/useCalcValues.js +13 -0
  130. package/dist/react/useCalcValues.js.map +1 -0
  131. package/dist/recall/resolver.js +1 -1
  132. package/dist/recall/resolver.js.map +1 -1
  133. package/dist/submissions/formIdOf.js +13 -0
  134. package/dist/submissions/formIdOf.js.map +1 -0
  135. package/dist/submissions/runSubmission.js +3 -3
  136. package/dist/submissions/runSubmission.js.map +1 -1
  137. package/dist/submissions/validateSubmission.js +26 -2
  138. package/dist/submissions/validateSubmission.js.map +1 -1
  139. package/dist/translations/de.js +58 -3
  140. package/dist/translations/de.js.map +1 -1
  141. package/dist/translations/en.js +58 -3
  142. package/dist/translations/en.js.map +1 -1
  143. package/dist/translations/keys.d.ts +56 -1
  144. package/dist/translations/keys.js +56 -1
  145. package/dist/translations/keys.js.map +1 -1
  146. package/dist/validation/builtin/index.js +2 -0
  147. package/dist/validation/builtin/index.js.map +1 -1
  148. package/dist/validation/builtin/integer.js +15 -0
  149. package/dist/validation/builtin/integer.js.map +1 -0
  150. package/package.json +10 -4
  151. package/styles/form-builder.css +84 -0
@@ -1 +1 @@
1
- {"version":3,"file":"normalizeCalc.js","names":[],"sources":["../../src/calc/normalizeCalc.ts"],"sourcesContent":["import {\n\tCALC_FNS,\n\tCALC_OPS,\n\ttype CalcExpression,\n\ttype CalcFn,\n\ttype CalcOp,\n\tMAX_DEPTH,\n} from './types'\n\nconst isOp = (v: unknown): v is CalcOp => (CALC_OPS as readonly unknown[]).includes(v)\nconst isFn = (v: unknown): v is CalcFn => (CALC_FNS as readonly unknown[]).includes(v)\n\nconst isRecord = (v: unknown): v is Record<string, unknown> =>\n\tv !== null && typeof v === 'object' && !Array.isArray(v)\n\nconst normalizeNode = (value: unknown, depth: number): CalcExpression | undefined => {\n\tif (depth > MAX_DEPTH) return undefined\n\tif (!isRecord(value)) return undefined\n\n\tswitch (value.type) {\n\t\tcase 'lit': {\n\t\t\tconst { value: v } = value\n\t\t\tif (typeof v !== 'number' || !Number.isFinite(v)) return undefined\n\t\t\treturn { type: 'lit', value: v }\n\t\t}\n\t\tcase 'ref': {\n\t\t\tconst { field } = value\n\t\t\tif (typeof field !== 'string') return undefined\n\t\t\treturn { type: 'ref', field }\n\t\t}\n\t\tcase 'op': {\n\t\t\tconst { op, left, right } = value\n\t\t\tif (!isOp(op)) return undefined\n\t\t\tconst l = normalizeNode(left, depth + 1)\n\t\t\tif (!l) return undefined\n\t\t\tconst r = normalizeNode(right, depth + 1)\n\t\t\tif (!r) return undefined\n\t\t\treturn { type: 'op', op, left: l, right: r }\n\t\t}\n\t\tcase 'neg': {\n\t\t\tconst operand = normalizeNode(value.operand, depth + 1)\n\t\t\tif (!operand) return undefined\n\t\t\treturn { type: 'neg', operand }\n\t\t}\n\t\tcase 'fn': {\n\t\t\tconst { fn, args } = value\n\t\t\tif (!isFn(fn)) return undefined\n\t\t\tif (!Array.isArray(args)) return undefined\n\t\t\tconst normalizedArgs: CalcExpression[] = []\n\t\t\tfor (const arg of args) {\n\t\t\t\tconst a = normalizeNode(arg, depth + 1)\n\t\t\t\tif (!a) return undefined\n\t\t\t\tnormalizedArgs.push(a)\n\t\t\t}\n\t\t\treturn { type: 'fn', fn, args: normalizedArgs }\n\t\t}\n\t\tcase 'weight': {\n\t\t\tconst { field, weights } = value\n\t\t\tif (typeof field !== 'string') return undefined\n\t\t\tif (!isRecord(weights)) return undefined\n\t\t\tconst normalizedWeights: Record<string, number> = {}\n\t\t\tfor (const [k, v] of Object.entries(weights)) {\n\t\t\t\tif (typeof v !== 'number' || !Number.isFinite(v)) return undefined\n\t\t\t\tnormalizedWeights[k] = v\n\t\t\t}\n\t\t\treturn { type: 'weight', field, weights: normalizedWeights }\n\t\t}\n\t\tdefault:\n\t\t\treturn undefined\n\t}\n}\n\n/** Structural guard: returns a valid CalcExpression if `value` is structurally sound (depth-guarded at 64), otherwise undefined. Never throws. */\nexport const normalizeCalc = (value: unknown): CalcExpression | undefined => normalizeNode(value, 0)\n"],"mappings":";;AASA,MAAM,QAAQ,MAA6B,SAAgC,SAAS,CAAC;AACrF,MAAM,QAAQ,MAA6B,SAAgC,SAAS,CAAC;AAErF,MAAM,YAAY,MACjB,MAAM,QAAQ,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC;AAExD,MAAM,iBAAiB,OAAgB,UAA8C;CACpF,IAAI,QAAA,IAAmB,OAAO,KAAA;CAC9B,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO,KAAA;CAE7B,QAAQ,MAAM,MAAd;EACC,KAAK,OAAO;GACX,MAAM,EAAE,OAAO,MAAM;GACrB,IAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO,KAAA;GACzD,OAAO;IAAE,MAAM;IAAO,OAAO;GAAE;EAChC;EACA,KAAK,OAAO;GACX,MAAM,EAAE,UAAU;GAClB,IAAI,OAAO,UAAU,UAAU,OAAO,KAAA;GACtC,OAAO;IAAE,MAAM;IAAO;GAAM;EAC7B;EACA,KAAK,MAAM;GACV,MAAM,EAAE,IAAI,MAAM,UAAU;GAC5B,IAAI,CAAC,KAAK,EAAE,GAAG,OAAO,KAAA;GACtB,MAAM,IAAI,cAAc,MAAM,QAAQ,CAAC;GACvC,IAAI,CAAC,GAAG,OAAO,KAAA;GACf,MAAM,IAAI,cAAc,OAAO,QAAQ,CAAC;GACxC,IAAI,CAAC,GAAG,OAAO,KAAA;GACf,OAAO;IAAE,MAAM;IAAM;IAAI,MAAM;IAAG,OAAO;GAAE;EAC5C;EACA,KAAK,OAAO;GACX,MAAM,UAAU,cAAc,MAAM,SAAS,QAAQ,CAAC;GACtD,IAAI,CAAC,SAAS,OAAO,KAAA;GACrB,OAAO;IAAE,MAAM;IAAO;GAAQ;EAC/B;EACA,KAAK,MAAM;GACV,MAAM,EAAE,IAAI,SAAS;GACrB,IAAI,CAAC,KAAK,EAAE,GAAG,OAAO,KAAA;GACtB,IAAI,CAAC,MAAM,QAAQ,IAAI,GAAG,OAAO,KAAA;GACjC,MAAM,iBAAmC,CAAC;GAC1C,KAAK,MAAM,OAAO,MAAM;IACvB,MAAM,IAAI,cAAc,KAAK,QAAQ,CAAC;IACtC,IAAI,CAAC,GAAG,OAAO,KAAA;IACf,eAAe,KAAK,CAAC;GACtB;GACA,OAAO;IAAE,MAAM;IAAM;IAAI,MAAM;GAAe;EAC/C;EACA,KAAK,UAAU;GACd,MAAM,EAAE,OAAO,YAAY;GAC3B,IAAI,OAAO,UAAU,UAAU,OAAO,KAAA;GACtC,IAAI,CAAC,SAAS,OAAO,GAAG,OAAO,KAAA;GAC/B,MAAM,oBAA4C,CAAC;GACnD,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,OAAO,GAAG;IAC7C,IAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO,KAAA;IACzD,kBAAkB,KAAK;GACxB;GACA,OAAO;IAAE,MAAM;IAAU;IAAO,SAAS;GAAkB;EAC5D;EACA,SACC;CACF;AACD;;AAGA,MAAa,iBAAiB,UAA+C,cAAc,OAAO,CAAC"}
1
+ {"version":3,"file":"normalizeCalc.js","names":[],"sources":["../../src/calc/normalizeCalc.ts"],"sourcesContent":["import { CALC_OPS, type CalcExpression, type CalcOp, isCalcFn, MAX_DEPTH } from './types'\n\n/**\n * The extension names a normalized expression may reference: registered calc source keys and custom\n * function names (plugin option `calc`). Absent (the default for every un-threaded caller), only the\n * built-in grammar is valid: `source` nodes, sourced weights, and custom fns all reject.\n */\nexport type CalcAllowed = {\n\tsources?: ReadonlySet<string>\n\tfunctions?: ReadonlySet<string>\n}\n\nconst isOp = (v: unknown): v is CalcOp => (CALC_OPS as readonly unknown[]).includes(v)\n\nconst isRecord = (v: unknown): v is Record<string, unknown> =>\n\tv !== null && typeof v === 'object' && !Array.isArray(v)\n\nconst normalizeNode = (\n\tvalue: unknown,\n\tdepth: number,\n\tallowed?: CalcAllowed\n): CalcExpression | undefined => {\n\tif (depth > MAX_DEPTH) return undefined\n\tif (!isRecord(value)) return undefined\n\n\tswitch (value.type) {\n\t\tcase 'lit': {\n\t\t\tconst { value: v } = value\n\t\t\tif (typeof v !== 'number' || !Number.isFinite(v)) return undefined\n\t\t\treturn { type: 'lit', value: v }\n\t\t}\n\t\tcase 'ref': {\n\t\t\tconst { field } = value\n\t\t\tif (typeof field !== 'string') return undefined\n\t\t\treturn { type: 'ref', field }\n\t\t}\n\t\tcase 'op': {\n\t\t\tconst { op, left, right } = value\n\t\t\tif (!isOp(op)) return undefined\n\t\t\tconst l = normalizeNode(left, depth + 1, allowed)\n\t\t\tif (!l) return undefined\n\t\t\tconst r = normalizeNode(right, depth + 1, allowed)\n\t\t\tif (!r) return undefined\n\t\t\treturn { type: 'op', op, left: l, right: r }\n\t\t}\n\t\tcase 'neg': {\n\t\t\tconst operand = normalizeNode(value.operand, depth + 1, allowed)\n\t\t\tif (!operand) return undefined\n\t\t\treturn { type: 'neg', operand }\n\t\t}\n\t\tcase 'fn': {\n\t\t\tconst { fn, args } = value\n\t\t\tif (typeof fn !== 'string') return undefined\n\t\t\tif (!isCalcFn(fn) && !allowed?.functions?.has(fn)) return undefined\n\t\t\tif (!Array.isArray(args)) return undefined\n\t\t\tconst normalizedArgs: CalcExpression[] = []\n\t\t\tfor (const arg of args) {\n\t\t\t\tconst a = normalizeNode(arg, depth + 1, allowed)\n\t\t\t\tif (!a) return undefined\n\t\t\t\tnormalizedArgs.push(a)\n\t\t\t}\n\t\t\treturn { type: 'fn', fn, args: normalizedArgs }\n\t\t}\n\t\tcase 'source': {\n\t\t\tconst { source } = value\n\t\t\tif (typeof source !== 'string') return undefined\n\t\t\tif (!allowed?.sources?.has(source)) return undefined\n\t\t\treturn { type: 'source', source }\n\t\t}\n\t\tcase 'weight': {\n\t\t\tconst { field, weights, source } = value\n\t\t\tif (typeof field !== 'string') return undefined\n\t\t\tif (source !== undefined) {\n\t\t\t\tif (typeof source !== 'string' || !allowed?.sources?.has(source)) return undefined\n\t\t\t}\n\t\t\tlet normalizedWeights: Record<string, number> | undefined\n\t\t\tif (weights !== undefined) {\n\t\t\t\tif (!isRecord(weights)) return undefined\n\t\t\t\tnormalizedWeights = {}\n\t\t\t\tfor (const [k, v] of Object.entries(weights)) {\n\t\t\t\t\tif (typeof v !== 'number' || !Number.isFinite(v)) return undefined\n\t\t\t\t\tnormalizedWeights[k] = v\n\t\t\t\t}\n\t\t\t}\n\t\t\t// A weight must carry a per-option map from somewhere: inline weights or a registered source.\n\t\t\tif (normalizedWeights === undefined && source === undefined) return undefined\n\t\t\treturn {\n\t\t\t\ttype: 'weight',\n\t\t\t\tfield,\n\t\t\t\t...(normalizedWeights !== undefined ? { weights: normalizedWeights } : {}),\n\t\t\t\t...(typeof source === 'string' ? { source } : {}),\n\t\t\t}\n\t\t}\n\t\tdefault:\n\t\t\treturn undefined\n\t}\n}\n\n/**\n * Structural guard: returns a valid CalcExpression if `value` is structurally sound (depth-guarded\n * at 64), otherwise undefined. Never throws. `allowed` admits registered extension names (source\n * keys, custom function names); without it only the built-in grammar passes, so an un-threaded\n * caller can never validate an expression the evaluator has no values for.\n */\nexport const normalizeCalc = (value: unknown, allowed?: CalcAllowed): CalcExpression | undefined =>\n\tnormalizeNode(value, 0, allowed)\n"],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { Payload, PayloadRequest } from "payload";
2
+
3
+ //#region src/calc/registry.d.ts
4
+ type CalcSourceResolveArgs = {
5
+ form: {
6
+ id: number | string;
7
+ } & Record<string, unknown>;
8
+ payload: Payload;
9
+ /**
10
+ * On public form reads and submits this is an unauthenticated visitor request: never feed data
11
+ * derived from it into access-escalated queries, and gate any per-user-sensitive number on
12
+ * `req.user` (resolved values are embedded on every readable form document).
13
+ */
14
+ req?: PayloadRequest;
15
+ };
16
+ type CalcWeightResolveArgs = CalcSourceResolveArgs & {
17
+ /** The field instance whose options are being priced (name, blockType, options, custom config). */field: Record<string, unknown> & {
18
+ name: string;
19
+ };
20
+ };
21
+ /**
22
+ * A named server-side value resolver the calculation field can reference. Values resolve on the
23
+ * server (render embedding and again at submit), so the client never supplies them. A source may
24
+ * offer either mode or both; an expression using a mode the source does not implement fails
25
+ * loudly at resolution.
26
+ */
27
+ type CalcSource = {
28
+ label: string | Record<string, string>; /** Scalar mode: one number for the whole form render/submission. */
29
+ resolve?: (args: CalcSourceResolveArgs) => number | Promise<number>; /** Weight mode: per-option values for a chosen field, keyed by option value. */
30
+ resolveWeights?: (args: CalcWeightResolveArgs) => Record<string, number> | Promise<Record<string, number>>;
31
+ };
32
+ /** A host-registered calculation function; `apply` must be pure and sync (it runs isomorphically in the live preview). */
33
+ type CalcFunction = {
34
+ label: string | Record<string, string>;
35
+ apply: (args: number[]) => number;
36
+ };
37
+ /** Identity helper: define a calc source once with full typing (the `defineFormField` precedent). */
38
+ declare const defineCalcSource: (source: CalcSource) => CalcSource;
39
+ /** Identity helper: define a calc function once with full typing (the `defineFormField` precedent). */
40
+ declare const defineCalcFunction: (fn: CalcFunction) => CalcFunction;
41
+ //#endregion
42
+ export { CalcFunction, CalcSource, CalcSourceResolveArgs, CalcWeightResolveArgs, defineCalcFunction, defineCalcSource };
43
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { CALC_FNS } from "./types.js";
2
+ //#region src/calc/registry.ts
3
+ /** Identity helper: define a calc source once with full typing (the `defineFormField` precedent). */
4
+ const defineCalcSource = (source) => source;
5
+ /** Identity helper: define a calc function once with full typing (the `defineFormField` precedent). */
6
+ const defineCalcFunction = (fn) => fn;
7
+ /**
8
+ * Fail fast at boot when a registered calc function key collides with a built-in. The evaluator
9
+ * always resolves built-ins first, so a colliding custom function could never run; rather than let
10
+ * it be shadowed quietly, throw a clear error naming the collision so the author renames it.
11
+ */
12
+ const assertNoCalcFunctionCollision = (functions) => {
13
+ for (const key of Object.keys(functions)) if (CALC_FNS.includes(key)) throw new Error(`@10x-media/form-builder: calculation function "${key}" collides with the built-in function "${key}". Rename the custom function.`);
14
+ };
15
+ const CALC_SOURCE_KEY_PATTERN = /^[\w.-]+$/;
16
+ /**
17
+ * Fail fast at boot on a calc source key outside `[\w.-]`. Resolved weight maps are keyed
18
+ * `source + ' ' + field` (`calcWeightKey`), so the separator char must never appear in a source
19
+ * key or two different (source, field) pairs could collide on one map.
20
+ */
21
+ const assertValidCalcSourceKeys = (sources) => {
22
+ for (const key of Object.keys(sources)) if (!CALC_SOURCE_KEY_PATTERN.test(key)) throw new Error(`@10x-media/form-builder: calculation source key "${key}" is invalid. Use only letters, digits, "_", "." or "-".`);
23
+ };
24
+ //#endregion
25
+ export { assertNoCalcFunctionCollision, assertValidCalcSourceKeys, defineCalcFunction, defineCalcSource };
26
+
27
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","names":[],"sources":["../../src/calc/registry.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { CALC_FNS } from './types'\n\nexport type CalcSourceResolveArgs = {\n\tform: { id: number | string } & Record<string, unknown>\n\tpayload: Payload\n\t/**\n\t * On public form reads and submits this is an unauthenticated visitor request: never feed data\n\t * derived from it into access-escalated queries, and gate any per-user-sensitive number on\n\t * `req.user` (resolved values are embedded on every readable form document).\n\t */\n\treq?: PayloadRequest\n}\n\nexport type CalcWeightResolveArgs = CalcSourceResolveArgs & {\n\t/** The field instance whose options are being priced (name, blockType, options, custom config). */\n\tfield: Record<string, unknown> & { name: string }\n}\n\n/**\n * A named server-side value resolver the calculation field can reference. Values resolve on the\n * server (render embedding and again at submit), so the client never supplies them. A source may\n * offer either mode or both; an expression using a mode the source does not implement fails\n * loudly at resolution.\n */\nexport type CalcSource = {\n\tlabel: string | Record<string, string>\n\t/** Scalar mode: one number for the whole form render/submission. */\n\tresolve?: (args: CalcSourceResolveArgs) => number | Promise<number>\n\t/** Weight mode: per-option values for a chosen field, keyed by option value. */\n\tresolveWeights?: (\n\t\targs: CalcWeightResolveArgs\n\t) => Record<string, number> | Promise<Record<string, number>>\n}\n\n/** A host-registered calculation function; `apply` must be pure and sync (it runs isomorphically in the live preview). */\nexport type CalcFunction = {\n\tlabel: string | Record<string, string>\n\tapply: (args: number[]) => number\n}\n\n/** Identity helper: define a calc source once with full typing (the `defineFormField` precedent). */\nexport const defineCalcSource = (source: CalcSource): CalcSource => source\n\n/** Identity helper: define a calc function once with full typing (the `defineFormField` precedent). */\nexport const defineCalcFunction = (fn: CalcFunction): CalcFunction => fn\n\n/**\n * Fail fast at boot when a registered calc function key collides with a built-in. The evaluator\n * always resolves built-ins first, so a colliding custom function could never run; rather than let\n * it be shadowed quietly, throw a clear error naming the collision so the author renames it.\n */\nexport const assertNoCalcFunctionCollision = (functions: Record<string, CalcFunction>): void => {\n\tfor (const key of Object.keys(functions)) {\n\t\tif ((CALC_FNS as readonly string[]).includes(key)) {\n\t\t\tthrow new Error(\n\t\t\t\t`@10x-media/form-builder: calculation function \"${key}\" collides with the built-in function \"${key}\". Rename the custom function.`\n\t\t\t)\n\t\t}\n\t}\n}\n\nconst CALC_SOURCE_KEY_PATTERN = /^[\\w.-]+$/\n\n/**\n * Fail fast at boot on a calc source key outside `[\\w.-]`. Resolved weight maps are keyed\n * `source + ' ' + field` (`calcWeightKey`), so the separator char must never appear in a source\n * key or two different (source, field) pairs could collide on one map.\n */\nexport const assertValidCalcSourceKeys = (sources: Record<string, CalcSource>): void => {\n\tfor (const key of Object.keys(sources)) {\n\t\tif (!CALC_SOURCE_KEY_PATTERN.test(key)) {\n\t\t\tthrow new Error(\n\t\t\t\t`@10x-media/form-builder: calculation source key \"${key}\" is invalid. Use only letters, digits, \"_\", \".\" or \"-\".`\n\t\t\t)\n\t\t}\n\t}\n}\n"],"mappings":";;;AA0CA,MAAa,oBAAoB,WAAmC;;AAGpE,MAAa,sBAAsB,OAAmC;;;;;;AAOtE,MAAa,iCAAiC,cAAkD;CAC/F,KAAK,MAAM,OAAO,OAAO,KAAK,SAAS,GACtC,IAAK,SAA+B,SAAS,GAAG,GAC/C,MAAM,IAAI,MACT,kDAAkD,IAAI,yCAAyC,IAAI,+BACpG;AAGH;AAEA,MAAM,0BAA0B;;;;;;AAOhC,MAAa,6BAA6B,YAA8C;CACvF,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GACpC,IAAI,CAAC,wBAAwB,KAAK,GAAG,GACpC,MAAM,IAAI,MACT,oDAAoD,IAAI,yDACzD;AAGH"}
@@ -0,0 +1,36 @@
1
+ import { FormFieldInstance } from "../submissions/types.js";
2
+ import { CalcResolved } from "./evaluate.js";
3
+ import { CalcSource } from "./registry.js";
4
+ import { Payload, PayloadRequest } from "payload";
5
+
6
+ //#region src/calc/resolveCalcContext.d.ts
7
+ /** Whether any calc field's expression references a registered calc source (scalar or weight mode). */
8
+ declare const calcUsesSources: (fields: FormFieldInstance[]) => boolean;
9
+ type ResolveCalcContextArgs = {
10
+ fields: FormFieldInstance[]; /** The registered calc sources (plugin option `calc.sources`). */
11
+ sources: Record<string, CalcSource>;
12
+ form: {
13
+ id: number | string;
14
+ } & Record<string, unknown>;
15
+ payload: Payload;
16
+ req?: PayloadRequest;
17
+ };
18
+ /**
19
+ * Resolve every calc source a form's expressions reference: used scalar keys through `resolve`,
20
+ * used (source, field) weight pairs through `resolveWeights` with the referenced field instance,
21
+ * each exactly once. A weight pair whose field instance is missing resolves to an empty map. A
22
+ * used-but-unregistered key (or a mode the source does not implement) and any resolver failure
23
+ * THROW: these values are money math, so the caller decides how loud a failure is (submit rejects,
24
+ * the render hook catches and degrades). Custom functions are not resolved here; they come straight
25
+ * off the registry per environment.
26
+ */
27
+ declare const resolveCalcContext: ({
28
+ fields,
29
+ sources,
30
+ form,
31
+ payload,
32
+ req
33
+ }: ResolveCalcContextArgs) => Promise<CalcResolved>;
34
+ //#endregion
35
+ export { ResolveCalcContextArgs, calcUsesSources, resolveCalcContext };
36
+ //# sourceMappingURL=resolveCalcContext.d.ts.map
@@ -0,0 +1,87 @@
1
+ import { calcWeightKey } from "./evaluate.js";
2
+ import { calcExpressionOf } from "./computeCalcFields.js";
3
+ //#region src/calc/resolveCalcContext.ts
4
+ const collectNode = (expr, usage, depth) => {
5
+ if (depth > 64 || expr == null || typeof expr !== "object") return;
6
+ switch (expr.type) {
7
+ case "source":
8
+ usage.scalars.add(expr.source);
9
+ return;
10
+ case "weight":
11
+ if (typeof expr.source === "string") usage.weightPairs.set(calcWeightKey(expr.source, expr.field), {
12
+ source: expr.source,
13
+ field: expr.field
14
+ });
15
+ return;
16
+ case "op":
17
+ collectNode(expr.left, usage, depth + 1);
18
+ collectNode(expr.right, usage, depth + 1);
19
+ return;
20
+ case "neg":
21
+ collectNode(expr.operand, usage, depth + 1);
22
+ return;
23
+ case "fn":
24
+ if (Array.isArray(expr.args)) for (const arg of expr.args) collectNode(arg, usage, depth + 1);
25
+ return;
26
+ default: return;
27
+ }
28
+ };
29
+ const usageOf = (fields) => {
30
+ const usage = {
31
+ scalars: /* @__PURE__ */ new Set(),
32
+ weightPairs: /* @__PURE__ */ new Map()
33
+ };
34
+ for (const field of fields) {
35
+ const expr = calcExpressionOf(field);
36
+ if (expr) collectNode(expr, usage, 0);
37
+ }
38
+ return usage;
39
+ };
40
+ /** Whether any calc field's expression references a registered calc source (scalar or weight mode). */
41
+ const calcUsesSources = (fields) => {
42
+ const usage = usageOf(fields);
43
+ return usage.scalars.size > 0 || usage.weightPairs.size > 0;
44
+ };
45
+ /**
46
+ * Resolve every calc source a form's expressions reference: used scalar keys through `resolve`,
47
+ * used (source, field) weight pairs through `resolveWeights` with the referenced field instance,
48
+ * each exactly once. A weight pair whose field instance is missing resolves to an empty map. A
49
+ * used-but-unregistered key (or a mode the source does not implement) and any resolver failure
50
+ * THROW: these values are money math, so the caller decides how loud a failure is (submit rejects,
51
+ * the render hook catches and degrades). Custom functions are not resolved here; they come straight
52
+ * off the registry per environment.
53
+ */
54
+ const resolveCalcContext = async ({ fields, sources, form, payload, req }) => {
55
+ const usage = usageOf(fields);
56
+ if (usage.scalars.size === 0 && usage.weightPairs.size === 0) return {};
57
+ const resolvedSources = {};
58
+ for (const key of usage.scalars) {
59
+ const source = sources[key];
60
+ if (!source?.resolve) throw new Error(`@10x-media/form-builder: calculation expression references source "${key}", which is not registered or has no \`resolve\`.`);
61
+ resolvedSources[key] = await source.resolve({
62
+ form,
63
+ payload,
64
+ req
65
+ });
66
+ }
67
+ const resolvedWeights = {};
68
+ for (const [key, pair] of usage.weightPairs) {
69
+ const source = sources[pair.source];
70
+ if (!source?.resolveWeights) throw new Error(`@10x-media/form-builder: calculation expression references weights from source "${pair.source}", which is not registered or has no \`resolveWeights\`.`);
71
+ const instance = fields.find((field) => field.name === pair.field);
72
+ resolvedWeights[key] = instance ? await source.resolveWeights({
73
+ form,
74
+ payload,
75
+ req,
76
+ field: instance
77
+ }) : {};
78
+ }
79
+ return {
80
+ ...usage.scalars.size > 0 ? { sources: resolvedSources } : {},
81
+ ...usage.weightPairs.size > 0 ? { weights: resolvedWeights } : {}
82
+ };
83
+ };
84
+ //#endregion
85
+ export { calcUsesSources, resolveCalcContext };
86
+
87
+ //# sourceMappingURL=resolveCalcContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveCalcContext.js","names":[],"sources":["../../src/calc/resolveCalcContext.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport type { FormFieldInstance } from '../submissions/types'\nimport { calcExpressionOf } from './computeCalcFields'\nimport { type CalcResolved, calcWeightKey } from './evaluate'\nimport type { CalcSource, CalcWeightResolveArgs } from './registry'\nimport { type CalcExpression, MAX_DEPTH } from './types'\n\ntype CalcSourceUsage = {\n\tscalars: Set<string>\n\t/** Keyed by `calcWeightKey(source, field)`, deduped across expressions. */\n\tweightPairs: Map<string, { source: string; field: string }>\n}\n\nconst collectNode = (expr: CalcExpression, usage: CalcSourceUsage, depth: number): void => {\n\tif (depth > MAX_DEPTH || expr == null || typeof expr !== 'object') return\n\tswitch (expr.type) {\n\t\tcase 'source':\n\t\t\tusage.scalars.add(expr.source)\n\t\t\treturn\n\t\tcase 'weight':\n\t\t\tif (typeof expr.source === 'string') {\n\t\t\t\tusage.weightPairs.set(calcWeightKey(expr.source, expr.field), {\n\t\t\t\t\tsource: expr.source,\n\t\t\t\t\tfield: expr.field,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn\n\t\tcase 'op':\n\t\t\tcollectNode(expr.left, usage, depth + 1)\n\t\t\tcollectNode(expr.right, usage, depth + 1)\n\t\t\treturn\n\t\tcase 'neg':\n\t\t\tcollectNode(expr.operand, usage, depth + 1)\n\t\t\treturn\n\t\tcase 'fn':\n\t\t\tif (Array.isArray(expr.args)) {\n\t\t\t\tfor (const arg of expr.args) collectNode(arg, usage, depth + 1)\n\t\t\t}\n\t\t\treturn\n\t\tdefault:\n\t\t\treturn\n\t}\n}\n\nconst usageOf = (fields: FormFieldInstance[]): CalcSourceUsage => {\n\tconst usage: CalcSourceUsage = { scalars: new Set(), weightPairs: new Map() }\n\tfor (const field of fields) {\n\t\tconst expr = calcExpressionOf(field)\n\t\tif (expr) collectNode(expr, usage, 0)\n\t}\n\treturn usage\n}\n\n/** Whether any calc field's expression references a registered calc source (scalar or weight mode). */\nexport const calcUsesSources = (fields: FormFieldInstance[]): boolean => {\n\tconst usage = usageOf(fields)\n\treturn usage.scalars.size > 0 || usage.weightPairs.size > 0\n}\n\nexport type ResolveCalcContextArgs = {\n\tfields: FormFieldInstance[]\n\t/** The registered calc sources (plugin option `calc.sources`). */\n\tsources: Record<string, CalcSource>\n\tform: { id: number | string } & Record<string, unknown>\n\tpayload: Payload\n\treq?: PayloadRequest\n}\n\n/**\n * Resolve every calc source a form's expressions reference: used scalar keys through `resolve`,\n * used (source, field) weight pairs through `resolveWeights` with the referenced field instance,\n * each exactly once. A weight pair whose field instance is missing resolves to an empty map. A\n * used-but-unregistered key (or a mode the source does not implement) and any resolver failure\n * THROW: these values are money math, so the caller decides how loud a failure is (submit rejects,\n * the render hook catches and degrades). Custom functions are not resolved here; they come straight\n * off the registry per environment.\n */\nexport const resolveCalcContext = async ({\n\tfields,\n\tsources,\n\tform,\n\tpayload,\n\treq,\n}: ResolveCalcContextArgs): Promise<CalcResolved> => {\n\tconst usage = usageOf(fields)\n\tif (usage.scalars.size === 0 && usage.weightPairs.size === 0) {\n\t\treturn {}\n\t}\n\tconst resolvedSources: Record<string, number> = {}\n\tfor (const key of usage.scalars) {\n\t\tconst source = sources[key]\n\t\tif (!source?.resolve) {\n\t\t\tthrow new Error(\n\t\t\t\t`@10x-media/form-builder: calculation expression references source \"${key}\", which is not registered or has no \\`resolve\\`.`\n\t\t\t)\n\t\t}\n\t\tresolvedSources[key] = await source.resolve({ form, payload, req })\n\t}\n\tconst resolvedWeights: Record<string, Record<string, number>> = {}\n\tfor (const [key, pair] of usage.weightPairs) {\n\t\tconst source = sources[pair.source]\n\t\tif (!source?.resolveWeights) {\n\t\t\tthrow new Error(\n\t\t\t\t`@10x-media/form-builder: calculation expression references weights from source \"${pair.source}\", which is not registered or has no \\`resolveWeights\\`.`\n\t\t\t)\n\t\t}\n\t\tconst instance = fields.find((field) => field.name === pair.field)\n\t\tresolvedWeights[key] = instance\n\t\t\t? await source.resolveWeights({\n\t\t\t\t\tform,\n\t\t\t\t\tpayload,\n\t\t\t\t\treq,\n\t\t\t\t\tfield: instance as CalcWeightResolveArgs['field'],\n\t\t\t\t})\n\t\t\t: {}\n\t}\n\treturn {\n\t\t...(usage.scalars.size > 0 ? { sources: resolvedSources } : {}),\n\t\t...(usage.weightPairs.size > 0 ? { weights: resolvedWeights } : {}),\n\t}\n}\n"],"mappings":";;;AAaA,MAAM,eAAe,MAAsB,OAAwB,UAAwB;CAC1F,IAAI,QAAA,MAAqB,QAAQ,QAAQ,OAAO,SAAS,UAAU;CACnE,QAAQ,KAAK,MAAb;EACC,KAAK;GACJ,MAAM,QAAQ,IAAI,KAAK,MAAM;GAC7B;EACD,KAAK;GACJ,IAAI,OAAO,KAAK,WAAW,UAC1B,MAAM,YAAY,IAAI,cAAc,KAAK,QAAQ,KAAK,KAAK,GAAG;IAC7D,QAAQ,KAAK;IACb,OAAO,KAAK;GACb,CAAC;GAEF;EACD,KAAK;GACJ,YAAY,KAAK,MAAM,OAAO,QAAQ,CAAC;GACvC,YAAY,KAAK,OAAO,OAAO,QAAQ,CAAC;GACxC;EACD,KAAK;GACJ,YAAY,KAAK,SAAS,OAAO,QAAQ,CAAC;GAC1C;EACD,KAAK;GACJ,IAAI,MAAM,QAAQ,KAAK,IAAI,GAC1B,KAAK,MAAM,OAAO,KAAK,MAAM,YAAY,KAAK,OAAO,QAAQ,CAAC;GAE/D;EACD,SACC;CACF;AACD;AAEA,MAAM,WAAW,WAAiD;CACjE,MAAM,QAAyB;EAAE,yBAAS,IAAI,IAAI;EAAG,6BAAa,IAAI,IAAI;CAAE;CAC5E,KAAK,MAAM,SAAS,QAAQ;EAC3B,MAAM,OAAO,iBAAiB,KAAK;EACnC,IAAI,MAAM,YAAY,MAAM,OAAO,CAAC;CACrC;CACA,OAAO;AACR;;AAGA,MAAa,mBAAmB,WAAyC;CACxE,MAAM,QAAQ,QAAQ,MAAM;CAC5B,OAAO,MAAM,QAAQ,OAAO,KAAK,MAAM,YAAY,OAAO;AAC3D;;;;;;;;;;AAoBA,MAAa,qBAAqB,OAAO,EACxC,QACA,SACA,MACA,SACA,UACoD;CACpD,MAAM,QAAQ,QAAQ,MAAM;CAC5B,IAAI,MAAM,QAAQ,SAAS,KAAK,MAAM,YAAY,SAAS,GAC1D,OAAO,CAAC;CAET,MAAM,kBAA0C,CAAC;CACjD,KAAK,MAAM,OAAO,MAAM,SAAS;EAChC,MAAM,SAAS,QAAQ;EACvB,IAAI,CAAC,QAAQ,SACZ,MAAM,IAAI,MACT,sEAAsE,IAAI,kDAC3E;EAED,gBAAgB,OAAO,MAAM,OAAO,QAAQ;GAAE;GAAM;GAAS;EAAI,CAAC;CACnE;CACA,MAAM,kBAA0D,CAAC;CACjE,KAAK,MAAM,CAAC,KAAK,SAAS,MAAM,aAAa;EAC5C,MAAM,SAAS,QAAQ,KAAK;EAC5B,IAAI,CAAC,QAAQ,gBACZ,MAAM,IAAI,MACT,mFAAmF,KAAK,OAAO,yDAChG;EAED,MAAM,WAAW,OAAO,MAAM,UAAU,MAAM,SAAS,KAAK,KAAK;EACjE,gBAAgB,OAAO,WACpB,MAAM,OAAO,eAAe;GAC5B;GACA;GACA;GACA,OAAO;EACR,CAAC,IACA,CAAC;CACL;CACA,OAAO;EACN,GAAI,MAAM,QAAQ,OAAO,IAAI,EAAE,SAAS,gBAAgB,IAAI,CAAC;EAC7D,GAAI,MAAM,YAAY,OAAO,IAAI,EAAE,SAAS,gBAAgB,IAAI,CAAC;CAClE;AACD"}
@@ -5,7 +5,18 @@ type CalcOp = (typeof CALC_OPS)[number];
5
5
  /** Canonical function grammar; same single source of truth as `CALC_OPS`. */
6
6
  declare const CALC_FNS: readonly ["min", "max", "round", "abs", "ceil", "floor"];
7
7
  type CalcFn = (typeof CALC_FNS)[number];
8
- /** A serializable, safe-by-construction calculation expression. No strings are parsed; the evaluator tree-walks this closed node set (never `eval`). */
8
+ /**
9
+ * A function slot in the AST: a built-in, or a host-registered function name (plugin option
10
+ * `calc.functions`). The intersection trick keeps built-in autocomplete while admitting any
11
+ * registered key; `normalizeCalc` gates which names are actually valid.
12
+ */
13
+ type CalcFnName = CalcFn | (string & {});
14
+ /**
15
+ * A serializable, safe-by-construction calculation expression. No strings are parsed; the evaluator
16
+ * tree-walks this closed node set (never `eval`). `source` reads a server-resolved scalar from a
17
+ * registered calc source; a `weight` node with `source` reads its per-option map from a registered
18
+ * weight resolver instead of the inline `weights` (which is then ignored).
19
+ */
9
20
  type CalcExpression = {
10
21
  type: 'lit';
11
22
  value: number;
@@ -22,12 +33,16 @@ type CalcExpression = {
22
33
  operand: CalcExpression;
23
34
  } | {
24
35
  type: 'fn';
25
- fn: CalcFn;
36
+ fn: CalcFnName;
26
37
  args: CalcExpression[];
38
+ } | {
39
+ type: 'source';
40
+ source: string;
27
41
  } | {
28
42
  type: 'weight';
29
43
  field: string;
30
- weights: Record<string, number>;
44
+ weights?: Record<string, number>;
45
+ source?: string;
31
46
  };
32
47
  //#endregion
33
48
  export { CalcExpression };
@@ -16,7 +16,8 @@ const CALC_FNS = [
16
16
  "ceil",
17
17
  "floor"
18
18
  ];
19
+ const isCalcFn = (v) => CALC_FNS.includes(v);
19
20
  //#endregion
20
- export { CALC_FNS, CALC_OPS };
21
+ export { CALC_FNS, CALC_OPS, isCalcFn };
21
22
 
22
23
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../src/calc/types.ts"],"sourcesContent":["/** Canonical operator grammar; `CalcExpression`, `normalizeCalc`, and the admin editor's jsonSchema all derive from this list. */\nexport const CALC_OPS = ['+', '-', '*', '/', '%'] as const\nexport type CalcOp = (typeof CALC_OPS)[number]\n\n/** Canonical function grammar; same single source of truth as `CALC_OPS`. */\nexport const CALC_FNS = ['min', 'max', 'round', 'abs', 'ceil', 'floor'] as const\nexport type CalcFn = (typeof CALC_FNS)[number]\n\n/** Recursion guard shared by `normalizeCalc` (parse) and `evaluate` (walk) so both bound depth identically. */\nexport const MAX_DEPTH = 64\n\n/** A serializable, safe-by-construction calculation expression. No strings are parsed; the evaluator tree-walks this closed node set (never `eval`). */\nexport type CalcExpression =\n\t| { type: 'lit'; value: number }\n\t| { type: 'ref'; field: string }\n\t| { type: 'op'; op: CalcOp; left: CalcExpression; right: CalcExpression }\n\t| { type: 'neg'; operand: CalcExpression }\n\t| { type: 'fn'; fn: CalcFn; args: CalcExpression[] }\n\t| { type: 'weight'; field: string; weights: Record<string, number> }\n"],"mappings":";;AACA,MAAa,WAAW;CAAC;CAAK;CAAK;CAAK;CAAK;AAAG;;AAIhD,MAAa,WAAW;CAAC;CAAO;CAAO;CAAS;CAAO;CAAQ;AAAO"}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../src/calc/types.ts"],"sourcesContent":["/** Canonical operator grammar; `CalcExpression`, `normalizeCalc`, and the admin editor's jsonSchema all derive from this list. */\nexport const CALC_OPS = ['+', '-', '*', '/', '%'] as const\nexport type CalcOp = (typeof CALC_OPS)[number]\n\n/** Canonical function grammar; same single source of truth as `CALC_OPS`. */\nexport const CALC_FNS = ['min', 'max', 'round', 'abs', 'ceil', 'floor'] as const\nexport type CalcFn = (typeof CALC_FNS)[number]\n\nexport const isCalcFn = (v: unknown): v is CalcFn => (CALC_FNS as readonly unknown[]).includes(v)\n\n/**\n * A function slot in the AST: a built-in, or a host-registered function name (plugin option\n * `calc.functions`). The intersection trick keeps built-in autocomplete while admitting any\n * registered key; `normalizeCalc` gates which names are actually valid.\n */\nexport type CalcFnName = CalcFn | (string & {})\n\n/** Recursion guard shared by `normalizeCalc` (parse) and `evaluate` (walk) so both bound depth identically. */\nexport const MAX_DEPTH = 64\n\n/**\n * A serializable, safe-by-construction calculation expression. No strings are parsed; the evaluator\n * tree-walks this closed node set (never `eval`). `source` reads a server-resolved scalar from a\n * registered calc source; a `weight` node with `source` reads its per-option map from a registered\n * weight resolver instead of the inline `weights` (which is then ignored).\n */\nexport type CalcExpression =\n\t| { type: 'lit'; value: number }\n\t| { type: 'ref'; field: string }\n\t| { type: 'op'; op: CalcOp; left: CalcExpression; right: CalcExpression }\n\t| { type: 'neg'; operand: CalcExpression }\n\t| { type: 'fn'; fn: CalcFnName; args: CalcExpression[] }\n\t| { type: 'source'; source: string }\n\t| { type: 'weight'; field: string; weights?: Record<string, number>; source?: string }\n"],"mappings":";;AACA,MAAa,WAAW;CAAC;CAAK;CAAK;CAAK;CAAK;AAAG;;AAIhD,MAAa,WAAW;CAAC;CAAO;CAAO;CAAS;CAAO;CAAQ;AAAO;AAGtE,MAAa,YAAY,MAA6B,SAAgC,SAAS,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { CalcSourceClientMeta } from "../fields/builtin/calculation.js";
2
+ //#region src/client/CalcExpressionBuilder.d.ts
3
+ type CalcExpressionBuilderProps = {
4
+ path?: string;
5
+ field?: {
6
+ label?: unknown;
7
+ };
8
+ readOnly?: boolean; /** Registered calc-source metadata (key, label, modes), threaded as clientProps by `buildCalculationField`. */
9
+ sources?: CalcSourceClientMeta[]; /** Registered custom calc function names, so client-side normalization accepts what the server accepts. */
10
+ functions?: string[];
11
+ };
12
+ /**
13
+ * Visual editor for the calculation field's `expression` AST, presented as a left-to-right chain.
14
+ * Storage shape is unchanged and `validateExpression` stays the server gate; the AST remains
15
+ * authorable via the API.
16
+ */
17
+ declare const CalcExpressionBuilder: (props: CalcExpressionBuilderProps) => import("react/jsx-runtime").JSX.Element;
18
+ //#endregion
19
+ export { CalcExpressionBuilder };
20
+ //# sourceMappingURL=CalcExpressionBuilder.d.ts.map