@aglyn/plugins-logic 1.0.0-beta.246 → 1.0.0-beta.249

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 (29) hide show
  1. package/package.json +8 -8
  2. package/src/lib/components/formula-builder-dialog.component.d.ts +33 -0
  3. package/src/lib/components/formula-builder-dialog.component.js +331 -0
  4. package/src/lib/components/formula-builder-dialog.component.js.map +1 -0
  5. package/src/lib/components/formula-builder.component.d.ts +30 -0
  6. package/src/lib/components/formula-builder.component.js +62 -0
  7. package/src/lib/components/formula-builder.component.js.map +1 -0
  8. package/src/lib/components/host-functions-card.component.js +167 -62
  9. package/src/lib/components/host-functions-card.component.js.map +1 -1
  10. package/src/lib/components/host-variables-card.component.js +14 -9
  11. package/src/lib/components/host-variables-card.component.js.map +1 -1
  12. package/src/lib/components/parameter-choices-editor.component.d.ts +31 -0
  13. package/src/lib/components/parameter-choices-editor.component.js +124 -0
  14. package/src/lib/components/parameter-choices-editor.component.js.map +1 -0
  15. package/src/lib/components/structured-value-editor.component.d.ts +30 -0
  16. package/src/lib/components/structured-value-editor.component.js +189 -0
  17. package/src/lib/components/structured-value-editor.component.js.map +1 -0
  18. package/src/lib/model/formula-names.d.ts +41 -0
  19. package/src/lib/model/formula-names.js +82 -0
  20. package/src/lib/model/formula-names.js.map +1 -0
  21. package/src/lib/model/formula.d.ts +72 -0
  22. package/src/lib/model/formula.js +264 -0
  23. package/src/lib/model/formula.js.map +1 -0
  24. package/src/lib/model/parameter-options.d.ts +16 -0
  25. package/src/lib/model/parameter-options.js +31 -0
  26. package/src/lib/model/parameter-options.js.map +1 -1
  27. package/src/lib/model/structured-value.d.ts +51 -0
  28. package/src/lib/model/structured-value.js +135 -0
  29. package/src/lib/model/structured-value.js.map +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/logic/src/lib/components/structured-value-editor.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport {\n Alert,\n Button,\n IconButton,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useState } from 'react'\nimport {\n duplicateStructuredKeys,\n parseStructuredValue,\n serializeStructuredValue,\n type StructuredValueKind,\n type StructuredValueRow,\n} from '../model/structured-value'\n\nconst KIND_LABELS: Record<Exclude<StructuredValueKind, 'kept'>, string> = {\n text: 'Text',\n number: 'Number',\n boolean: 'True/false',\n}\n\nexport interface StructuredValueEditorProps {\n shape: 'dictionary' | 'collection'\n /** The stored JSON string; read once, when the editor opens. */\n value: string\n onChange: (value: string) => void\n}\n\n/**\n * A dictionary as a Key / Value table and a collection as a list\n * (AGL-3757): each row typed Text, Number or True/false, written back as the\n * JSON string the variable has always stored. The rows are this editor's own\n * state, so a row whose key is still being typed is not dropped mid-edit.\n */\nexport function StructuredValueEditor(props: StructuredValueEditorProps) {\n const { shape, value, onChange } = props\n const [initial] = useState(() => parseStructuredValue(value, shape))\n const [rows, setRows] = useState<StructuredValueRow[]>(initial.rows)\n const duplicates = shape === 'dictionary' ? duplicateStructuredKeys(rows) : new Set<string>()\n\n const commit = (next: StructuredValueRow[]) => {\n setRows(next)\n onChange(serializeStructuredValue(next, shape))\n }\n const update = (index: number, patch: Partial<StructuredValueRow>) =>\n commit(rows.map((row, at) => (at === index ? { ...row, ...patch } : row)))\n\n const noun = shape === 'dictionary' ? 'entry' : 'item'\n return (\n <Stack spacing={1}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {shape === 'dictionary'\n ? 'Each entry is a name and its value. A function reads one as variable.name.'\n : 'Each item is one value in the list, in order.'}\n </Typography>\n {initial.unreadable ? (\n <Alert severity=\"warning\">\n {'The saved value could not be read as a ' +\n (shape === 'dictionary' ? 'set of entries' : 'list') +\n '. Add its values below; saving replaces it.'}\n </Alert>\n ) : null}\n {rows.map((row, index) => (\n <Stack\n key={index}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n {shape === 'dictionary' ? (\n <TextField\n label=\"Entry name\"\n value={row.key}\n onChange={(event) => update(index, { key: event.target.value })}\n error={duplicates.has(row.key.trim())}\n helperText={\n duplicates.has(row.key.trim()) ? 'Used twice — the last one wins' : undefined\n }\n size=\"small\"\n sx={{ flex: 3, minWidth: 0 }}\n />\n ) : (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ width: 20, flexShrink: 0 }}\n >\n {`${index + 1}`}\n </Typography>\n )}\n {row.kind === 'kept' ? (\n <Typography\n variant=\"body2\"\n color=\"text.secondary\"\n sx={{ flex: 5, minWidth: 0 }}\n noWrap\n title={row.value}\n >\n {'A nested list or set of entries — kept as it is'}\n </Typography>\n ) : (\n <>\n <TextField\n select\n label=\"Type\"\n value={row.kind}\n onChange={(event) => {\n const kind = event.target.value as StructuredValueKind\n update(index, {\n kind,\n value:\n kind === 'boolean'\n ? row.value === 'true'\n ? 'true'\n : 'false'\n : row.kind === 'boolean'\n ? ''\n : row.value,\n })\n }}\n size=\"small\"\n sx={{ flex: 2, minWidth: 108 }}\n >\n {Object.entries(KIND_LABELS).map(([kind, label]) => (\n <MenuItem key={kind} value={kind}>\n {label}\n </MenuItem>\n ))}\n </TextField>\n {row.kind === 'boolean' ? (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flex: 3, minWidth: 0 }}\n >\n <Switch\n checked={row.value === 'true'}\n onChange={(event) =>\n update(index, {\n value: event.target.checked ? 'true' : 'false',\n })\n }\n slotProps={{ input: { 'aria-label': `Value ${index + 1}` } }}\n />\n <Typography variant=\"body2\">\n {row.value === 'true' ? 'True' : 'False'}\n </Typography>\n </Stack>\n ) : (\n <TextField\n label=\"Value\"\n value={row.value}\n onChange={(event) =>\n update(index, {\n value:\n row.kind === 'number'\n ? event.target.value.replace(/[^0-9.eE+-]/g, '')\n : event.target.value,\n })\n }\n slotProps={\n row.kind === 'number'\n ? { htmlInput: { inputMode: 'decimal' } }\n : undefined\n }\n size=\"small\"\n sx={{ flex: 3, minWidth: 0 }}\n />\n )}\n </>\n )}\n <IconButton\n size=\"small\"\n aria-label={`Remove ${noun} ${index + 1}`}\n onClick={() => commit(rows.filter((_, at) => at !== index))}\n >\n {'×'}\n </IconButton>\n </Stack>\n ))}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => commit([...rows, { key: '', kind: 'text', value: '' }])}\n >\n {shape === 'dictionary' ? 'Add entry' : 'Add item'}\n </Button>\n </Stack>\n )\n}\n\nexport default StructuredValueEditor\n"],"names":["Alert","Button","IconButton","MenuItem","Stack","Switch","TextField","Typography","useState","duplicateStructuredKeys","parseStructuredValue","serializeStructuredValue","KIND_LABELS","text","number","boolean","StructuredValueEditor","props","shape","value","onChange","initial","rows","setRows","duplicates","Set","commit","next","update","index","patch","map","row","at","noun","spacing","variant","color","unreadable","severity","direction","sx","alignItems","label","key","event","target","error","has","trim","helperText","undefined","size","flex","minWidth","width","flexShrink","kind","noWrap","title","select","Object","entries","checked","slotProps","input","replace","htmlInput","inputMode","aria-label","onClick","filter","_","alignSelf"],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /** A name a formula can read, grouped the way the picker lists it. */
18
+ export interface FormulaName {
19
+ value: string;
20
+ label: string;
21
+ group: string;
22
+ }
23
+ /**
24
+ * Everything a function's formula can read, in the order the evaluator
25
+ * resolves a clash: its parameters, its own variables, then the site's
26
+ * variables — each dictionary's members listed under it as `name.member`,
27
+ * since that is how a step reads one (AGL-3202).
28
+ */
29
+ export declare function formulaNames(input: {
30
+ parameters: readonly {
31
+ name: string;
32
+ }[];
33
+ locals: readonly {
34
+ name: string;
35
+ }[];
36
+ siteVariables: readonly {
37
+ name?: string;
38
+ type?: string;
39
+ value?: string;
40
+ }[];
41
+ }): FormulaName[];
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /** A name a formula can read, grouped the way the picker lists it. */ const VALID_NAME = /^[a-zA-Z_][a-zA-Z0-9_]{0,39}$/;
17
+ /**
18
+ * Everything a function's formula can read, in the order the evaluator
19
+ * resolves a clash: its parameters, its own variables, then the site's
20
+ * variables — each dictionary's members listed under it as `name.member`,
21
+ * since that is how a step reads one (AGL-3202).
22
+ */ export function formulaNames(input) {
23
+ const names = [];
24
+ const seen = new Set();
25
+ const push = (value, label, group)=>{
26
+ if (seen.has(value)) return;
27
+ seen.add(value);
28
+ names.push({
29
+ value,
30
+ label,
31
+ group
32
+ });
33
+ };
34
+ for (const parameter of input.parameters){
35
+ if (VALID_NAME.test(parameter.name)) {
36
+ push(parameter.name, parameter.name, 'Parameters');
37
+ }
38
+ }
39
+ for (const local of input.locals){
40
+ if (VALID_NAME.test(local.name)) {
41
+ push(local.name, local.name, 'This function’s variables');
42
+ }
43
+ }
44
+ const sorted = [
45
+ ...input.siteVariables
46
+ ].sort((a, b)=>{
47
+ var _a_name, _b_name;
48
+ return String((_a_name = a.name) != null ? _a_name : '').localeCompare(String((_b_name = b.name) != null ? _b_name : ''));
49
+ });
50
+ for (const variable of sorted){
51
+ var _variable_name;
52
+ const name = String((_variable_name = variable.name) != null ? _variable_name : '');
53
+ if (!VALID_NAME.test(name)) continue;
54
+ if (variable.type === 'collection') continue;
55
+ if (variable.type !== 'dictionary') {
56
+ push(name, name, 'Site variables');
57
+ continue;
58
+ }
59
+ let entries = null;
60
+ try {
61
+ var _variable_value;
62
+ const parsed = JSON.parse(String((_variable_value = variable.value) != null ? _variable_value : ''));
63
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
64
+ entries = parsed;
65
+ }
66
+ } catch (unused) {
67
+ entries = null;
68
+ }
69
+ for (const [member, value] of Object.entries(entries != null ? entries : {})){
70
+ if (!VALID_NAME.test(member)) continue;
71
+ if (![
72
+ 'number',
73
+ 'string',
74
+ 'boolean'
75
+ ].includes(typeof value)) continue;
76
+ push(`${name}.${member}`, `${name} → ${member}`, 'Site variables');
77
+ }
78
+ }
79
+ return names;
80
+ }
81
+
82
+ //# sourceMappingURL=formula-names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/logic/src/lib/model/formula-names.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\n/** A name a formula can read, grouped the way the picker lists it. */\nexport interface FormulaName {\n value: string\n label: string\n group: string\n}\n\nconst VALID_NAME = /^[a-zA-Z_][a-zA-Z0-9_]{0,39}$/\n\n/**\n * Everything a function's formula can read, in the order the evaluator\n * resolves a clash: its parameters, its own variables, then the site's\n * variables — each dictionary's members listed under it as `name.member`,\n * since that is how a step reads one (AGL-3202).\n */\nexport function formulaNames(input: {\n parameters: readonly { name: string }[]\n locals: readonly { name: string }[]\n siteVariables: readonly { name?: string; type?: string; value?: string }[]\n}): FormulaName[] {\n const names: FormulaName[] = []\n const seen = new Set<string>()\n const push = (value: string, label: string, group: string) => {\n if (seen.has(value)) return\n seen.add(value)\n names.push({ value, label, group })\n }\n for (const parameter of input.parameters) {\n if (VALID_NAME.test(parameter.name)) {\n push(parameter.name, parameter.name, 'Parameters')\n }\n }\n for (const local of input.locals) {\n if (VALID_NAME.test(local.name)) {\n push(local.name, local.name, 'This function’s variables')\n }\n }\n const sorted = [...input.siteVariables].sort((a, b) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n for (const variable of sorted) {\n const name = String(variable.name ?? '')\n if (!VALID_NAME.test(name)) continue\n if (variable.type === 'collection') continue\n if (variable.type !== 'dictionary') {\n push(name, name, 'Site variables')\n continue\n }\n let entries: Record<string, unknown> | null = null\n try {\n const parsed: unknown = JSON.parse(String(variable.value ?? ''))\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n entries = parsed as Record<string, unknown>\n }\n } catch {\n entries = null\n }\n for (const [member, value] of Object.entries(entries ?? {})) {\n if (!VALID_NAME.test(member)) continue\n if (!['number', 'string', 'boolean'].includes(typeof value)) continue\n push(`${name}.${member}`, `${name} → ${member}`, 'Site variables')\n }\n }\n return names\n}\n"],"names":["VALID_NAME","formulaNames","input","names","seen","Set","push","value","label","group","has","add","parameter","parameters","test","name","local","locals","sorted","siteVariables","sort","a","b","String","localeCompare","variable","type","entries","parsed","JSON","parse","Array","isArray","member","Object","includes"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,oEAAoE,GAOpE,MAAMA,aAAa;AAEnB;;;;;CAKC,GACD,OAAO,SAASC,aAAaC,KAI5B;IACC,MAAMC,QAAuB,EAAE;IAC/B,MAAMC,OAAO,IAAIC;IACjB,MAAMC,OAAO,CAACC,OAAeC,OAAeC;QAC1C,IAAIL,KAAKM,GAAG,CAACH,QAAQ;QACrBH,KAAKO,GAAG,CAACJ;QACTJ,MAAMG,IAAI,CAAC;YAAEC;YAAOC;YAAOC;QAAM;IACnC;IACA,KAAK,MAAMG,aAAaV,MAAMW,UAAU,CAAE;QACxC,IAAIb,WAAWc,IAAI,CAACF,UAAUG,IAAI,GAAG;YACnCT,KAAKM,UAAUG,IAAI,EAAEH,UAAUG,IAAI,EAAE;QACvC;IACF;IACA,KAAK,MAAMC,SAASd,MAAMe,MAAM,CAAE;QAChC,IAAIjB,WAAWc,IAAI,CAACE,MAAMD,IAAI,GAAG;YAC/BT,KAAKU,MAAMD,IAAI,EAAEC,MAAMD,IAAI,EAAE;QAC/B;IACF;IACA,MAAMG,SAAS;WAAIhB,MAAMiB,aAAa;KAAC,CAACC,IAAI,CAAC,CAACC,GAAGC;YACxCD,SAAmCC;eAA1CC,QAAOF,UAAAA,EAAEN,IAAI,YAANM,UAAU,IAAIG,aAAa,CAACD,QAAOD,UAAAA,EAAEP,IAAI,YAANO,UAAU;;IAEtD,KAAK,MAAMG,YAAYP,OAAQ;YACTO;QAApB,MAAMV,OAAOQ,QAAOE,iBAAAA,SAASV,IAAI,YAAbU,iBAAiB;QACrC,IAAI,CAACzB,WAAWc,IAAI,CAACC,OAAO;QAC5B,IAAIU,SAASC,IAAI,KAAK,cAAc;QACpC,IAAID,SAASC,IAAI,KAAK,cAAc;YAClCpB,KAAKS,MAAMA,MAAM;YACjB;QACF;QACA,IAAIY,UAA0C;QAC9C,IAAI;gBACwCF;YAA1C,MAAMG,SAAkBC,KAAKC,KAAK,CAACP,QAAOE,kBAAAA,SAASlB,KAAK,YAAdkB,kBAAkB;YAC5D,IAAIG,UAAU,OAAOA,WAAW,YAAY,CAACG,MAAMC,OAAO,CAACJ,SAAS;gBAClED,UAAUC;YACZ;QACF,EAAE,eAAM;YACND,UAAU;QACZ;QACA,KAAK,MAAM,CAACM,QAAQ1B,MAAM,IAAI2B,OAAOP,OAAO,CAACA,kBAAAA,UAAW,CAAC,GAAI;YAC3D,IAAI,CAAC3B,WAAWc,IAAI,CAACmB,SAAS;YAC9B,IAAI,CAAC;gBAAC;gBAAU;gBAAU;aAAU,CAACE,QAAQ,CAAC,OAAO5B,QAAQ;YAC7DD,KAAK,GAAGS,KAAK,CAAC,EAAEkB,QAAQ,EAAE,GAAGlB,KAAK,GAAG,EAAEkB,QAAQ,EAAE;QACnD;IACF;IACA,OAAO9B;AACT"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * A function step's expression, built by picking an operation and its values
19
+ * instead of typing it (AGL-3757). What comes out is the SAME expression an
20
+ * author could have typed — the evaluator, the stored definition and every
21
+ * page that runs it are untouched; this only writes the text.
22
+ */
23
+ export type FormulaOperation = 'value' | 'add' | 'subtract' | 'multiply' | 'divide' | 'min' | 'max' | 'round' | 'roundDown' | 'roundUp' | 'absolute' | 'currency' | 'number' | 'join';
24
+ /** One value an operation works on. */
25
+ export type FormulaOperand =
26
+ /** A parameter, a local variable, a site variable or a dictionary member. */
27
+ {
28
+ source: 'name';
29
+ name: string;
30
+ } | {
31
+ source: 'number';
32
+ value: string;
33
+ } | {
34
+ source: 'text';
35
+ value: string;
36
+ }
37
+ /** Whatever the box holds already, so formulas can be layered. */
38
+ | {
39
+ source: 'current';
40
+ };
41
+ export interface FormulaDraft {
42
+ operation: FormulaOperation;
43
+ operands: FormulaOperand[];
44
+ /** Decimal places for round, currency and number. */
45
+ places?: number;
46
+ /** The sign written before a currency amount. */
47
+ symbol?: string;
48
+ /** What goes between joined texts. */
49
+ separator?: string;
50
+ }
51
+ interface FormulaOperationSpec {
52
+ label: string;
53
+ /** How many values it takes at least; `variadic` ones take more. */
54
+ minOperands: number;
55
+ variadic?: boolean;
56
+ /** What each value box is called, in order; the last repeats. */
57
+ operandLabels: string[];
58
+ places?: boolean;
59
+ }
60
+ export declare const FORMULA_OPERATIONS: Record<FormulaOperation, FormulaOperationSpec>;
61
+ /** The operations, in the order the picker lists them. */
62
+ export declare const FORMULA_OPERATION_ORDER: FormulaOperation[];
63
+ /** The label for a value box at `index` of `operation`. */
64
+ export declare function formulaOperandLabel(operation: FormulaOperation, index: number): string;
65
+ /**
66
+ * The expression a draft writes, or `null` while a value it needs is blank.
67
+ * `current` is what the box held when the builder opened.
68
+ */
69
+ export declare function buildFormula(draft: FormulaDraft, current?: string): string | null;
70
+ /** A fresh draft for `operation`, carrying over the values already chosen. */
71
+ export declare function formulaDraftFor(operation: FormulaOperation, previous?: FormulaDraft, current?: string): FormulaDraft;
72
+ export {};
@@ -0,0 +1,264 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * A function step's expression, built by picking an operation and its values
18
+ * instead of typing it (AGL-3757). What comes out is the SAME expression an
19
+ * author could have typed — the evaluator, the stored definition and every
20
+ * page that runs it are untouched; this only writes the text.
21
+ */ export const FORMULA_OPERATIONS = {
22
+ value: {
23
+ label: 'Use a value',
24
+ minOperands: 1,
25
+ operandLabels: [
26
+ 'Value'
27
+ ]
28
+ },
29
+ add: {
30
+ label: 'Add',
31
+ minOperands: 2,
32
+ variadic: true,
33
+ operandLabels: [
34
+ 'Value',
35
+ 'Plus'
36
+ ]
37
+ },
38
+ subtract: {
39
+ label: 'Subtract',
40
+ minOperands: 2,
41
+ operandLabels: [
42
+ 'Value',
43
+ 'Minus'
44
+ ]
45
+ },
46
+ multiply: {
47
+ label: 'Multiply',
48
+ minOperands: 2,
49
+ variadic: true,
50
+ operandLabels: [
51
+ 'Value',
52
+ 'Times'
53
+ ]
54
+ },
55
+ divide: {
56
+ label: 'Divide',
57
+ minOperands: 2,
58
+ operandLabels: [
59
+ 'Value',
60
+ 'Divided by'
61
+ ]
62
+ },
63
+ min: {
64
+ label: 'Smallest of',
65
+ minOperands: 2,
66
+ variadic: true,
67
+ operandLabels: [
68
+ 'Value',
69
+ 'Or'
70
+ ]
71
+ },
72
+ max: {
73
+ label: 'Largest of',
74
+ minOperands: 2,
75
+ variadic: true,
76
+ operandLabels: [
77
+ 'Value',
78
+ 'Or'
79
+ ]
80
+ },
81
+ round: {
82
+ label: 'Round',
83
+ minOperands: 1,
84
+ operandLabels: [
85
+ 'Value'
86
+ ],
87
+ places: true
88
+ },
89
+ roundDown: {
90
+ label: 'Round down',
91
+ minOperands: 1,
92
+ operandLabels: [
93
+ 'Value'
94
+ ]
95
+ },
96
+ roundUp: {
97
+ label: 'Round up',
98
+ minOperands: 1,
99
+ operandLabels: [
100
+ 'Value'
101
+ ]
102
+ },
103
+ absolute: {
104
+ label: 'Without its minus sign',
105
+ minOperands: 1,
106
+ operandLabels: [
107
+ 'Value'
108
+ ]
109
+ },
110
+ currency: {
111
+ label: 'Format as money',
112
+ minOperands: 1,
113
+ operandLabels: [
114
+ 'Amount'
115
+ ],
116
+ places: true
117
+ },
118
+ number: {
119
+ label: 'Format with thousands separators',
120
+ minOperands: 1,
121
+ operandLabels: [
122
+ 'Number'
123
+ ],
124
+ places: true
125
+ },
126
+ join: {
127
+ label: 'Join text',
128
+ minOperands: 2,
129
+ variadic: true,
130
+ operandLabels: [
131
+ 'Text',
132
+ 'Then'
133
+ ]
134
+ }
135
+ };
136
+ /** The operations, in the order the picker lists them. */ export const FORMULA_OPERATION_ORDER = Object.keys(FORMULA_OPERATIONS);
137
+ /** The label for a value box at `index` of `operation`. */ export function formulaOperandLabel(operation, index) {
138
+ const labels = FORMULA_OPERATIONS[operation].operandLabels;
139
+ return labels[Math.min(index, labels.length - 1)];
140
+ }
141
+ /**
142
+ * A text as the evaluator reads it. Its strings have no escapes, so the
143
+ * quote the text does not contain is used; a text holding both loses its
144
+ * double quotes rather than writing an expression that cannot be read.
145
+ */ function quoteText(value) {
146
+ if (!value.includes("'")) return `'${value}'`;
147
+ if (!value.includes('"')) return `"${value}"`;
148
+ return `'${value.replace(/"/g, '')}'`;
149
+ }
150
+ const SIMPLE_EXPRESSION = /^\s*(?:-?[0-9]*\.?[0-9]+|[a-zA-Z_][a-zA-Z0-9_.]*)\s*$/;
151
+ /**
152
+ * One operand as expression text, or `null` while it is still blank. `bare`
153
+ * is a built-in's argument, which its own parentheses already delimit.
154
+ */ function operandText(operand, current, bare = false) {
155
+ switch(operand.source){
156
+ case 'name':
157
+ return operand.name ? operand.name : null;
158
+ case 'number':
159
+ {
160
+ const text = operand.value.trim();
161
+ if (!text) return null;
162
+ const parsed = Number(text);
163
+ return Number.isFinite(parsed) ? String(parsed) : null;
164
+ }
165
+ case 'text':
166
+ return quoteText(operand.value);
167
+ case 'current':
168
+ {
169
+ const text = current.trim();
170
+ if (!text) return null;
171
+ return bare || SIMPLE_EXPRESSION.test(text) ? text : `(${text})`;
172
+ }
173
+ }
174
+ }
175
+ function placesOf(draft, fallback) {
176
+ var _draft_places;
177
+ const places = Number((_draft_places = draft.places) != null ? _draft_places : fallback);
178
+ return Number.isInteger(places) && places >= 0 && places <= 6 ? places : fallback;
179
+ }
180
+ /**
181
+ * The expression a draft writes, or `null` while a value it needs is blank.
182
+ * `current` is what the box held when the builder opened.
183
+ */ export function buildFormula(draft, current = '') {
184
+ const spec = FORMULA_OPERATIONS[draft.operation];
185
+ const operands = draft.operands.slice(0, spec.variadic ? draft.operands.length : spec.minOperands);
186
+ if (operands.length < spec.minOperands) return null;
187
+ const arithmetic = [
188
+ 'add',
189
+ 'subtract',
190
+ 'multiply',
191
+ 'divide',
192
+ 'join'
193
+ ].includes(draft.operation);
194
+ const parts = operands.map((operand)=>operandText(operand, current, !arithmetic && draft.operation !== 'value'));
195
+ if (parts.some((part)=>part === null)) return null;
196
+ const values = parts;
197
+ const [first] = values;
198
+ switch(draft.operation){
199
+ case 'value':
200
+ return first;
201
+ case 'add':
202
+ return values.join(' + ');
203
+ case 'subtract':
204
+ return values.join(' - ');
205
+ case 'multiply':
206
+ return values.join(' * ');
207
+ case 'divide':
208
+ return values.join(' / ');
209
+ case 'min':
210
+ return `min(${values.join(', ')})`;
211
+ case 'max':
212
+ return `max(${values.join(', ')})`;
213
+ case 'round':
214
+ return `round(${first}, ${placesOf(draft, 0)})`;
215
+ case 'roundDown':
216
+ return `floor(${first})`;
217
+ case 'roundUp':
218
+ return `ceil(${first})`;
219
+ case 'absolute':
220
+ return `abs(${first})`;
221
+ case 'currency':
222
+ var _draft_symbol;
223
+ return `${quoteText((_draft_symbol = draft.symbol) != null ? _draft_symbol : '$')} + format(${first}, ${placesOf(draft, 2)})`;
224
+ case 'number':
225
+ return `format(${first}, ${placesOf(draft, 0)})`;
226
+ case 'join':
227
+ {
228
+ var _draft_separator;
229
+ // A leading '' keeps two numbers joined as text rather than added.
230
+ const separator = (_draft_separator = draft.separator) != null ? _draft_separator : ' ';
231
+ return separator ? values.join(` + ${quoteText(separator)} + `) : [
232
+ '\'\'',
233
+ ...values
234
+ ].join(' + ');
235
+ }
236
+ }
237
+ }
238
+ /** A fresh draft for `operation`, carrying over the values already chosen. */ export function formulaDraftFor(operation, previous, current = '') {
239
+ const spec = FORMULA_OPERATIONS[operation];
240
+ const blank = (index)=>index === 0 && current.trim() ? {
241
+ source: 'current'
242
+ } : operation === 'join' ? {
243
+ source: 'text',
244
+ value: ''
245
+ } : {
246
+ source: 'name',
247
+ name: ''
248
+ };
249
+ const operands = Array.from({
250
+ length: spec.minOperands
251
+ }, (_, index)=>{
252
+ var _ref;
253
+ return (_ref = previous == null ? void 0 : previous.operands[index]) != null ? _ref : blank(index);
254
+ });
255
+ return {
256
+ operation,
257
+ operands,
258
+ places: previous == null ? void 0 : previous.places,
259
+ symbol: previous == null ? void 0 : previous.symbol,
260
+ separator: previous == null ? void 0 : previous.separator
261
+ };
262
+ }
263
+
264
+ //# sourceMappingURL=formula.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/logic/src/lib/model/formula.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A function step's expression, built by picking an operation and its values\n * instead of typing it (AGL-3757). What comes out is the SAME expression an\n * author could have typed — the evaluator, the stored definition and every\n * page that runs it are untouched; this only writes the text.\n */\n\nexport type FormulaOperation =\n | 'value'\n | 'add'\n | 'subtract'\n | 'multiply'\n | 'divide'\n | 'min'\n | 'max'\n | 'round'\n | 'roundDown'\n | 'roundUp'\n | 'absolute'\n | 'currency'\n | 'number'\n | 'join'\n\n/** One value an operation works on. */\nexport type FormulaOperand =\n /** A parameter, a local variable, a site variable or a dictionary member. */\n | { source: 'name'; name: string }\n | { source: 'number'; value: string }\n | { source: 'text'; value: string }\n /** Whatever the box holds already, so formulas can be layered. */\n | { source: 'current' }\n\nexport interface FormulaDraft {\n operation: FormulaOperation\n operands: FormulaOperand[]\n /** Decimal places for round, currency and number. */\n places?: number\n /** The sign written before a currency amount. */\n symbol?: string\n /** What goes between joined texts. */\n separator?: string\n}\n\ninterface FormulaOperationSpec {\n label: string\n /** How many values it takes at least; `variadic` ones take more. */\n minOperands: number\n variadic?: boolean\n /** What each value box is called, in order; the last repeats. */\n operandLabels: string[]\n places?: boolean\n}\n\nexport const FORMULA_OPERATIONS: Record<FormulaOperation, FormulaOperationSpec> = {\n value: { label: 'Use a value', minOperands: 1, operandLabels: ['Value'] },\n add: {\n label: 'Add',\n minOperands: 2,\n variadic: true,\n operandLabels: ['Value', 'Plus'],\n },\n subtract: {\n label: 'Subtract',\n minOperands: 2,\n operandLabels: ['Value', 'Minus'],\n },\n multiply: {\n label: 'Multiply',\n minOperands: 2,\n variadic: true,\n operandLabels: ['Value', 'Times'],\n },\n divide: {\n label: 'Divide',\n minOperands: 2,\n operandLabels: ['Value', 'Divided by'],\n },\n min: {\n label: 'Smallest of',\n minOperands: 2,\n variadic: true,\n operandLabels: ['Value', 'Or'],\n },\n max: {\n label: 'Largest of',\n minOperands: 2,\n variadic: true,\n operandLabels: ['Value', 'Or'],\n },\n round: {\n label: 'Round',\n minOperands: 1,\n operandLabels: ['Value'],\n places: true,\n },\n roundDown: { label: 'Round down', minOperands: 1, operandLabels: ['Value'] },\n roundUp: { label: 'Round up', minOperands: 1, operandLabels: ['Value'] },\n absolute: {\n label: 'Without its minus sign',\n minOperands: 1,\n operandLabels: ['Value'],\n },\n currency: {\n label: 'Format as money',\n minOperands: 1,\n operandLabels: ['Amount'],\n places: true,\n },\n number: {\n label: 'Format with thousands separators',\n minOperands: 1,\n operandLabels: ['Number'],\n places: true,\n },\n join: {\n label: 'Join text',\n minOperands: 2,\n variadic: true,\n operandLabels: ['Text', 'Then'],\n },\n}\n\n/** The operations, in the order the picker lists them. */\nexport const FORMULA_OPERATION_ORDER = Object.keys(\n FORMULA_OPERATIONS,\n) as FormulaOperation[]\n\n/** The label for a value box at `index` of `operation`. */\nexport function formulaOperandLabel(\n operation: FormulaOperation,\n index: number,\n): string {\n const labels = FORMULA_OPERATIONS[operation].operandLabels\n return labels[Math.min(index, labels.length - 1)]\n}\n\n/**\n * A text as the evaluator reads it. Its strings have no escapes, so the\n * quote the text does not contain is used; a text holding both loses its\n * double quotes rather than writing an expression that cannot be read.\n */\nfunction quoteText(value: string): string {\n if (!value.includes(\"'\")) return `'${value}'`\n if (!value.includes('\"')) return `\"${value}\"`\n return `'${value.replace(/\"/g, '')}'`\n}\n\nconst SIMPLE_EXPRESSION = /^\\s*(?:-?[0-9]*\\.?[0-9]+|[a-zA-Z_][a-zA-Z0-9_.]*)\\s*$/\n\n/**\n * One operand as expression text, or `null` while it is still blank. `bare`\n * is a built-in's argument, which its own parentheses already delimit.\n */\nfunction operandText(\n operand: FormulaOperand,\n current: string,\n bare = false,\n): string | null {\n switch (operand.source) {\n case 'name':\n return operand.name ? operand.name : null\n case 'number': {\n const text = operand.value.trim()\n if (!text) return null\n const parsed = Number(text)\n return Number.isFinite(parsed) ? String(parsed) : null\n }\n case 'text':\n return quoteText(operand.value)\n case 'current': {\n const text = current.trim()\n if (!text) return null\n return bare || SIMPLE_EXPRESSION.test(text) ? text : `(${text})`\n }\n }\n}\n\nfunction placesOf(draft: FormulaDraft, fallback: number): number {\n const places = Number(draft.places ?? fallback)\n return Number.isInteger(places) && places >= 0 && places <= 6 ? places : fallback\n}\n\n/**\n * The expression a draft writes, or `null` while a value it needs is blank.\n * `current` is what the box held when the builder opened.\n */\nexport function buildFormula(draft: FormulaDraft, current = ''): string | null {\n const spec = FORMULA_OPERATIONS[draft.operation]\n const operands = draft.operands.slice(\n 0,\n spec.variadic ? draft.operands.length : spec.minOperands,\n )\n if (operands.length < spec.minOperands) return null\n const arithmetic = ['add', 'subtract', 'multiply', 'divide', 'join'].includes(\n draft.operation,\n )\n const parts = operands.map((operand) =>\n operandText(operand, current, !arithmetic && draft.operation !== 'value'),\n )\n if (parts.some((part) => part === null)) return null\n const values = parts as string[]\n const [first] = values\n switch (draft.operation) {\n case 'value':\n return first\n case 'add':\n return values.join(' + ')\n case 'subtract':\n return values.join(' - ')\n case 'multiply':\n return values.join(' * ')\n case 'divide':\n return values.join(' / ')\n case 'min':\n return `min(${values.join(', ')})`\n case 'max':\n return `max(${values.join(', ')})`\n case 'round':\n return `round(${first}, ${placesOf(draft, 0)})`\n case 'roundDown':\n return `floor(${first})`\n case 'roundUp':\n return `ceil(${first})`\n case 'absolute':\n return `abs(${first})`\n case 'currency':\n return `${quoteText(draft.symbol ?? '$')} + format(${first}, ${placesOf(draft, 2)})`\n case 'number':\n return `format(${first}, ${placesOf(draft, 0)})`\n case 'join': {\n // A leading '' keeps two numbers joined as text rather than added.\n const separator = draft.separator ?? ' '\n return separator\n ? values.join(` + ${quoteText(separator)} + `)\n : ['\\'\\'', ...values].join(' + ')\n }\n }\n}\n\n/** A fresh draft for `operation`, carrying over the values already chosen. */\nexport function formulaDraftFor(\n operation: FormulaOperation,\n previous?: FormulaDraft,\n current = '',\n): FormulaDraft {\n const spec = FORMULA_OPERATIONS[operation]\n const blank = (index: number): FormulaOperand =>\n index === 0 && current.trim()\n ? { source: 'current' }\n : operation === 'join'\n ? { source: 'text', value: '' }\n : { source: 'name', name: '' }\n const operands = Array.from(\n { length: spec.minOperands },\n (_, index) => previous?.operands[index] ?? blank(index),\n )\n return {\n operation,\n operands,\n places: previous?.places,\n symbol: previous?.symbol,\n separator: previous?.separator,\n }\n}\n"],"names":["FORMULA_OPERATIONS","value","label","minOperands","operandLabels","add","variadic","subtract","multiply","divide","min","max","round","places","roundDown","roundUp","absolute","currency","number","join","FORMULA_OPERATION_ORDER","Object","keys","formulaOperandLabel","operation","index","labels","Math","length","quoteText","includes","replace","SIMPLE_EXPRESSION","operandText","operand","current","bare","source","name","text","trim","parsed","Number","isFinite","String","test","placesOf","draft","fallback","isInteger","buildFormula","spec","operands","slice","arithmetic","parts","map","some","part","values","first","symbol","separator","formulaDraftFor","previous","blank","Array","from","_"],"mappings":"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"}
@@ -24,3 +24,19 @@ import { type HostFunctionParameterOption } from '@aglyn/aglyn';
24
24
  export declare function parseParameterOptions(text: string): HostFunctionParameterOption[];
25
25
  /** The inverse, for showing a stored list in that one line. */
26
26
  export declare function formatParameterOptions(options: HostFunctionParameterOption[] | undefined): string;
27
+ /** One row of the choices editor, blanks and all, as the author sees it. */
28
+ export interface ParameterChoiceRow {
29
+ /** What the function receives. */
30
+ value: string;
31
+ /** What the visitor reads; the value when blank. */
32
+ label: string;
33
+ }
34
+ /** The rows a stored choice list opens as. */
35
+ export declare function choiceRowsFromOptions(options: HostFunctionParameterOption[] | undefined): ParameterChoiceRow[];
36
+ /**
37
+ * The choice list rows store as (AGL-3768) — the same shape the one-line
38
+ * grammar produced. A row with no value is not a choice yet; the second row
39
+ * of a value is dropped, since a list cannot tell the function which of two
40
+ * identical values was picked; a label equal to its value is not stored.
41
+ */
42
+ export declare function optionsFromChoiceRows(rows: readonly ParameterChoiceRow[]): HostFunctionParameterOption[];
@@ -25,5 +25,36 @@
25
25
  /** The inverse, for showing a stored list in that one line. */ export function formatParameterOptions(options) {
26
26
  return formatFunctionParameterOptions(options);
27
27
  }
28
+ /** The rows a stored choice list opens as. */ export function choiceRowsFromOptions(options) {
29
+ return (options != null ? options : []).map((option)=>{
30
+ var _option_label;
31
+ return {
32
+ value: option.value,
33
+ label: (_option_label = option.label) != null ? _option_label : ''
34
+ };
35
+ });
36
+ }
37
+ /**
38
+ * The choice list rows store as (AGL-3768) — the same shape the one-line
39
+ * grammar produced. A row with no value is not a choice yet; the second row
40
+ * of a value is dropped, since a list cannot tell the function which of two
41
+ * identical values was picked; a label equal to its value is not stored.
42
+ */ export function optionsFromChoiceRows(rows) {
43
+ const options = [];
44
+ const seen = new Set();
45
+ for (const row of rows){
46
+ const value = row.value.trim();
47
+ if (!value || seen.has(value)) continue;
48
+ seen.add(value);
49
+ const label = row.label.trim();
50
+ options.push(label && label !== value ? {
51
+ value,
52
+ label
53
+ } : {
54
+ value
55
+ });
56
+ }
57
+ return options;
58
+ }
28
59
 
29
60
  //# sourceMappingURL=parameter-options.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/logic/src/lib/model/parameter-options.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n formatFunctionParameterOptions,\n type HostFunctionParameterOption,\n parseFunctionParameterOptions,\n} from '@aglyn/aglyn'\n\n/**\n * A parameter's choice list as one line of text (AGL-3202). The grammar lives\n * in core — `parseFunctionParameterOptions` — because the canvas's Function\n * Input element reads the same line and a plugin may not import another. These\n * are the names the function builder has always called it by.\n */\nexport function parseParameterOptions(\n text: string,\n): HostFunctionParameterOption[] {\n return parseFunctionParameterOptions(text)\n}\n\n/** The inverse, for showing a stored list in that one line. */\nexport function formatParameterOptions(\n options: HostFunctionParameterOption[] | undefined,\n): string {\n return formatFunctionParameterOptions(options)\n}\n"],"names":["formatFunctionParameterOptions","parseFunctionParameterOptions","parseParameterOptions","text","formatParameterOptions","options"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,8BAA8B,EAE9BC,6BAA6B,QACxB,eAAc;AAErB;;;;;CAKC,GACD,OAAO,SAASC,sBACdC,IAAY;IAEZ,OAAOF,8BAA8BE;AACvC;AAEA,6DAA6D,GAC7D,OAAO,SAASC,uBACdC,OAAkD;IAElD,OAAOL,+BAA+BK;AACxC"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/logic/src/lib/model/parameter-options.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n formatFunctionParameterOptions,\n type HostFunctionParameterOption,\n parseFunctionParameterOptions,\n} from '@aglyn/aglyn'\n\n/**\n * A parameter's choice list as one line of text (AGL-3202). The grammar lives\n * in core — `parseFunctionParameterOptions` — because the canvas's Function\n * Input element reads the same line and a plugin may not import another. These\n * are the names the function builder has always called it by.\n */\nexport function parseParameterOptions(\n text: string,\n): HostFunctionParameterOption[] {\n return parseFunctionParameterOptions(text)\n}\n\n/** The inverse, for showing a stored list in that one line. */\nexport function formatParameterOptions(\n options: HostFunctionParameterOption[] | undefined,\n): string {\n return formatFunctionParameterOptions(options)\n}\n\n/** One row of the choices editor, blanks and all, as the author sees it. */\nexport interface ParameterChoiceRow {\n /** What the function receives. */\n value: string\n /** What the visitor reads; the value when blank. */\n label: string\n}\n\n/** The rows a stored choice list opens as. */\nexport function choiceRowsFromOptions(\n options: HostFunctionParameterOption[] | undefined,\n): ParameterChoiceRow[] {\n return (options ?? []).map((option) => ({\n value: option.value,\n label: option.label ?? '',\n }))\n}\n\n/**\n * The choice list rows store as (AGL-3768) — the same shape the one-line\n * grammar produced. A row with no value is not a choice yet; the second row\n * of a value is dropped, since a list cannot tell the function which of two\n * identical values was picked; a label equal to its value is not stored.\n */\nexport function optionsFromChoiceRows(\n rows: readonly ParameterChoiceRow[],\n): HostFunctionParameterOption[] {\n const options: HostFunctionParameterOption[] = []\n const seen = new Set<string>()\n for (const row of rows) {\n const value = row.value.trim()\n if (!value || seen.has(value)) continue\n seen.add(value)\n const label = row.label.trim()\n options.push(label && label !== value ? { value, label } : { value })\n }\n return options\n}\n"],"names":["formatFunctionParameterOptions","parseFunctionParameterOptions","parseParameterOptions","text","formatParameterOptions","options","choiceRowsFromOptions","map","option","value","label","optionsFromChoiceRows","rows","seen","Set","row","trim","has","add","push"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,8BAA8B,EAE9BC,6BAA6B,QACxB,eAAc;AAErB;;;;;CAKC,GACD,OAAO,SAASC,sBACdC,IAAY;IAEZ,OAAOF,8BAA8BE;AACvC;AAEA,6DAA6D,GAC7D,OAAO,SAASC,uBACdC,OAAkD;IAElD,OAAOL,+BAA+BK;AACxC;AAUA,4CAA4C,GAC5C,OAAO,SAASC,sBACdD,OAAkD;IAElD,OAAO,CAACA,kBAAAA,UAAW,EAAE,EAAEE,GAAG,CAAC,CAACC;YAEnBA;eAF+B;YACtCC,OAAOD,OAAOC,KAAK;YACnBC,KAAK,GAAEF,gBAAAA,OAAOE,KAAK,YAAZF,gBAAgB;QACzB;;AACF;AAEA;;;;;CAKC,GACD,OAAO,SAASG,sBACdC,IAAmC;IAEnC,MAAMP,UAAyC,EAAE;IACjD,MAAMQ,OAAO,IAAIC;IACjB,KAAK,MAAMC,OAAOH,KAAM;QACtB,MAAMH,QAAQM,IAAIN,KAAK,CAACO,IAAI;QAC5B,IAAI,CAACP,SAASI,KAAKI,GAAG,CAACR,QAAQ;QAC/BI,KAAKK,GAAG,CAACT;QACT,MAAMC,QAAQK,IAAIL,KAAK,CAACM,IAAI;QAC5BX,QAAQc,IAAI,CAACT,SAASA,UAAUD,QAAQ;YAAEA;YAAOC;QAAM,IAAI;YAAED;QAAM;IACrE;IACA,OAAOJ;AACT"}