@lotics/app-sdk 0.114.0 → 0.115.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21561,6 +21561,180 @@ function validateFieldTypeForOperation(fieldType, operation, subject) {
21561
21561
  throw new Error(`Operation '${operation}' is not valid for field type '${fieldType}'`);
21562
21562
  }
21563
21563
 
21564
+ // ../shared/src/schemas/iam.ts
21565
+ var jsonSchemaSchema = zod_default.string();
21566
+ var organizationSchema = zod_default.object({
21567
+ id: zod_default.string().describe("Unique identifier for the organization"),
21568
+ name: zod_default.string().describe("Display name of the organization"),
21569
+ slug: zod_default.string().nullable().describe("Slug for the organization")
21570
+ });
21571
+ var memberAccountTypeSchema = zod_default.enum(["self_managed", "admin_managed", "service"]).describe(
21572
+ "How the account is credentialed. admin_managed means an admin chose the sign-in name and set the password; the account has NO email address at all, so nothing can be mailed to it until the member claims one they control. service means no credential exists at all \u2014 the membership is an integration's identity, reached only by an API key bound to it."
21573
+ );
21574
+ var ALL_ACCOUNT_TYPES = memberAccountTypeSchema.options;
21575
+ var memberSchema = zod_default.object({
21576
+ member_id: zod_default.string().describe("Unique identifier for the member"),
21577
+ organization_id: zod_default.string().describe("Organization ID the member belongs to"),
21578
+ name: zod_default.string().describe("Display name of the member"),
21579
+ email_address: zod_default.string().nullable().describe(
21580
+ "What the member types to sign in: an email address for a self-managed member, an administrator-issued name (e.g. giamdoc.sunrise) for an admin-managed one. The latter is NOT an address and cannot receive mail \u2014 the field name predates the split. Null for a service identity, which holds no credential and signs in nowhere."
21581
+ ),
21582
+ role: zod_default.enum(["owner", "admin", "member"]).describe("Organization role"),
21583
+ image: zod_default.string().nullable().optional().describe("URL to the member's profile picture"),
21584
+ account_type: memberAccountTypeSchema,
21585
+ archived: zod_default.boolean().optional().describe(
21586
+ "True when this person has been removed from the organization. The membership survives removal so records naming them keep resolving; it grants nothing. Optional because an older server omits it \u2014 absent means active."
21587
+ ),
21588
+ joined_at: zod_default.string().optional().describe(
21589
+ "ISO timestamp of when this membership began. Optional because an older server omits it \u2014 absent means not told, never 'joined at the epoch'."
21590
+ )
21591
+ });
21592
+ var pendingInvitationSchema = zod_default.object({
21593
+ invitation_id: zod_default.string().describe("Unique identifier for the pending invitation"),
21594
+ organization_id: zod_default.string().describe("Organization the invitee will join on accept"),
21595
+ email: zod_default.string().describe("Email the invitation was sent to"),
21596
+ name: zod_default.string().nullable().describe("Admin-supplied display name (carried onto member.name on accept; invitee can override at accept time)"),
21597
+ role: zod_default.enum(["owner", "admin", "member"]).describe("Role the invitee will receive on accept"),
21598
+ expires_at: zod_default.string().describe("ISO timestamp when the invitation expires")
21599
+ });
21600
+ var ianaTimezoneSchema = zod_default.string().refine(
21601
+ (tz) => {
21602
+ try {
21603
+ new Intl.DateTimeFormat("en-US", { timeZone: tz });
21604
+ return true;
21605
+ } catch {
21606
+ return false;
21607
+ }
21608
+ },
21609
+ { message: "must be an IANA timezone name, e.g. Asia/Ho_Chi_Minh or America/New_York" }
21610
+ ).describe("IANA timezone name (e.g., 'America/New_York', 'Asia/Ho_Chi_Minh')");
21611
+ var currencyCodeSchema = zod_default.string().trim().toUpperCase().regex(/^[A-Z]{3}$/, "must be an ISO 4217 currency code, e.g. VND, USD, EUR").describe("ISO 4217 currency code (e.g. 'VND', 'USD', 'EUR'). Upper-cased.");
21612
+ var workspaceSchema = zod_default.object({
21613
+ id: zod_default.string().describe("Unique identifier for the workspace"),
21614
+ name: zod_default.string().describe("Display name of the workspace"),
21615
+ default_currency: zod_default.string().describe("Default currency code for the workspace"),
21616
+ // Plain string on the way OUT, deliberately. This is a response schema, so
21617
+ // validating here would make the server reject its own stored rows — and on
21618
+ // the workspace LIST one bad row fails the entire array. The zone is enforced
21619
+ // where it is written; a read is too late to do anything about it anyway.
21620
+ timezone: zod_default.string().describe("IANA timezone name (e.g., 'America/New_York', 'Asia/Ho_Chi_Minh')"),
21621
+ organization_id: zod_default.string().describe("Organization ID this workspace belongs to"),
21622
+ created_at: zod_default.string().describe("Timestamp when workspace was created")
21623
+ });
21624
+ var memberGroupSchema = zod_default.object({
21625
+ id: zod_default.string().describe("Unique identifier for the member group"),
21626
+ name: zod_default.string().describe("Display name of the group"),
21627
+ description: zod_default.string().optional().describe("Optional description of the group's purpose"),
21628
+ member_ids: zod_default.array(zod_default.string()).describe("Array of member IDs in this group"),
21629
+ invitation_ids: zod_default.array(zod_default.string()).describe("Array of pending invitation IDs pre-granted membership; migrated to member_ids on accept"),
21630
+ organization_id: zod_default.string().describe("ID of the organization this group belongs to"),
21631
+ created_at: zod_default.string().describe("Timestamp when group was created"),
21632
+ created_by: zod_default.string().describe("Member ID of the user who created this group")
21633
+ });
21634
+ var principalGroupSchema = zod_default.object({
21635
+ id: zod_default.string().describe("Unique identifier for the member group"),
21636
+ name: zod_default.string().describe("Display name of the group"),
21637
+ description: zod_default.string().optional().describe("Optional description of the group's purpose"),
21638
+ member_count: zod_default.number().int().describe("Number of accepted members in the group"),
21639
+ invitation_count: zod_default.number().int().describe("Number of pending invitations pre-granted membership")
21640
+ });
21641
+ var PUBLIC_PRINCIPAL_ID = "public";
21642
+ var principalSchema = zod_default.discriminatedUnion("type", [
21643
+ zod_default.object({
21644
+ type: zod_default.literal("member").describe("Principal type: individual member"),
21645
+ id: zod_default.string().describe("Member ID")
21646
+ }),
21647
+ zod_default.object({
21648
+ type: zod_default.literal("member_group").describe("Principal type: group of members"),
21649
+ id: zod_default.string().describe("Member group ID")
21650
+ }),
21651
+ zod_default.object({
21652
+ type: zod_default.literal("organization").describe("Principal type: all members in organization"),
21653
+ id: zod_default.string().describe("Organization ID")
21654
+ }),
21655
+ zod_default.object({
21656
+ type: zod_default.literal("public").describe("Principal type: anyone, including unauthenticated visitors"),
21657
+ id: zod_default.literal(PUBLIC_PRINCIPAL_ID).describe("Always the `public` sentinel \u2014 no entity")
21658
+ }),
21659
+ // Pending invitation as principal: lets admins pre-grant group membership and
21660
+ // resource access before the invitee accepts. Never resolved as a Subject —
21661
+ // a pending invitee cannot authenticate, so these bindings stay dormant until
21662
+ // accept-time migration rewrites them to type:"member".
21663
+ zod_default.object({
21664
+ type: zod_default.literal("invitation").describe("Principal type: pending invitation (not yet accepted)"),
21665
+ id: zod_default.string().describe("Invitation ID")
21666
+ })
21667
+ ]);
21668
+ var subjectSchema = zod_default.discriminatedUnion("type", [
21669
+ zod_default.object({
21670
+ type: zod_default.literal("member").describe("Subject type: authenticated member"),
21671
+ id: zod_default.string().describe("Member ID")
21672
+ }),
21673
+ zod_default.object({
21674
+ type: zod_default.literal("public").describe("Subject type: unauthenticated visitor")
21675
+ })
21676
+ ]);
21677
+ var resourceTypeSchema = zod_default.enum(["table", "table_view", "app", "automation", "skill", "document_template", "instruction", "knowledge_doc", "connected_account"]);
21678
+ var RESOURCE_ROLE_ACTIONS = {
21679
+ table: {
21680
+ editor: ["read_data", "write_data"],
21681
+ viewer: ["read_data"]
21682
+ },
21683
+ app: { manager: ["use", "manage"], user: ["use"] },
21684
+ skill: { manager: ["use", "manage"], user: ["use"] },
21685
+ document_template: { manager: ["use", "manage"], user: ["use"] },
21686
+ instruction: { manager: ["use", "manage"], user: ["use"] },
21687
+ knowledge_doc: { manager: ["use", "manage"], user: ["use"] },
21688
+ connected_account: { manager: ["use", "manage"], user: ["use"] },
21689
+ table_view: {},
21690
+ automation: {}
21691
+ };
21692
+ function shareableRolesFor(type) {
21693
+ return Object.keys(RESOURCE_ROLE_ACTIONS[type]);
21694
+ }
21695
+ var SHAREABLE_RESOURCE_TYPES = resourceTypeSchema.options.filter(
21696
+ (type) => shareableRolesFor(type).length > 0
21697
+ );
21698
+ var accessPrincipalSchema = zod_default.discriminatedUnion("type", [
21699
+ zod_default.object({
21700
+ type: zod_default.literal("member").describe("Principal type: an individual member"),
21701
+ id: zod_default.string().describe("Member ID")
21702
+ }),
21703
+ zod_default.object({
21704
+ type: zod_default.literal("member_group").describe("Principal type: a member group"),
21705
+ id: zod_default.string().describe("Group ID")
21706
+ }),
21707
+ zod_default.object({
21708
+ type: zod_default.literal("organization").describe("Principal type: everyone in the organization"),
21709
+ id: zod_default.string().describe("Organization ID")
21710
+ })
21711
+ ]);
21712
+ var resourceAccessSchema = zod_default.object({
21713
+ resource_id: zod_default.string().describe("ID of the resource these principals can reach"),
21714
+ principals: zod_default.array(accessPrincipalSchema).describe("Members, groups and the organization that have been granted access")
21715
+ });
21716
+ var contentResourceTypeSchema = zod_default.enum(["knowledge_doc", "document_template"]);
21717
+ var contentItemSchema = zod_default.object({
21718
+ id: zod_default.string().describe("ID of the knowledge doc or template"),
21719
+ name: zod_default.string().describe("Its name"),
21720
+ owner_member_id: zod_default.string().nullable().describe("Member who owns it; null when nobody does"),
21721
+ created_at: zod_default.string().describe("When it was created"),
21722
+ updated_at: zod_default.string().nullable().describe("When a knowledge doc was last changed; null for a template"),
21723
+ hidden_at: zod_default.string().nullable().describe("When a knowledge doc was hidden; null when it is in use, and for a template"),
21724
+ template_type: zod_default.string().nullable().describe("A template's type (html, email, word, excel, pdf-form); null for a knowledge doc"),
21725
+ can_open: zod_default.boolean().describe("Whether the caller may open it; false means it is not shared with them"),
21726
+ principals: zod_default.array(accessPrincipalSchema).describe("Members, groups and the organization it has been shared with, besides its owner")
21727
+ });
21728
+ var resourceRoleBindingSchema = zod_default.object({
21729
+ id: zod_default.string().describe("Unique identifier for the role binding"),
21730
+ principal: principalSchema.describe(
21731
+ "Who this role binding applies to (member, group, or organization)"
21732
+ ),
21733
+ resource_type: resourceTypeSchema.describe("Type of resource being accessed"),
21734
+ resource_id: zod_default.string().describe("ID of the specific resource"),
21735
+ role: zod_default.string().describe("Role name (e.g., 'viewer', 'editor', 'manager', 'user')")
21736
+ });
21737
+
21564
21738
  // ../shared/src/number_unit.ts
21565
21739
  var MEASURED_UNITS = [
21566
21740
  { code: "g", dimension: "mass", factor: 1, label: { vi: "g", en: "g" }, scales: true, spellings: ["gram", "grams", "gam"] },
@@ -21579,9 +21753,11 @@ var MEASURED_UNITS = [
21579
21753
  { code: "day", dimension: "duration", factor: 1440, label: { vi: "ng\xE0y", en: "d" }, scales: true, spellings: ["days"] }
21580
21754
  ];
21581
21755
  var COUNTED_UNIT_MAX_LENGTH = 12;
21582
- var UNIT_DESCRIPTION = `What a plain figure counts or measures, drawn after it: a measured code (${MEASURED_UNITS.map((unit) => unit.code).join(", ")}), which converts and scales within its dimension, or any other noun of at most ${COUNTED_UNIT_MAX_LENGTH} characters (ki\u1EC7n, pallet, TEU), which never does. Only beside format "number".`;
21756
+ var UNIT_WORDS = `a measured code (${MEASURED_UNITS.map((unit) => unit.code).join(", ")}), which converts and scales within its dimension, or any other noun of at most ${COUNTED_UNIT_MAX_LENGTH} characters (ki\u1EC7n, pallet, TEU), which never does`;
21757
+ var UNIT_DESCRIPTION = `What a plain figure counts or measures, drawn after it: ${UNIT_WORDS}. Only beside format "number".`;
21583
21758
  var UNIT_FIELD_DESCRIPTION = 'A single select on the same row, or a lookup of one through a one-link, whose chosen option is that row\'s unit: every option label is a unit as `unit` takes one. In place of `unit`; only beside format "number".';
21584
21759
  var CURRENCY_FIELD_DESCRIPTION = 'A single select on the same row, or a lookup of one through a one-link, whose chosen option is that row\'s currency: every option label is an ISO 4217 code. In place of `currency`; only beside format "currency".';
21760
+ var DEFAULT_CURRENCY = "VND";
21585
21761
  function unitKey(text2) {
21586
21762
  return text2.normalize("NFC").trim().toLocaleLowerCase("vi").replace(/\s+/g, " ");
21587
21763
  }
@@ -21619,88 +21795,151 @@ function numberUnitRefusal(format2, unit) {
21619
21795
  if (format2 !== void 0 && format2 !== "number") {
21620
21796
  return `a unit is for a plain figure (format "number"), not "${format2}": money states its currency and a percent its sign`;
21621
21797
  }
21798
+ return unitRefusalWords(unit);
21799
+ }
21800
+ function unitRefusalWords(unit) {
21622
21801
  const refusal = unitRefusal(unit);
21623
21802
  return refusal === void 0 ? void 0 : refusalWords(unit, refusal);
21624
21803
  }
21625
-
21626
- // ../shared/src/select_option_enrichment.ts
21627
- function effectiveSelectOptions(field) {
21628
- if (field === void 0) return null;
21629
- if (field.type === "select") return field.options ?? null;
21630
- if (field.type === "formula") return field.formula?.options ?? null;
21631
- if (field.type === "lookup" && field.lookup_field_type === "select") {
21632
- return field.lookup_field_options ?? null;
21633
- }
21634
- return null;
21804
+ function currencyCodeRefusal(code) {
21805
+ if (Intl.supportedValuesOf("currency").includes(code)) return void 0;
21806
+ const coded = currencyCodeOf(code);
21807
+ return coded !== void 0 ? `a currency is written as its ISO 4217 code: "${coded}"` : `"${code}" is not an ISO 4217 currency code (USD, EUR, VND)`;
21808
+ }
21809
+ var DONG_SPELLINGS = ["vn\u0111", "\u0111", "\u20AB", "\u0111\u1ED3ng"];
21810
+ function currencyCodeOf(written) {
21811
+ const upper2 = written.trim().toUpperCase();
21812
+ if (Intl.supportedValuesOf("currency").includes(upper2)) return upper2;
21813
+ return DONG_SPELLINGS.includes(unitKey(written)) ? "VND" : void 0;
21814
+ }
21815
+ var EXACT_DIGITS = 15;
21816
+ function convert(value, from, to) {
21817
+ return from.code === to.code ? value : Number((value * from.factor / to.factor).toPrecision(EXACT_DIGITS));
21818
+ }
21819
+ function scaleIn(own, pack) {
21820
+ const kin = MEASURED_UNITS.filter((one) => one.dimension === own.dimension);
21821
+ const rung = (one) => ({ label: one.label[pack], factor: convert(1, one, own) });
21822
+ const names = (one) => [...SPELLED].filter(([, spelled]) => spelled.code === one.code).map(([name]) => name);
21823
+ return {
21824
+ label: own.label[pack],
21825
+ ladder: own.scales ? kin.filter((one) => one.scales && one.factor >= own.factor).map(rung) : [rung(own)],
21826
+ entries: [own, ...kin.filter((one) => one.code !== own.code)].map((one) => ({ ...rung(one), names: names(one) }))
21827
+ };
21635
21828
  }
21829
+ var SCALES = new Map(
21830
+ MEASURED_UNITS.map((unit) => [unit.code, { vi: scaleIn(unit, "vi"), en: scaleIn(unit, "en") }])
21831
+ );
21636
21832
 
21637
- // ../shared/src/row_figure.ts
21638
- function namedSelect(unitField, currencyField) {
21639
- if (unitField != null) return { vocabulary: "unit", key: unitField };
21640
- if (currencyField != null) return { vocabulary: "currency", key: currencyField };
21641
- return void 0;
21833
+ // ../shared/src/notation.ts
21834
+ var PERCENT = "percent";
21835
+ var DECIMAL = { style: "decimal" };
21836
+ var NOTATION_DESCRIPTION = `How the figure reads: { "style": "decimal" }; money, { "style": "currency", "currency": an ISO 4217 code (USD, VND) }; or a quantity, { "style": "unit", "unit": "${PERCENT}" (10 reads "10%"), or ${UNIT_WORDS} }. A currency or unit read on each row from a single select of that row is { "per_row": that select }.`;
21837
+ function unitIssue(unit, ctx) {
21838
+ const refused = unit === PERCENT ? void 0 : unitRefusalWords(unit);
21839
+ if (refused !== void 0) ctx.addIssue({ code: "custom", message: refused });
21642
21840
  }
21643
- function figureRowSelect(field) {
21841
+ function notationSchemaOf(reference) {
21842
+ const perRow = external_exports.object({ per_row: reference.describe("The single select of the same row whose option is each row's currency or unit") }).strict();
21843
+ return external_exports.discriminatedUnion("style", [
21844
+ external_exports.object({ style: external_exports.literal("decimal") }).strict(),
21845
+ external_exports.object({ style: external_exports.literal("currency"), currency: external_exports.union([external_exports.string().min(1), perRow]) }).strict(),
21846
+ external_exports.object({ style: external_exports.literal("unit"), unit: external_exports.union([external_exports.string().superRefine(unitIssue), perRow]) }).strict()
21847
+ ]).describe(NOTATION_DESCRIPTION);
21848
+ }
21849
+ var notationSchema = notationSchemaOf(external_exports.string().min(1));
21850
+ function notationOfKeys(keys2) {
21851
+ if (keys2.format === "currency") {
21852
+ return { style: "currency", currency: keys2.currency_field ? { per_row: keys2.currency_field } : keys2.currency || DEFAULT_CURRENCY };
21853
+ }
21854
+ if (keys2.unit_field) return { style: "unit", unit: { per_row: keys2.unit_field } };
21855
+ if (keys2.unit) return { style: "unit", unit: keys2.unit };
21856
+ if (keys2.format === "percentage") return { style: "unit", unit: PERCENT };
21857
+ return DECIMAL;
21858
+ }
21859
+ var KEYS_BEFORE_NOTATION = ["format", "currency", "unit", "unit_field", "currency_field"];
21860
+ function keysStated(keys2) {
21861
+ return KEYS_BEFORE_NOTATION.some((key) => Boolean(keys2[key]));
21862
+ }
21863
+ function sameValue(a, b) {
21864
+ return typeof a === "string" || typeof b === "string" ? a === b : a.per_row === b.per_row;
21865
+ }
21866
+ function sameNotation(a, b) {
21867
+ if (a === void 0 || b === void 0) return a === b;
21868
+ if (a.style === "currency" && b.style === "currency") return sameValue(a.currency, b.currency);
21869
+ if (a.style === "unit" && b.style === "unit") return sameValue(a.unit, b.unit);
21870
+ return a.style === "decimal" && b.style === "decimal";
21871
+ }
21872
+ function storedNotation(notation, keys2) {
21873
+ if (notation == null) return notationOfKeys(keys2);
21874
+ if (!keysStated(keys2)) return notation;
21875
+ const derived = notationOfKeys(keys2);
21876
+ return sameNotation(notation, derived) ? notation : derived;
21877
+ }
21878
+ function fieldNotation(field) {
21644
21879
  switch (field.type) {
21645
21880
  case "number":
21646
- return namedSelect(field.unit_field, field.currency_field);
21881
+ return storedNotation(field.notation, field);
21647
21882
  case "formula": {
21648
- const output = field.formula?.output_type;
21649
- return output == null || output === "number" ? namedSelect(field.formula?.unit_field, field.formula?.currency_field) : void 0;
21883
+ const formula = field.formula;
21884
+ const output = formula?.output_type;
21885
+ return output == null || output === "number" ? storedNotation(formula?.notation, formula ?? {}) : void 0;
21650
21886
  }
21651
21887
  case "rollup": {
21652
21888
  const operation = field.aggregate_option?.operation;
21653
- return isAggregateOperation(operation) && rollupOutputType(operation) === "number" ? namedSelect(field.aggregate_field_unit_field, field.aggregate_field_currency_field) : void 0;
21889
+ if (!isAggregateOperation(operation) || rollupOutputType(operation) !== "number") return void 0;
21890
+ return storedNotation(field.aggregate_field_notation, {
21891
+ format: field.aggregate_field_format,
21892
+ currency: field.aggregate_field_currency,
21893
+ unit: field.aggregate_field_unit,
21894
+ unit_field: field.aggregate_field_unit_field,
21895
+ currency_field: field.aggregate_field_currency_field
21896
+ });
21654
21897
  }
21898
+ case "lookup":
21899
+ return field.lookup_field_type === "number" ? storedNotation(field.lookup_field_notation, { format: field.lookup_field_format, currency: field.lookup_field_currency, unit: field.lookup_field_unit }) : void 0;
21655
21900
  default:
21656
21901
  return void 0;
21657
21902
  }
21658
21903
  }
21659
- function firstOptionKey(cell) {
21660
- if (!Array.isArray(cell)) return void 0;
21661
- for (const entry of cell) {
21662
- if (typeof entry === "string") return entry;
21663
- const inner = firstOptionKey(entry);
21664
- if (inner !== void 0) return inner;
21665
- }
21904
+ function notationRowSelect(notation) {
21905
+ if (notation?.style === "currency" && typeof notation.currency !== "string") return { vocabulary: "currency", key: notation.currency.per_row };
21906
+ if (notation?.style === "unit" && typeof notation.unit !== "string") return { vocabulary: "unit", key: notation.unit.per_row };
21666
21907
  return void 0;
21667
21908
  }
21668
- function comparesFigure(operator) {
21669
- return operator !== "is_empty" && operator !== "is_not_empty";
21909
+ function notationCurrency(notation) {
21910
+ return notation?.style === "currency" && typeof notation.currency === "string" ? notation.currency : void 0;
21670
21911
  }
21671
- function nameOf(field) {
21672
- return `"${field.name ?? field.key}"`;
21912
+ function notationSaid(figure) {
21913
+ return { stated: figure.notation ?? void 0, byKeys: keysStated(figure) ? notationOfKeys(figure) : void 0, keys: figure };
21673
21914
  }
21674
- function conditionUnit(condition2, field, fieldOf2) {
21675
- if (!comparesFigure(condition2.operator)) return { ok: true, unit: void 0 };
21676
- const stated = condition2.unit_option;
21677
- const read2 = figureRowSelect(field);
21678
- if (read2 === void 0) {
21679
- return stated === void 0 ? { ok: true, unit: void 0 } : { ok: false, reason: `${nameOf(field)} is read in one unit for every row, so a condition on it states no unit_option.` };
21680
- }
21681
- const select = fieldOf2(read2.key);
21682
- const options = effectiveSelectOptions(select) ?? [];
21683
- const held2 = options.length === 0 ? `${select === void 0 ? `"${read2.key}"` : nameOf(select)} holds no ${read2.vocabulary} yet` : `one of ${options.map((option2) => `${option2.name} (${option2.key})`).join(", ")}`;
21684
- const reads = `${nameOf(field)} is read in each row's own ${read2.vocabulary} (${select === void 0 ? `"${read2.key}"` : nameOf(select)})`;
21685
- if (stated === void 0) {
21686
- const unstated = condition2.value === null || condition2.value === void 0 || condition2.value === "";
21687
- return unstated ? { ok: true, unit: void 0 } : { ok: false, reason: `${reads}, so '${condition2.operator}' states the ${read2.vocabulary} its value is in as unit_option: ${held2}.` };
21688
- }
21689
- const option = typeof stated === "string" ? options.find((candidate) => candidate.key === stated) : void 0;
21690
- if (option === void 0) {
21691
- return { ok: false, reason: `${reads}; unit_option ${JSON.stringify(stated)} is none of them: ${held2}.` };
21692
- }
21693
- const target = read2.vocabulary === "unit" ? measuredUnit(option.name) : void 0;
21694
- if (target === void 0) return { ok: true, unit: { select: read2, options: [{ key: option.key, factor: 1 }], factor: 1 } };
21695
- const kin = options.flatMap((candidate) => {
21696
- const measured = measuredUnit(candidate.name);
21697
- return measured?.dimension === target.dimension ? [{ key: candidate.key, factor: measured.factor }] : [];
21698
- });
21699
- return { ok: true, unit: { select: read2, options: kin, factor: target.factor } };
21915
+ function sameReading(a, b) {
21916
+ return a === "link" || b === "link" ? a === b : sameNotation(a, b);
21700
21917
  }
21701
- function rowUnitFactor(unit, data) {
21702
- const key = firstOptionKey(data[unit.select.key]);
21703
- return key === void 0 ? void 0 : unit.options.find((option) => option.key === key)?.factor;
21918
+ function readingWords(reading) {
21919
+ return reading === "link" ? "a link" : JSON.stringify(reading);
21920
+ }
21921
+ function settledReading(written, stored) {
21922
+ const held2 = stored === void 0 ? void 0 : stored.byKeys ?? stored.stated;
21923
+ const { stated, byKeys } = written;
21924
+ const parted = partedReadings(written);
21925
+ if (parted === void 0) return checkedReading(stated ?? byKeys ?? held2, held2);
21926
+ const changed2 = stored === void 0 ? void 0 : sameReading(stated, stored.stated) ? byKeys : sameReading(byKeys, stored.byKeys) ? stated : void 0;
21927
+ return changed2 === void 0 ? { refusal: parted } : checkedReading(changed2, held2);
21928
+ }
21929
+ function partedReadings(written) {
21930
+ const { stated, byKeys } = written;
21931
+ if (stated === void 0 || byKeys === void 0 || sameReading(stated, byKeys)) return void 0;
21932
+ const key = stated === "link" ? "link" : "notation";
21933
+ const keys2 = KEYS_BEFORE_NOTATION.flatMap((name) => written.keys[name] ? [`${name} ${JSON.stringify(written.keys[name])}`] : []);
21934
+ return `"${key}" says ${readingWords(stated)} and ${keys2.join(", ")} say ${readingWords(byKeys)} \u2014 state "${key}" alone`;
21935
+ }
21936
+ function checkedReading(reading, held2) {
21937
+ const refused = changedCurrencyRefusal(reading, held2);
21938
+ return refused === void 0 ? { reading } : { refusal: refused };
21939
+ }
21940
+ function changedCurrencyRefusal(reading, held2) {
21941
+ const currency = reading === "link" || sameReading(reading, held2) ? void 0 : notationCurrency(reading);
21942
+ return currency === void 0 ? void 0 : currencyCodeRefusal(currency);
21704
21943
  }
21705
21944
 
21706
21945
  // ../shared/src/icon_names.generated.ts
@@ -23625,7 +23864,11 @@ var KIT_GLYPH_NAMES = [
23625
23864
  "align-center",
23626
23865
  "align-left",
23627
23866
  "align-right",
23867
+ "align-vertical-justify-center",
23868
+ "align-vertical-justify-end",
23869
+ "align-vertical-justify-start",
23628
23870
  "arrow-down",
23871
+ "arrow-down-a-z",
23629
23872
  "arrow-down-up",
23630
23873
  "arrow-down-wide-narrow",
23631
23874
  "arrow-left",
@@ -23636,10 +23879,13 @@ var KIT_GLYPH_NAMES = [
23636
23879
  "arrow-up",
23637
23880
  "arrow-up-down",
23638
23881
  "arrow-up-wide-narrow",
23882
+ "arrow-up-z-a",
23639
23883
  "ban",
23640
23884
  "banknote",
23641
23885
  "bed",
23642
23886
  "bell",
23887
+ "between-horizontal-start",
23888
+ "between-vertical-start",
23643
23889
  "bold",
23644
23890
  "bolt",
23645
23891
  "book-marked",
@@ -23667,17 +23913,23 @@ var KIT_GLYPH_NAMES = [
23667
23913
  "chevrons-up-down",
23668
23914
  "circle-alert",
23669
23915
  "circle-check",
23916
+ "circle-x",
23670
23917
  "clipboard-list",
23918
+ "clipboard-paste",
23671
23919
  "clock",
23672
23920
  "code",
23673
23921
  "code-xml",
23674
23922
  "columns-3",
23675
23923
  "columns-3-cog",
23924
+ "combine",
23676
23925
  "construction",
23677
23926
  "container",
23678
23927
  "copy",
23679
23928
  "credit-card",
23680
23929
  "database",
23930
+ "decimals-arrow-left",
23931
+ "decimals-arrow-right",
23932
+ "dollar-sign",
23681
23933
  "download",
23682
23934
  "ellipsis",
23683
23935
  "eraser",
@@ -23704,9 +23956,11 @@ var KIT_GLYPH_NAMES = [
23704
23956
  "gauge",
23705
23957
  "globe",
23706
23958
  "gpu",
23959
+ "grid-2x2",
23707
23960
  "grip-vertical",
23708
23961
  "group",
23709
23962
  "hand-coins",
23963
+ "hash",
23710
23964
  "heading",
23711
23965
  "heading-1",
23712
23966
  "heading-2",
@@ -23759,6 +24013,7 @@ var KIT_GLYPH_NAMES = [
23759
24013
  "notepad-text-dashed",
23760
24014
  "package",
23761
24015
  "paint-bucket",
24016
+ "paintbrush-vertical",
23762
24017
  "palette",
23763
24018
  "panel-left",
23764
24019
  "panel-left-close",
@@ -23769,6 +24024,7 @@ var KIT_GLYPH_NAMES = [
23769
24024
  "paperclip",
23770
24025
  "pause",
23771
24026
  "pencil",
24027
+ "percent",
23772
24028
  "phone",
23773
24029
  "pin",
23774
24030
  "pin-off",
@@ -23783,7 +24039,9 @@ var KIT_GLYPH_NAMES = [
23783
24039
  "repeat",
23784
24040
  "rotate-ccw",
23785
24041
  "rotate-cw",
24042
+ "rows-3",
23786
24043
  "scan",
24044
+ "scissors",
23787
24045
  "search",
23788
24046
  "send",
23789
24047
  "settings",
@@ -23793,9 +24051,11 @@ var KIT_GLYPH_NAMES = [
23793
24051
  "shield-alert",
23794
24052
  "shield-check",
23795
24053
  "shopping-cart",
24054
+ "sigma",
23796
24055
  "sliders-horizontal",
23797
24056
  "smile",
23798
24057
  "smile-plus",
24058
+ "snowflake",
23799
24059
  "sparkles",
23800
24060
  "split",
23801
24061
  "square",
@@ -23804,11 +24064,13 @@ var KIT_GLYPH_NAMES = [
23804
24064
  "square-sigma",
23805
24065
  "stethoscope",
23806
24066
  "sticky-note",
24067
+ "strikethrough",
23807
24068
  "table",
23808
24069
  "table-2",
23809
24070
  "tag",
23810
24071
  "target",
23811
24072
  "text-quote",
24073
+ "text-wrap",
23812
24074
  "thumbs-down",
23813
24075
  "thumbs-up",
23814
24076
  "ticket",
@@ -23819,6 +24081,7 @@ var KIT_GLYPH_NAMES = [
23819
24081
  "truck",
23820
24082
  "tv-minimal",
23821
24083
  "twitter",
24084
+ "type",
23822
24085
  "underline",
23823
24086
  "undo",
23824
24087
  "upload",
@@ -23831,7 +24094,9 @@ var KIT_GLYPH_NAMES = [
23831
24094
  "workflow",
23832
24095
  "wrench",
23833
24096
  "x",
23834
- "zap"
24097
+ "zap",
24098
+ "zoom-in",
24099
+ "zoom-out"
23835
24100
  ];
23836
24101
 
23837
24102
  // ../shared/src/icon_names.ts
@@ -23971,6 +24236,68 @@ function everyOptionMarked(nameOf2) {
23971
24236
  };
23972
24237
  }
23973
24238
 
24239
+ // ../shared/src/select_option_enrichment.ts
24240
+ function effectiveSelectOptions(field) {
24241
+ if (field === void 0) return null;
24242
+ if (field.type === "select") return field.options ?? null;
24243
+ if (field.type === "formula") return field.formula?.options ?? null;
24244
+ if (field.type === "lookup" && field.lookup_field_type === "select") {
24245
+ return field.lookup_field_options ?? null;
24246
+ }
24247
+ return null;
24248
+ }
24249
+
24250
+ // ../shared/src/row_figure.ts
24251
+ function figureRowSelect(field) {
24252
+ return notationRowSelect(fieldNotation(field));
24253
+ }
24254
+ function firstOptionKey(cell) {
24255
+ if (!Array.isArray(cell)) return void 0;
24256
+ for (const entry of cell) {
24257
+ if (typeof entry === "string") return entry;
24258
+ const inner = firstOptionKey(entry);
24259
+ if (inner !== void 0) return inner;
24260
+ }
24261
+ return void 0;
24262
+ }
24263
+ function comparesFigure(operator) {
24264
+ return operator !== "is_empty" && operator !== "is_not_empty";
24265
+ }
24266
+ function nameOf(field) {
24267
+ return `"${field.name ?? field.key}"`;
24268
+ }
24269
+ function conditionUnit(condition2, field, fieldOf2) {
24270
+ if (!comparesFigure(condition2.operator)) return { ok: true, unit: void 0 };
24271
+ const stated = condition2.unit_option;
24272
+ const read2 = figureRowSelect(field);
24273
+ if (read2 === void 0) {
24274
+ return stated === void 0 ? { ok: true, unit: void 0 } : { ok: false, reason: `${nameOf(field)} is read in one unit for every row, so a condition on it states no unit_option.` };
24275
+ }
24276
+ const select = fieldOf2(read2.key);
24277
+ const options = effectiveSelectOptions(select) ?? [];
24278
+ const held2 = options.length === 0 ? `${select === void 0 ? `"${read2.key}"` : nameOf(select)} holds no ${read2.vocabulary} yet` : `one of ${options.map((option2) => `${option2.name} (${option2.key})`).join(", ")}`;
24279
+ const reads = `${nameOf(field)} is read in each row's own ${read2.vocabulary} (${select === void 0 ? `"${read2.key}"` : nameOf(select)})`;
24280
+ if (stated === void 0) {
24281
+ const unstated = condition2.value === null || condition2.value === void 0 || condition2.value === "";
24282
+ return unstated ? { ok: true, unit: void 0 } : { ok: false, reason: `${reads}, so '${condition2.operator}' states the ${read2.vocabulary} its value is in as unit_option: ${held2}.` };
24283
+ }
24284
+ const option = typeof stated === "string" ? options.find((candidate) => candidate.key === stated) : void 0;
24285
+ if (option === void 0) {
24286
+ return { ok: false, reason: `${reads}; unit_option ${JSON.stringify(stated)} is none of them: ${held2}.` };
24287
+ }
24288
+ const target = read2.vocabulary === "unit" ? measuredUnit(option.name) : void 0;
24289
+ if (target === void 0) return { ok: true, unit: { select: read2, options: [{ key: option.key, factor: 1 }], factor: 1 } };
24290
+ const kin = options.flatMap((candidate) => {
24291
+ const measured = measuredUnit(candidate.name);
24292
+ return measured?.dimension === target.dimension ? [{ key: candidate.key, factor: measured.factor }] : [];
24293
+ });
24294
+ return { ok: true, unit: { select: read2, options: kin, factor: target.factor } };
24295
+ }
24296
+ function rowUnitFactor(unit, data) {
24297
+ const key = firstOptionKey(data[unit.select.key]);
24298
+ return key === void 0 ? void 0 : unit.options.find((option) => option.key === key)?.factor;
24299
+ }
24300
+
23974
24301
  // ../shared/src/diacritics.ts
23975
24302
  var COMBINING_MARKS = /[̀-ͯ]/g;
23976
24303
  function foldDiacritics(value) {
@@ -24564,22 +24891,35 @@ function perRowRefusals(value) {
24564
24891
  ...currencyField.map((message2) => ({ key: "currency_field", message: message2 }))
24565
24892
  ];
24566
24893
  }
24894
+ function figureKeysRefusals(value) {
24895
+ const refused = numberUnitRefusal(value.format, value.unit);
24896
+ return [...refused === void 0 ? [] : [{ key: "unit", message: refused }], ...perRowRefusals(value)];
24897
+ }
24567
24898
  function figureIssue(path) {
24568
24899
  return (value, ctx) => {
24569
- const refused = numberUnitRefusal(value.format, value.unit);
24570
- if (refused !== void 0) ctx.addIssue({ code: "custom", message: refused, path: [...path, "unit"] });
24571
- for (const { key, message: message2 } of perRowRefusals(value)) ctx.addIssue({ code: "custom", message: message2, path: [...path, key] });
24900
+ for (const { key, message: message2 } of figureKeysRefusals(value)) ctx.addIssue({ code: "custom", message: message2, path: [...path, key] });
24572
24901
  };
24573
24902
  }
24574
- var tableNumberFieldSchema = tableFieldBaseSchema.extend({
24903
+ var BEFORE_NOTATION = 'Before "notation", and read as it: state "notation" instead.';
24904
+ function notationStatedIssue(path) {
24905
+ return (value, ctx) => {
24906
+ if (value.notation === void 0) return;
24907
+ const settled = settledReading(notationSaid(value), void 0);
24908
+ if ("refusal" in settled) ctx.addIssue({ code: "custom", message: settled.refusal, path: [...path, "notation"] });
24909
+ };
24910
+ }
24911
+ var tableNumberFieldShape = tableFieldBaseSchema.extend({
24575
24912
  type: zod_default.literal("number"),
24576
- format: numberFormatSchema,
24577
- currency: zod_default.string().nullish().describe("ISO 4217 code. null clears it."),
24578
- unit: zod_default.string().nullish().describe(`${UNIT_DESCRIPTION} null clears it.`),
24579
- unit_field: zod_default.string().nullish().describe(`${UNIT_FIELD_DESCRIPTION} null clears it.`),
24580
- currency_field: zod_default.string().nullish().describe(`${CURRENCY_FIELD_DESCRIPTION} null clears it.`),
24913
+ notation: notationSchema.optional(),
24914
+ format: numberFormatSchema.describe(BEFORE_NOTATION),
24915
+ currency: zod_default.string().nullish().describe(`ISO 4217 code. null clears it. ${BEFORE_NOTATION}`),
24916
+ unit: zod_default.string().nullish().describe(`${UNIT_DESCRIPTION} null clears it. ${BEFORE_NOTATION}`),
24917
+ unit_field: zod_default.string().nullish().describe(`${UNIT_FIELD_DESCRIPTION} null clears it. ${BEFORE_NOTATION}`),
24918
+ currency_field: zod_default.string().nullish().describe(`${CURRENCY_FIELD_DESCRIPTION} null clears it. ${BEFORE_NOTATION}`),
24581
24919
  default_value: numberDefaultValueSchema
24582
- }).superRefine(figureIssue([]));
24920
+ });
24921
+ var tableNumberFieldSchema = tableNumberFieldShape.superRefine(figureIssue([]));
24922
+ var tableNumberFieldInputSchema = tableNumberFieldShape.extend({ format: numberFormatSchema.optional().describe(BEFORE_NOTATION) }).superRefine(figureIssue([]));
24583
24923
  var tableTextFieldSchema = tableFieldBaseSchema.extend({
24584
24924
  type: zod_default.literal("text"),
24585
24925
  unique: zod_default.boolean().optional().describe("Unique values required"),
@@ -24672,6 +25012,9 @@ var tableRollupFieldSchema = tableFieldBaseSchema.extend({
24672
25012
  aggregate_field_type: tableFieldTypeNameSchema.optional().describe(
24673
25013
  "What this rollup's CELL holds \u2014 'date' for earliest/latest, 'number' for every other operation (counts, sums, percentages). Derived from the operation, not from the aggregated field: a 'filled' count over a files column is a number. Server-derived at write time; ignored on input."
24674
25014
  ),
25015
+ aggregate_field_notation: notationSchema.optional().describe(
25016
+ `How this rollup's figure reads. Operations that preserve the aggregated value's unit (sum/avg/median/min/max/range) inherit the target field's; counts are a decimal, percentages the unit "percent". Server-derived at write time; ignored on input.`
25017
+ ),
24675
25018
  aggregate_field_format: zod_default.string().optional().describe(
24676
25019
  "The format of this rollup's own value. Operations that preserve the aggregated value's unit (sum/avg/median/min/max/range/earliest/latest) inherit the target field's format; counting operations are 'number', percentages 'percentage'. Server-derived at write time; ignored on input."
24677
25020
  ),
@@ -24699,6 +25042,9 @@ var tableLookupFieldSchema = tableFieldBaseSchema.extend({
24699
25042
  lookup_field_type: tableFieldTypeNameSchema.optional().describe(
24700
25043
  "The type of the target lookup field (e.g. 'text', 'number', 'date'). Resolved from the linked table's field definition at read time."
24701
25044
  ),
25045
+ lookup_field_notation: notationSchema.optional().describe(
25046
+ "How the looked-up figure reads, where the target lookup field holds a number. Resolved from the linked table's field definition at write time."
25047
+ ),
24702
25048
  lookup_field_format: zod_default.string().optional().describe(
24703
25049
  "The format of the target lookup field (e.g. 'currency', 'percentage', 'datetime'). Resolved from the linked table's field definition at read time."
24704
25050
  ),
@@ -24711,6 +25057,9 @@ var tableLookupFieldSchema = tableFieldBaseSchema.extend({
24711
25057
  lookup_field_options: zod_default.array(zod_default.object({ key: zod_default.string(), name: zod_default.string(), color: zod_default.string().optional(), mark: storedOptionMarkSchema.optional() })).optional().describe(
24712
25058
  "Options for select-type lookup fields. Synced from the source select field's options."
24713
25059
  ),
25060
+ lookup_field_autonumber: zod_default.boolean().optional().describe(
25061
+ "True where the looked-up value is an autonumber's text, which sorts by its number where it is a bare one. Synced from the looked-up field."
25062
+ ),
24714
25063
  order_by: lookupOrderBySchema.optional().describe(
24715
25064
  "When set, sort the linked records by this field on the linked table and return ONLY the picked field from the single extreme record (desc=latest, asc=earliest), as a one-element array. Omit to return every linked record's value as an array. field_key must be a field on the linked table."
24716
25065
  )
@@ -24723,25 +25072,36 @@ var formulaInputSchema = zod_default.object({
24723
25072
  expression: zod_default.string().describe(
24724
25073
  "JavaScript expression. Uses {Field Name} for field references."
24725
25074
  ),
24726
- format: zod_default.enum(["number", "currency", "percentage", "link"]).optional().describe(
24727
- "Display format. 'number' / 'currency' / 'percentage' for numeric results; 'link' for text-output formulas that return a URL \u2014 renders the result as a clickable link."
24728
- ),
24729
- currency: zod_default.string().optional().describe("ISO 4217 currency code, e.g. USD, VND, EUR"),
24730
- unit: zod_default.string().optional().describe(UNIT_DESCRIPTION),
24731
- unit_field: zod_default.string().optional().describe(UNIT_FIELD_DESCRIPTION),
24732
- currency_field: zod_default.string().optional().describe(CURRENCY_FIELD_DESCRIPTION),
25075
+ notation: notationSchema.optional().describe(`${NOTATION_DESCRIPTION} For a formula yielding a number.`),
25076
+ link: zod_default.literal(true).optional().describe("The formula yields a URL as text, drawn as a link that opens."),
25077
+ format: zod_default.enum(["number", "currency", "percentage", "link"]).optional().describe(`'number' / 'currency' / 'percentage' for numeric results; 'link' for a URL. ${BEFORE_NOTATION} 'link' is "link": true.`),
25078
+ currency: zod_default.string().optional().describe(`ISO 4217 currency code, e.g. USD, VND, EUR. ${BEFORE_NOTATION}`),
25079
+ unit: zod_default.string().optional().describe(`${UNIT_DESCRIPTION} ${BEFORE_NOTATION}`),
25080
+ unit_field: zod_default.string().optional().describe(`${UNIT_FIELD_DESCRIPTION} ${BEFORE_NOTATION}`),
25081
+ currency_field: zod_default.string().optional().describe(`${CURRENCY_FIELD_DESCRIPTION} ${BEFORE_NOTATION}`),
24733
25082
  options: zod_default.array(tableSelectFieldOptionSchema).min(1).optional().describe(
24734
25083
  "The categories the formula yields, drawn as a single select's options are. The expression yields one option's `key` (text) or null; a key it does not declare computes an error. Omit for a formula yielding a plain value."
24735
25084
  )
24736
25085
  });
25086
+ var formulaToolInputSchema = formulaInputSchema.omit({ format: true, currency: true, unit: true, unit_field: true, currency_field: true });
24737
25087
  var formulaSchema = formulaInputSchema.extend({
24738
25088
  output_type: formulaOutputTypeSchema.optional().describe("Output type of the formula. Inferred by the backend from the expression \u2014 read-only on input. `datetime` when the result carries a time of day (references a datetime field or a time-bearing helper); `select` when the formula declares `options`."),
24739
25089
  volatile: zod_default.boolean().optional().describe("Whether the formula's result depends on `now()` or other non-deterministic helpers. Inferred by the backend \u2014 read-only on input.")
24740
25090
  });
24741
- var tableFormulaFieldSchema = tableFieldBaseSchema.extend({
25091
+ var tableFormulaFieldShape = tableFieldBaseSchema.extend({
24742
25092
  type: zod_default.literal("formula").describe("Field type for calculated values based on expressions"),
24743
25093
  formula: formulaSchema.describe("Formula configuration and expression")
24744
- }).superRefine((field, ctx) => figureIssue(["formula"])(field.formula, ctx));
25094
+ });
25095
+ var tableFormulaFieldSchema = tableFormulaFieldShape.superRefine((field, ctx) => figureIssue(["formula"])(field.formula, ctx));
25096
+ function formulaLinks(formula) {
25097
+ return formula.format == null ? formula.link === true : formula.format === "link";
25098
+ }
25099
+ function formulaLinkIssue(formula, ctx) {
25100
+ if (formula.link === true && formula.notation !== void 0) {
25101
+ ctx.addIssue({ code: "custom", message: "a formula yielding a link yields text, which reads in no notation", path: ["formula", "link"] });
25102
+ }
25103
+ }
25104
+ var tableFormulaFieldInputSchema = tableFormulaFieldShape.superRefine((field, ctx) => figureIssue(["formula"])(field.formula, ctx)).superRefine((field, ctx) => formulaLinkIssue(field.formula, ctx));
24745
25105
  var workflowMetadataSchema = zod_default.object({
24746
25106
  table_ids: zod_default.array(zod_default.string()).describe("IDs of the tables this workflow affects"),
24747
25107
  staged_for_app_id: zod_default.string().optional().describe(
@@ -24797,7 +25157,7 @@ var tableFieldSchema = zod_default.discriminatedUnion("type", [
24797
25157
  tableAutonumberFieldSchema
24798
25158
  ]);
24799
25159
  var tableFieldInputSchema = zod_default.discriminatedUnion("type", [
24800
- tableNumberFieldSchema,
25160
+ tableNumberFieldInputSchema,
24801
25161
  tableTextFieldSchema,
24802
25162
  tableDateFieldSchema,
24803
25163
  tableBooleanFieldSchema,
@@ -24807,16 +25167,28 @@ var tableFieldInputSchema = zod_default.discriminatedUnion("type", [
24807
25167
  tableRollupFieldSchema,
24808
25168
  tableLookupFieldSchema,
24809
25169
  tableFilesFieldSchema,
24810
- tableFormulaFieldSchema,
25170
+ tableFormulaFieldInputSchema,
24811
25171
  tableButtonFieldSchema,
24812
25172
  tableAutonumberFieldSchema
24813
25173
  ]);
24814
25174
  var tableFieldsSchema = zod_default.array(tableFieldSchema);
24815
- function selectOptionsOf(field) {
24816
- if (field.type === "select") return field.options;
24817
- if (field.type === "formula") return field.formula.options;
24818
- return void 0;
24819
- }
25175
+ var tableFieldsInputSchema = zod_default.array(
25176
+ zod_default.discriminatedUnion("type", [
25177
+ tableNumberFieldInputSchema,
25178
+ tableTextFieldSchema,
25179
+ tableDateFieldSchema,
25180
+ tableBooleanFieldSchema,
25181
+ tableSelectFieldSchema,
25182
+ tableSelectMemberFieldSchema,
25183
+ tableSelectRecordLinkFieldSchema,
25184
+ tableRollupFieldSchema,
25185
+ tableLookupFieldSchema,
25186
+ tableFilesFieldSchema,
25187
+ tableFormulaFieldInputSchema,
25188
+ tableButtonFieldSchema,
25189
+ tableAutonumberFieldSchema
25190
+ ])
25191
+ );
24820
25192
  var tableFieldCreateBaseSchema = zod_default.object({
24821
25193
  name: zod_default.string().describe("Display name"),
24822
25194
  description: zod_default.string().describe("Field description"),
@@ -24830,13 +25202,9 @@ var tableSelectFieldOptionCreateSchema = zod_default.object({
24830
25202
  var tableFieldTypeConfigSchema = zod_default.discriminatedUnion("type", [
24831
25203
  zod_default.object({
24832
25204
  type: zod_default.literal("number"),
24833
- format: numberFormatSchema.default("number"),
24834
- currency: zod_default.string().optional().describe("ISO 4217 code"),
24835
- unit: zod_default.string().optional().describe(UNIT_DESCRIPTION),
24836
- unit_field: zod_default.string().optional().describe(UNIT_FIELD_DESCRIPTION),
24837
- currency_field: zod_default.string().optional().describe(CURRENCY_FIELD_DESCRIPTION),
25205
+ notation: notationSchema.optional().describe(`${NOTATION_DESCRIPTION} Omitted, a decimal.`),
24838
25206
  default_value: numberDefaultValueSchema
24839
- }).superRefine(figureIssue([])),
25207
+ }).superRefine(notationStatedIssue([])),
24840
25208
  zod_default.object({
24841
25209
  type: zod_default.literal("text"),
24842
25210
  unique: zod_default.boolean().optional().describe("Unique values required"),
@@ -24900,8 +25268,8 @@ var tableFieldTypeConfigSchema = zod_default.discriminatedUnion("type", [
24900
25268
  }),
24901
25269
  zod_default.object({
24902
25270
  type: zod_default.literal("formula"),
24903
- formula: formulaInputSchema
24904
- }).superRefine((field, ctx) => figureIssue(["formula"])(field.formula, ctx)),
25271
+ formula: formulaToolInputSchema
25272
+ }).superRefine((field, ctx) => notationStatedIssue(["formula"])(field.formula, ctx)).superRefine((field, ctx) => formulaLinkIssue(field.formula, ctx)),
24905
25273
  // `button` is deliberately absent: button fields are legacy — this union
24906
25274
  // shapes CREATE input only, and new buttons can no longer be created
24907
25275
  // (see LEGACY_FIELD_TYPES). Stored button fields still parse via the
@@ -24972,10 +25340,8 @@ function projectCell(field, value) {
24972
25340
  }
24973
25341
  }
24974
25342
 
24975
- // ../shared/src/walk_workflow_expression.ts
24976
- var LINKED_CALL = "linked";
24977
- var GRAMMAR_NAME_SHAPE = `${LINKED_CALL}(record["fld_link"])[0] fetches one linked row`;
24978
- var RUNTIME_KEYS = /* @__PURE__ */ new Set([
25343
+ // ../shared/src/schemas/workflow_expressions.ts
25344
+ var runtimeKeySchema = zod_default.enum([
24979
25345
  "timezone",
24980
25346
  "workflow_id",
24981
25347
  "execution_id",
@@ -24983,184 +25349,10 @@ var RUNTIME_KEYS = /* @__PURE__ */ new Set([
24983
25349
  "organization_id",
24984
25350
  "now",
24985
25351
  "change_origin",
24986
- "execution_principal",
24987
25352
  "triggered_by_member_id"
24988
25353
  ]);
24989
- var CURRENT_MEMBER_IN_ANY_GROUP = "current_member_in_any_group";
24990
- var PARKED_RUNTIME_KEYS = /* @__PURE__ */ new Map([
24991
- [
24992
- "execution_principal",
24993
- `runtime.execution_principal is not readable \u2014 a workflow runs under the OWNER's authority, so it never answers "is the person who triggered this allowed?". Authorize the caller with ${CURRENT_MEMBER_IN_ANY_GROUP}(["grp_\u2026"]) or read runtime.triggered_by_member_id.`
24994
- ]
24995
- ]);
24996
- var READABLE_RUNTIME_KEYS = Array.from(RUNTIME_KEYS).filter(
24997
- (key) => !PARKED_RUNTIME_KEYS.has(key)
24998
- );
24999
- var INLINE_HELPERS = [
25000
- "formatCurrency",
25001
- "randomNumber",
25002
- "randomAlphaNumeric",
25003
- "sample",
25004
- // Actor authorization predicate — resolves the triggering member's groups
25005
- // server-side (bound in the backend helper registry, not a pure shared
25006
- // helper).
25007
- CURRENT_MEMBER_IN_ANY_GROUP
25008
- ];
25009
- var HELPERS = /* @__PURE__ */ new Set([
25010
- ...Object.keys(expression_array_exports),
25011
- ...Object.keys(expression_string_exports),
25012
- ...Object.keys(expression_math_exports),
25013
- ...Object.keys(expression_object_exports),
25014
- ...Object.keys(expression_type_exports),
25015
- ...Object.keys(getDateExpressionFunctions()),
25016
- ...INLINE_HELPERS
25017
- ]);
25018
- function isHelper(name) {
25019
- return HELPERS.has(name);
25020
- }
25021
- var HELPER_MENU = [
25022
- `text \u2014 ${Object.keys(expression_string_exports).sort().join(", ")}`,
25023
- `number \u2014 ${Object.keys(expression_math_exports).sort().join(", ")}`,
25024
- `array \u2014 ${Object.keys(expression_array_exports).sort().join(", ")}`,
25025
- `object \u2014 ${Object.keys(expression_object_exports).sort().join(", ")}`,
25026
- `type/null \u2014 ${Object.keys(expression_type_exports).sort().join(", ")}`,
25027
- `date \u2014 ${Object.keys(getDateExpressionFunctions()).sort().join(", ")}`,
25028
- `other \u2014 ${[...INLINE_HELPERS].sort().join(", ")}`
25029
- ].join("; ");
25030
- var AWAIT_IF_FORM = "Write the if form: let x = null; if (<condition>) { x = await tool({...}); }";
25031
- var AWAIT_IN_OPTIONAL_CHAIN = `An await after \`?.\` runs only when the value before it is not null. ${AWAIT_IF_FORM}`;
25032
- var CALLBACK_HELPERS = /* @__PURE__ */ new Set([
25033
- "filter",
25034
- "find",
25035
- "pluck",
25036
- "sortBy",
25037
- "sumBy",
25038
- "meanBy",
25039
- "minBy",
25040
- "maxBy",
25041
- "countBy",
25042
- "groupBy",
25043
- "uniqueBy",
25044
- "some",
25045
- "every",
25046
- "differenceBy",
25047
- "intersectionBy",
25048
- "reduce"
25049
- ]);
25050
- var LAMBDA_POSITION_HINT = `Function expressions are not supported here. A lambda \u2014 either \`(x) => <expr>\` or \`function (x) { return <expr>; }\` \u2014 is legal only INLINE as a callback argument to ${[...CALLBACK_HELPERS].sort().join(" / ")}, e.g. pluck(rows, (r) => r.qty * r.price). To NAME reusable logic, declare it at the top level instead: \`function toLine(l = {}) { return { ... }; }\` \u2014 every parameter needs a default, and the body is inlined at each call site. Binding a lambda to a name (const f = (x) => ...) or calling one directly stays unsupported.`;
25051
- var HELPER_ALIASES = /* @__PURE__ */ new Map([
25052
- ["String", "toString"],
25053
- ["Number", "toNumber"],
25054
- ["parseFloat", "toNumber"],
25055
- // faithful: toNumber IS parseFloat semantics
25056
- ["toUpperCase", "upper"],
25057
- ["toLowerCase", "lower"],
25058
- ["map", "pluck"],
25059
- // arr.map(x => …) ≡ pluck(arr, x => …)
25060
- ["toFixed", "formatNumber"]
25061
- // x.toFixed(2) ≡ formatNumber(x, 2)
25062
- ]);
25063
- var REJECTED_HELPERS = /* @__PURE__ */ new Map([
25064
- [
25065
- "parseInt",
25066
- {
25067
- message: "parseInt is not supported \u2014 parse with toNumber(x), then apply floor(...), ceil(...), or round(...) to get a whole number. floor rounds down (toward negative infinity), so for negative values choose the rounding deliberately.",
25068
- construct: "parse_int"
25069
- }
25070
- ]
25071
- ]);
25072
- var NAMESPACE_HELPERS = /* @__PURE__ */ new Map([
25073
- ["JSON", /* @__PURE__ */ new Map([["parse", "parseJson"], ["stringify", "toJson"]])],
25074
- ["Array", /* @__PURE__ */ new Map([["isArray", "isArray"]])],
25075
- ["Object", /* @__PURE__ */ new Map([["keys", "keys"], ["values", "values"], ["entries", "entries"]])],
25076
- ["Number", /* @__PURE__ */ new Map([["parseFloat", "toNumber"]])]
25077
- ]);
25078
- var METHOD_FORM_NAMES = /* @__PURE__ */ new Set([
25079
- // String.prototype
25080
- "trim",
25081
- "toUpperCase",
25082
- "toLowerCase",
25083
- "replace",
25084
- "replaceAll",
25085
- "split",
25086
- "substring",
25087
- "padStart",
25088
- "padEnd",
25089
- "startsWith",
25090
- "endsWith",
25091
- // String.prototype + Array.prototype
25092
- "includes",
25093
- "slice",
25094
- "at",
25095
- "concat",
25096
- // Array.prototype
25097
- "join",
25098
- "reverse",
25099
- "map",
25100
- "filter",
25101
- "find",
25102
- "some",
25103
- "every",
25104
- "reduce",
25105
- // Number.prototype / Object.prototype
25106
- "toFixed",
25107
- "toString"
25108
- ]);
25109
- var METHOD_FORM_MENU = [...METHOD_FORM_NAMES].sort().join(", ");
25110
- var PROPERTY_HELPERS = /* @__PURE__ */ new Map([["length", "length"]]);
25111
- var HELPER_SPELLINGS = (() => {
25112
- const out = /* @__PURE__ */ new Map();
25113
- const add = (helper, spelling) => {
25114
- if (isHelper(helper) && !out.has(helper)) out.set(helper, spelling);
25115
- };
25116
- for (const name of METHOD_FORM_NAMES) add(HELPER_ALIASES.get(name) ?? name, { kind: "method", name });
25117
- for (const [name, helper] of PROPERTY_HELPERS) add(helper, { kind: "property", name });
25118
- for (const [namespace, members2] of NAMESPACE_HELPERS) {
25119
- for (const [member, helper] of members2) add(helper, { kind: "namespace", namespace, member });
25120
- }
25121
- for (const fn of Object.keys(expression_math_exports)) {
25122
- if (fn !== "random" && typeof Reflect.get(Math, fn) === "function") {
25123
- add(fn, { kind: "namespace", namespace: "Math", member: fn });
25124
- }
25125
- }
25126
- return out;
25127
- })();
25128
- var RESERVED_CALLABLE_NAMES = /* @__PURE__ */ new Set([
25129
- ...REJECTED_HELPERS.keys(),
25130
- ...NAMESPACE_HELPERS.keys(),
25131
- "Math",
25132
- "Boolean"
25133
- ]);
25134
- var LINKED_ARITY = `${LINKED_CALL}(...) takes exactly one argument \u2014 the read of the link field, e.g. ${LINKED_CALL}(record["fld_link"])[0] for one row or ${LINKED_CALL}(record["fld_link"]) for every row.`;
25135
- var LINKED_ARGUMENT_SHAPE = `The argument of ${LINKED_CALL}(...) must END on the link field, because that field is what it follows \u2014 ${LINKED_CALL}(record["fld_link"])[0]. A value that names no field (a binding, a literal, a helper result) cannot say which link to follow; read the field on it: ${LINKED_CALL}(ids["fld_link"]).`;
25136
- var LINKED_OPTIONAL_RESULT = `${LINKED_CALL}(...) always returns an array, never null, so \`?.\` on its result can never fire. The argument takes a guard \u2014 ${LINKED_CALL}(record?.["fld_link"])[0] \u2014 and so does the fetched row, which is null where the linked record is deleted or unreadable: ${LINKED_CALL}(record["fld_link"])[0]?.["fld_x"].`;
25137
- var LINKED_METHOD_FORM = `${LINKED_CALL} has no method form \u2014 write ${LINKED_CALL}(record["fld_link"])[0], with the link read as the argument.`;
25138
- var LINKED_ON_NON_RECORD = `${LINKED_CALL}(...) follows a link field on a record, and this value is not one. Read the record first, then follow the link: ${LINKED_CALL}(record["fld_link"])[0].`;
25139
- var LINKED_ON_CHANGES = `A change diff is not a record, so ${LINKED_CALL}(...) cannot follow a link off it. Read the post-state with ${LINKED_CALL}(record["fld_link"]) or the pre-state with ${LINKED_CALL}(prev_record["fld_link"]).`;
25140
- var READ_ROOT_HINT = `A read starts from a name: a reserved identifier (record, prev_record, changes, runtime, trigger, index), a binding, or ${LINKED_CALL}(record["fld_link"]). Bind the value first \u2014 const x = <expression>; \u2014 then read x.y.`;
25141
-
25142
- // ../shared/src/schemas/workflow_expressions.ts
25143
- var runtimeKeySchema = zod_default.enum([
25144
- "timezone",
25145
- "workflow_id",
25146
- "execution_id",
25147
- "workspace_id",
25148
- "organization_id",
25149
- "now",
25150
- "change_origin",
25151
- // PARKED — accepted in stored ASTs, rejected for new saves at parse (see
25152
- // `PARKED_RUNTIME_KEYS` in walk_workflow_expression.ts). It is the OWNER's
25153
- // principal (a workflow runs under owner authority), so it can't answer the
25154
- // caller-authorization question anyone reads it for; the generated `.d.ts`
25155
- // omits it and the docs point at `current_member_in_any_group` /
25156
- // `runtime.triggered_by_member_id` instead. The entry stays so any stored
25157
- // AST keeps validating and executing — drop it only once a prod probe shows
25158
- // zero references (`workflows.steps_v2::text LIKE '%execution_principal%'`).
25159
- "execution_principal",
25160
- "triggered_by_member_id"
25161
- ]);
25162
- var nestedExpressionSchema = zod_default.lazy(
25163
- () => workflowExpressionSchema
25354
+ var nestedExpressionSchema = zod_default.lazy(
25355
+ () => workflowExpressionSchema
25164
25356
  );
25165
25357
  var readSourceSchema = zod_default.discriminatedUnion("from", [
25166
25358
  zod_default.object({ from: zod_default.literal("trigger") }),
@@ -25329,6 +25521,154 @@ var templatePartSchema = zod_default.lazy(
25329
25521
  ])
25330
25522
  );
25331
25523
 
25524
+ // ../shared/src/walk_workflow_expression.ts
25525
+ var LINKED_CALL = "linked";
25526
+ var GRAMMAR_NAME_SHAPE = `${LINKED_CALL}(record["fld_link"])[0] fetches one linked row`;
25527
+ var RUNTIME_KEYS = new Set(runtimeKeySchema.options);
25528
+ var CURRENT_MEMBER_IN_ANY_GROUP = "current_member_in_any_group";
25529
+ var INLINE_HELPERS = [
25530
+ "formatCurrency",
25531
+ "randomNumber",
25532
+ "randomAlphaNumeric",
25533
+ "sample",
25534
+ // Actor authorization predicate — resolves the triggering member's groups
25535
+ // server-side (bound in the backend helper registry, not a pure shared
25536
+ // helper).
25537
+ CURRENT_MEMBER_IN_ANY_GROUP
25538
+ ];
25539
+ var HELPERS = /* @__PURE__ */ new Set([
25540
+ ...Object.keys(expression_array_exports),
25541
+ ...Object.keys(expression_string_exports),
25542
+ ...Object.keys(expression_math_exports),
25543
+ ...Object.keys(expression_object_exports),
25544
+ ...Object.keys(expression_type_exports),
25545
+ ...Object.keys(getDateExpressionFunctions()),
25546
+ ...INLINE_HELPERS
25547
+ ]);
25548
+ function isHelper(name) {
25549
+ return HELPERS.has(name);
25550
+ }
25551
+ var HELPER_MENU = [
25552
+ `text \u2014 ${Object.keys(expression_string_exports).sort().join(", ")}`,
25553
+ `number \u2014 ${Object.keys(expression_math_exports).sort().join(", ")}`,
25554
+ `array \u2014 ${Object.keys(expression_array_exports).sort().join(", ")}`,
25555
+ `object \u2014 ${Object.keys(expression_object_exports).sort().join(", ")}`,
25556
+ `type/null \u2014 ${Object.keys(expression_type_exports).sort().join(", ")}`,
25557
+ `date \u2014 ${Object.keys(getDateExpressionFunctions()).sort().join(", ")}`,
25558
+ `other \u2014 ${[...INLINE_HELPERS].sort().join(", ")}`
25559
+ ].join("; ");
25560
+ var AWAIT_IF_FORM = "Write the if form: let x = null; if (<condition>) { x = await tool({...}); }";
25561
+ var AWAIT_IN_OPTIONAL_CHAIN = `An await after \`?.\` runs only when the value before it is not null. ${AWAIT_IF_FORM}`;
25562
+ var CALLBACK_HELPERS = /* @__PURE__ */ new Set([
25563
+ "filter",
25564
+ "find",
25565
+ "pluck",
25566
+ "sortBy",
25567
+ "sumBy",
25568
+ "meanBy",
25569
+ "minBy",
25570
+ "maxBy",
25571
+ "countBy",
25572
+ "groupBy",
25573
+ "uniqueBy",
25574
+ "some",
25575
+ "every",
25576
+ "differenceBy",
25577
+ "intersectionBy",
25578
+ "reduce"
25579
+ ]);
25580
+ var LAMBDA_POSITION_HINT = `Function expressions are not supported here. A lambda \u2014 either \`(x) => <expr>\` or \`function (x) { return <expr>; }\` \u2014 is legal only INLINE as a callback argument to ${[...CALLBACK_HELPERS].sort().join(" / ")}, e.g. pluck(rows, (r) => r.qty * r.price). To NAME reusable logic, declare it at the top level instead: \`function toLine(l = {}) { return { ... }; }\` \u2014 every parameter needs a default, and the body is inlined at each call site. Binding a lambda to a name (const f = (x) => ...) or calling one directly stays unsupported.`;
25581
+ var HELPER_ALIASES = /* @__PURE__ */ new Map([
25582
+ ["String", "toString"],
25583
+ ["Number", "toNumber"],
25584
+ ["parseFloat", "toNumber"],
25585
+ // faithful: toNumber IS parseFloat semantics
25586
+ ["toUpperCase", "upper"],
25587
+ ["toLowerCase", "lower"],
25588
+ ["map", "pluck"],
25589
+ // arr.map(x => …) ≡ pluck(arr, x => …)
25590
+ ["toFixed", "formatNumber"]
25591
+ // x.toFixed(2) ≡ formatNumber(x, 2)
25592
+ ]);
25593
+ var REJECTED_HELPERS = /* @__PURE__ */ new Map([
25594
+ [
25595
+ "parseInt",
25596
+ {
25597
+ message: "parseInt is not supported \u2014 parse with toNumber(x), then apply floor(...), ceil(...), or round(...) to get a whole number. floor rounds down (toward negative infinity), so for negative values choose the rounding deliberately.",
25598
+ construct: "parse_int"
25599
+ }
25600
+ ]
25601
+ ]);
25602
+ var NAMESPACE_HELPERS = /* @__PURE__ */ new Map([
25603
+ ["JSON", /* @__PURE__ */ new Map([["parse", "parseJson"], ["stringify", "toJson"]])],
25604
+ ["Array", /* @__PURE__ */ new Map([["isArray", "isArray"]])],
25605
+ ["Object", /* @__PURE__ */ new Map([["keys", "keys"], ["values", "values"], ["entries", "entries"]])],
25606
+ ["Number", /* @__PURE__ */ new Map([["parseFloat", "toNumber"]])]
25607
+ ]);
25608
+ var METHOD_FORM_NAMES = /* @__PURE__ */ new Set([
25609
+ // String.prototype
25610
+ "trim",
25611
+ "toUpperCase",
25612
+ "toLowerCase",
25613
+ "replace",
25614
+ "replaceAll",
25615
+ "split",
25616
+ "substring",
25617
+ "padStart",
25618
+ "padEnd",
25619
+ "startsWith",
25620
+ "endsWith",
25621
+ // String.prototype + Array.prototype
25622
+ "includes",
25623
+ "slice",
25624
+ "at",
25625
+ "concat",
25626
+ // Array.prototype
25627
+ "join",
25628
+ "reverse",
25629
+ "map",
25630
+ "filter",
25631
+ "find",
25632
+ "some",
25633
+ "every",
25634
+ "reduce",
25635
+ // Number.prototype / Object.prototype
25636
+ "toFixed",
25637
+ "toString"
25638
+ ]);
25639
+ var METHOD_FORM_MENU = [...METHOD_FORM_NAMES].sort().join(", ");
25640
+ var PROPERTY_HELPERS = /* @__PURE__ */ new Map([["length", "length"]]);
25641
+ var HELPER_SPELLINGS = (() => {
25642
+ const out = /* @__PURE__ */ new Map();
25643
+ const add = (helper, spelling) => {
25644
+ if (isHelper(helper) && !out.has(helper)) out.set(helper, spelling);
25645
+ };
25646
+ for (const name of METHOD_FORM_NAMES) add(HELPER_ALIASES.get(name) ?? name, { kind: "method", name });
25647
+ for (const [name, helper] of PROPERTY_HELPERS) add(helper, { kind: "property", name });
25648
+ for (const [namespace, members2] of NAMESPACE_HELPERS) {
25649
+ for (const [member, helper] of members2) add(helper, { kind: "namespace", namespace, member });
25650
+ }
25651
+ for (const fn of Object.keys(expression_math_exports)) {
25652
+ if (fn !== "random" && typeof Reflect.get(Math, fn) === "function") {
25653
+ add(fn, { kind: "namespace", namespace: "Math", member: fn });
25654
+ }
25655
+ }
25656
+ return out;
25657
+ })();
25658
+ var RESERVED_CALLABLE_NAMES = /* @__PURE__ */ new Set([
25659
+ ...REJECTED_HELPERS.keys(),
25660
+ ...NAMESPACE_HELPERS.keys(),
25661
+ "Math",
25662
+ "Boolean"
25663
+ ]);
25664
+ var LINKED_ARITY = `${LINKED_CALL}(...) takes exactly one argument \u2014 the read of the link field, e.g. ${LINKED_CALL}(record["fld_link"])[0] for one row or ${LINKED_CALL}(record["fld_link"]) for every row.`;
25665
+ var LINKED_ARGUMENT_SHAPE = `The argument of ${LINKED_CALL}(...) must END on the link field, because that field is what it follows \u2014 ${LINKED_CALL}(record["fld_link"])[0]. A value that names no field (a binding, a literal, a helper result) cannot say which link to follow; read the field on it: ${LINKED_CALL}(ids["fld_link"]).`;
25666
+ var LINKED_OPTIONAL_RESULT = `${LINKED_CALL}(...) always returns an array, never null, so \`?.\` on its result can never fire. The argument takes a guard \u2014 ${LINKED_CALL}(record?.["fld_link"])[0] \u2014 and so does the fetched row, which is null where the linked record is deleted or unreadable: ${LINKED_CALL}(record["fld_link"])[0]?.["fld_x"].`;
25667
+ var LINKED_METHOD_FORM = `${LINKED_CALL} has no method form \u2014 write ${LINKED_CALL}(record["fld_link"])[0], with the link read as the argument.`;
25668
+ var LINKED_ON_NON_RECORD = `${LINKED_CALL}(...) follows a link field on a record, and this value is not one. Read the record first, then follow the link: ${LINKED_CALL}(record["fld_link"])[0].`;
25669
+ var LINKED_ON_CHANGES = `A change diff is not a record, so ${LINKED_CALL}(...) cannot follow a link off it. Read the post-state with ${LINKED_CALL}(record["fld_link"]) or the pre-state with ${LINKED_CALL}(prev_record["fld_link"]).`;
25670
+ var READ_ROOT_HINT = `A read starts from a name: a reserved identifier (record, prev_record, changes, runtime, trigger, index), a binding, or ${LINKED_CALL}(record["fld_link"]). Bind the value first \u2014 const x = <expression>; \u2014 then read x.y.`;
25671
+
25332
25672
  // ../shared/src/chat_models.ts
25333
25673
  var MODEL_TIERS = ["haiku", "sonnet", "opus"];
25334
25674
  var TIER_MODELS = {
@@ -26976,36 +27316,46 @@ var WorkflowEvalError = class extends Error {
26976
27316
  detail;
26977
27317
  };
26978
27318
  var WorkflowTimeBudgetError = class extends Error {
26979
- constructor(subject, budgetMs) {
26980
- super(`${subject} exceeded its time budget of ${budgetMs / 1e3}s.`);
27319
+ /** `waitedFor`: what the run's deadline passed waiting on, when that was not the run's own work. */
27320
+ constructor(subject, budgetMs, waitedFor) {
27321
+ super(`${subject} exceeded its time budget of ${budgetMs / 1e3}s${waitedFor === void 0 ? "" : ` waiting for ${waitedFor}`}.`);
26981
27322
  this.name = "WorkflowTimeBudgetError";
26982
27323
  }
26983
27324
  };
26984
27325
  var WorkflowExpressionBudgetError = class extends Error {
26985
- constructor(exceeded) {
26986
- super(
26987
- exceeded === "nodes" ? `Expression evaluated more than ${MAX_EVALUATION_NODES} nodes. A callback nested in another runs once per pair of elements.` : `The run built more than ${MAX_RUN_BUILT_BYTES / 1024 / 1024}MB of arrays, objects and text. Build long text with join() rather than a fold of +.`
26988
- );
26989
- this.exceeded = exceeded;
27326
+ constructor(budget) {
27327
+ super(budgetMessage(budget));
27328
+ this.budget = budget;
26990
27329
  this.name = "WorkflowExpressionBudgetError";
26991
27330
  }
26992
- exceeded;
27331
+ budget;
26993
27332
  };
26994
- var MAX_EVALUATION_NODES = 2e7;
27333
+ function budgetMessage(budget) {
27334
+ switch (budget.exceeded) {
27335
+ case "bytes":
27336
+ return `The run built more than ${MAX_RUN_BUILT_BYTES / 1024 / 1024}MB of arrays, objects and text.`;
27337
+ case "heap":
27338
+ return `The expression used more than ${budget.heap_mb}MB of memory.`;
27339
+ case "compute":
27340
+ return `The expression computed for more than ${budget.compute_ms / 1e3}s.`;
27341
+ }
27342
+ }
26995
27343
  var SLOT_BYTES = 8;
26996
27344
  var CHAR_BYTES = 2;
27345
+ var OBJECT_BYTES = 56;
26997
27346
  var EVALUATION_SLICE_MS = 20;
26998
27347
  var turn = {
26999
27348
  slice_started_ms: performance.now(),
27000
27349
  pause: null
27001
27350
  };
27002
- function startRunBudget(subject, budgetMs) {
27003
- return { deadline_ms: Date.now() + budgetMs, budget_ms: budgetMs, subject, built_bytes: 0 };
27351
+ function startRunBudget(subject, budgetMs, path) {
27352
+ return { host: "shared", deadline_ms: Date.now() + budgetMs, budget_ms: budgetMs, subject, path, built_bytes: 0 };
27004
27353
  }
27005
27354
  function runClock(run2) {
27006
27355
  if (Date.now() > run2.deadline_ms) {
27007
27356
  throw new WorkflowTimeBudgetError(run2.subject, run2.budget_ms);
27008
27357
  }
27358
+ if (run2.host === "isolated") return null;
27009
27359
  if (turn.pause === null && performance.now() - turn.slice_started_ms >= EVALUATION_SLICE_MS) {
27010
27360
  turn.pause = new Promise((resolve) => setTimeout(resolve, 0)).then(() => {
27011
27361
  turn.pause = null;
@@ -27015,15 +27365,16 @@ function runClock(run2) {
27015
27365
  return turn.pause;
27016
27366
  }
27017
27367
  function chargeBuilt(run2, bytes) {
27368
+ if (run2.host === "isolated") return;
27018
27369
  run2.built_bytes += bytes;
27019
27370
  if (run2.built_bytes > MAX_RUN_BUILT_BYTES) {
27020
- throw new WorkflowExpressionBudgetError("bytes");
27371
+ throw new WorkflowExpressionBudgetError({ exceeded: "bytes" });
27021
27372
  }
27022
27373
  }
27023
27374
  function builtSize(value) {
27024
27375
  if (typeof value === "string") return value.length * CHAR_BYTES;
27025
- if (Array.isArray(value)) return value.length * SLOT_BYTES;
27026
- if (typeof value === "object" && value !== null) return Object.keys(value).length * SLOT_BYTES;
27376
+ if (Array.isArray(value)) return OBJECT_BYTES + value.length * SLOT_BYTES;
27377
+ if (typeof value === "object" && value !== null) return OBJECT_BYTES + Object.keys(value).length * SLOT_BYTES;
27027
27378
  return 0;
27028
27379
  }
27029
27380
  var PARSED_NODE_BYTES = 40;
@@ -27053,13 +27404,13 @@ function callbackWorkers() {
27053
27404
  };
27054
27405
  }
27055
27406
  function evaluateWorkflowExpression(expr2, ctx) {
27056
- const budget = { nodes: 0, workers: callbackWorkers(), settled: false };
27407
+ const budget = { workers: callbackWorkers(), settled: false };
27057
27408
  return evaluateExpression(expr2, { ...ctx, evaluation_budget: budget }).finally(() => {
27058
27409
  budget.settled = true;
27059
27410
  });
27060
27411
  }
27061
27412
  async function evaluateExpression(expr2, ctx) {
27062
- for (let pause = spendNode(ctx); pause !== null; pause = checkClock(ctx)) await pause;
27413
+ for (let pause = checkClock(ctx); pause !== null; pause = checkClock(ctx)) await pause;
27063
27414
  switch (expr2.kind) {
27064
27415
  case "lit":
27065
27416
  return expr2.value;
@@ -27096,15 +27447,6 @@ async function evaluateExpression(expr2, ctx) {
27096
27447
  return assertNever(expr2);
27097
27448
  }
27098
27449
  }
27099
- function spendNode(ctx) {
27100
- const budget = ctx.evaluation_budget;
27101
- assertUnsettled(budget);
27102
- budget.nodes += 1;
27103
- if (budget.nodes > MAX_EVALUATION_NODES) {
27104
- throw new WorkflowExpressionBudgetError("nodes");
27105
- }
27106
- return runClock(ctx.run_budget);
27107
- }
27108
27450
  function checkClock(ctx) {
27109
27451
  assertUnsettled(ctx.evaluation_budget);
27110
27452
  return runClock(ctx.run_budget);
@@ -28096,9 +28438,6 @@ function clientRuntime(input) {
28096
28438
  },
28097
28439
  get change_origin() {
28098
28440
  return unread("change_origin");
28099
- },
28100
- get execution_principal() {
28101
- return unread("execution_principal");
28102
28441
  }
28103
28442
  };
28104
28443
  }
@@ -28131,7 +28470,7 @@ async function predictWorkflowEffects(input) {
28131
28470
  trigger_table: null,
28132
28471
  member_lookup: async () => unread("member display"),
28133
28472
  lexical_bindings: [{}],
28134
- run_budget: startRunBudget("Write prediction", PREDICTION_BUDGET_MS)
28473
+ run_budget: startRunBudget("Write prediction", PREDICTION_BUDGET_MS, "write")
28135
28474
  };
28136
28475
  const walk = { ctx, effects: [], written, input, unsure: false, aligned: true };
28137
28476
  try {
@@ -28144,6 +28483,9 @@ async function predictWorkflowEffects(input) {
28144
28483
  }
28145
28484
 
28146
28485
  // ../shared/src/lookup_display.ts
28486
+ function flattenLookupValues(values2) {
28487
+ return values2.flatMap((value) => Array.isArray(value) ? flattenLookupValues(value) : [value]);
28488
+ }
28147
28489
  function lookupElementToString(element) {
28148
28490
  if (element === null || element === void 0) return "";
28149
28491
  if (typeof element === "boolean") return element ? "True" : "False";
@@ -29882,9 +30224,6 @@ function evaluateCondition(record2, fieldByKey, looseCondition, memberContext, f
29882
30224
  }
29883
30225
  return matchCell(columnValue, field, filterCondition, looseCondition, memberContext, fileFacts);
29884
30226
  }
29885
- function flattenLookupValues(values2) {
29886
- return values2.flatMap((value) => Array.isArray(value) ? flattenLookupValues(value) : [value]);
29887
- }
29888
30227
  function matchLookup(cell, field, filterCondition, looseCondition, memberContext, fileFacts) {
29889
30228
  switch (filterCondition.type) {
29890
30229
  case "select":
@@ -30302,197 +30641,378 @@ function matchCell(cell, field, filterCondition, looseCondition, memberContext,
30302
30641
  if (!term) return null;
30303
30642
  matches = files !== null && files.some((entry) => fileProperty(entry, property, fileFacts, fieldKey, filterCondition.operator).toLowerCase().includes(term));
30304
30643
  }
30305
- } else {
30306
- const op = typeof looseCondition.operator === "string" ? looseCondition.operator : "(unset)";
30307
- const source = field ? `'${String(resolvedType)}', resolved from field '${fieldKey}'` : resolvedType === void 0 ? `missing, and field '${fieldKey}' is not on this table to resolve it from` : `'${String(resolvedType)}', stated on the condition (field '${fieldKey}' is not on this table)`;
30308
- throw new Error(
30309
- `Filter condition cannot be evaluated: filter type ${source} has no evaluator. Evaluable: text, number, date, boolean, select, select_member, select_record_link, files, locked. Condition operator was "${op}".`
30310
- );
30311
- }
30312
- return matches;
30644
+ } else {
30645
+ const op = typeof looseCondition.operator === "string" ? looseCondition.operator : "(unset)";
30646
+ const source = field ? `'${String(resolvedType)}', resolved from field '${fieldKey}'` : resolvedType === void 0 ? `missing, and field '${fieldKey}' is not on this table to resolve it from` : `'${String(resolvedType)}', stated on the condition (field '${fieldKey}' is not on this table)`;
30647
+ throw new Error(
30648
+ `Filter condition cannot be evaluated: filter type ${source} has no evaluator. Evaluable: text, number, date, boolean, select, select_member, select_record_link, files, locked. Condition operator was "${op}".`
30649
+ );
30650
+ }
30651
+ return matches;
30652
+ }
30653
+
30654
+ // ../shared/src/record_filter_truth.ts
30655
+ var BY_VIEWER = /* @__PURE__ */ new Set(["is_current_member", "is_not_current_member"]);
30656
+ function condition(node, subject) {
30657
+ if (node.type === "locked" || node.type === "record_id" || node.type === "current_member") return "unknown";
30658
+ if (node.field_key === void 0 || node.unit_option !== void 0) return "unknown";
30659
+ if (typeof node.value === "string" && node.value.includes("{{")) return "unknown";
30660
+ if (BY_VIEWER.has(node.operator) && subject.member_id === null) return "unknown";
30661
+ if (FILE_NAME_OPERATORS.has(node.operator)) return "unknown";
30662
+ const field = subject.field(node.field_key);
30663
+ const held2 = subject.cell(node.field_key);
30664
+ if (field === void 0 || held2 === void 0) return "unknown";
30665
+ const decided = evaluateCondition(
30666
+ { data: { [node.field_key]: held2.value } },
30667
+ { [node.field_key]: field },
30668
+ node,
30669
+ subject.member_id === null ? null : { member_id: subject.member_id, group_ids: [] }
30670
+ );
30671
+ return decided === null ? "unknown" : decided;
30672
+ }
30673
+ function recordFilterTruth(filter2, subject) {
30674
+ switch (filter2.node_type) {
30675
+ case "condition":
30676
+ return condition(filter2, subject);
30677
+ case "traversal":
30678
+ return "unknown";
30679
+ case "group": {
30680
+ const each = filter2.children.map((child) => recordFilterTruth(child, subject));
30681
+ if (filter2.logic === "and") {
30682
+ if (each.includes(false)) return false;
30683
+ return each.every((one) => one === true) ? true : "unknown";
30684
+ }
30685
+ if (each.includes(true)) return true;
30686
+ return each.every((one) => one === false) ? false : "unknown";
30687
+ }
30688
+ }
30689
+ }
30690
+
30691
+ // ../shared/src/interpolate.ts
30692
+ function interpolateObject(obj, context) {
30693
+ if (obj === null || obj === void 0) {
30694
+ return obj;
30695
+ }
30696
+ if (typeof obj === "string") {
30697
+ const expressionMatches = obj.match(/\{\{.*?\}\}/g);
30698
+ if (expressionMatches && expressionMatches.length > 0) {
30699
+ if (expressionMatches.length === 1 && obj === expressionMatches[0]) {
30700
+ const expression = obj.slice(2, -2).trim();
30701
+ return evaluateJsExpression(expression, context);
30702
+ }
30703
+ const parts = obj.split(/(\{\{.*?\}\})/g);
30704
+ const evaluatedParts = parts.map((part) => {
30705
+ if (part.startsWith("{{") && part.endsWith("}}")) {
30706
+ const expression = part.slice(2, -2).trim();
30707
+ const result = evaluateJsExpression(expression, context);
30708
+ return result != null ? String(result) : "";
30709
+ }
30710
+ return part;
30711
+ });
30712
+ return evaluatedParts.join("");
30713
+ }
30714
+ return obj;
30715
+ }
30716
+ if (Array.isArray(obj)) {
30717
+ return obj.map((item) => interpolateObject(item, context));
30718
+ }
30719
+ if (typeof obj === "object") {
30720
+ const result = {};
30721
+ for (const [key, value] of Object.entries(obj)) {
30722
+ result[key] = interpolateObject(value, context);
30723
+ }
30724
+ return result;
30725
+ }
30726
+ return obj;
30727
+ }
30728
+
30729
+ // ../shared/src/app_query_ast.ts
30730
+ function collectParamTokens(node, found) {
30731
+ if (typeof node === "string") {
30732
+ for (const tok of node.matchAll(/\{\{(.*?)\}\}/g)) {
30733
+ for (const m of tok[1].matchAll(/\bparams\.([a-zA-Z_$][\w$]*)/g)) {
30734
+ found.add(m[1]);
30735
+ }
30736
+ }
30737
+ return;
30738
+ }
30739
+ if (Array.isArray(node)) {
30740
+ for (const item of node) collectParamTokens(item, found);
30741
+ return;
30742
+ }
30743
+ if (node && typeof node === "object") {
30744
+ for (const value of Object.values(node)) collectParamTokens(value, found);
30745
+ }
30746
+ }
30747
+ function paramTokens(node) {
30748
+ const tokens = /* @__PURE__ */ new Set();
30749
+ collectParamTokens(node, tokens);
30750
+ return tokens;
30751
+ }
30752
+ function pruneFilterTree(filter2, survives) {
30753
+ if (filter2.node_type === "group") {
30754
+ const children = filter2.children.map((child) => pruneFilterTree(child, survives)).filter((c) => c !== void 0);
30755
+ if (children.length === 0) return void 0;
30756
+ return { ...filter2, children };
30757
+ }
30758
+ return survives.leaf(filter2) ? filter2 : void 0;
30759
+ }
30760
+ function statedParamNames(params) {
30761
+ return new Set(Object.keys(params).filter((name) => !isUnstatedFilterValue(params[name])));
30762
+ }
30763
+ function statesNothing(leaf, params) {
30764
+ const filled = interpolateObject(leaf.node_type === "traversal" ? leaf.condition : leaf, { params });
30765
+ return isUnstatedConditionValue(filled.operator, filled.value);
30766
+ }
30767
+ function searchStatesNothing(search, params) {
30768
+ const filled = interpolateObject(search, { params });
30769
+ return typeof filled === "string" && searchWords(filled).length === 0;
30770
+ }
30771
+ function survivesCall(params) {
30772
+ const stated = statedParamNames(params);
30773
+ const readsOnlyStated = (node) => {
30774
+ const tokens = paramTokens(node);
30775
+ if (tokens.size === 0) return void 0;
30776
+ return [...tokens].every((name) => stated.has(name));
30777
+ };
30778
+ return {
30779
+ leaf: (leaf) => {
30780
+ const reads = readsOnlyStated(leaf);
30781
+ return reads === void 0 || reads && !statesNothing(leaf, params);
30782
+ },
30783
+ search: (search) => {
30784
+ const reads = readsOnlyStated(search);
30785
+ return reads === void 0 || reads && !searchStatesNothing(search, params);
30786
+ }
30787
+ };
30788
+ }
30789
+ function filterForCall(filter2, params) {
30790
+ const pruned = pruneFilterTree(filter2, survivesCall(params));
30791
+ return pruned === void 0 ? void 0 : interpolateObject(pruned, { params });
30313
30792
  }
30314
30793
 
30315
- // ../shared/src/record_filter_truth.ts
30316
- var BY_VIEWER = /* @__PURE__ */ new Set(["is_current_member", "is_not_current_member"]);
30317
- function condition(node, subject) {
30318
- if (node.type === "locked" || node.type === "record_id" || node.type === "current_member") return "unknown";
30319
- if (node.field_key === void 0 || node.unit_option !== void 0) return "unknown";
30320
- if (typeof node.value === "string" && node.value.includes("{{")) return "unknown";
30321
- if (BY_VIEWER.has(node.operator) && subject.member_id === null) return "unknown";
30322
- if (FILE_NAME_OPERATORS.has(node.operator)) return "unknown";
30323
- const field = subject.field(node.field_key);
30324
- const held2 = subject.cell(node.field_key);
30325
- if (field === void 0 || held2 === void 0) return "unknown";
30326
- const decided = evaluateCondition(
30327
- { data: { [node.field_key]: held2.value } },
30328
- { [node.field_key]: field },
30329
- node,
30330
- subject.member_id === null ? null : { member_id: subject.member_id, group_ids: [] }
30331
- );
30332
- return decided === null ? "unknown" : decided;
30794
+ // ../shared/src/app_query_output_schema.ts
30795
+ function fieldTypeToColumnType(field) {
30796
+ switch (field.type) {
30797
+ case "text":
30798
+ return "text";
30799
+ case "number":
30800
+ return "number";
30801
+ case "boolean":
30802
+ return "boolean";
30803
+ case "date":
30804
+ return field.format === "datetime" || field.format === "datetime_range" ? "datetime" : "date";
30805
+ case "select":
30806
+ return "select";
30807
+ case "select_member":
30808
+ return "select_member";
30809
+ case "select_record_link":
30810
+ return "select_record_link";
30811
+ case "files":
30812
+ return "files";
30813
+ case "rollup":
30814
+ return rollupColumnType(field);
30815
+ case "lookup":
30816
+ return lookupOutputType(field);
30817
+ case "formula":
30818
+ return formulaOutputType(field);
30819
+ case "button":
30820
+ return "json";
30821
+ case "autonumber":
30822
+ return "text";
30823
+ default: {
30824
+ const _exhaustive = field;
30825
+ void _exhaustive;
30826
+ return "json";
30827
+ }
30828
+ }
30333
30829
  }
30334
- function recordFilterTruth(filter2, subject) {
30335
- switch (filter2.node_type) {
30336
- case "condition":
30337
- return condition(filter2, subject);
30338
- case "traversal":
30339
- return "unknown";
30340
- case "group": {
30341
- const each = filter2.children.map((child) => recordFilterTruth(child, subject));
30342
- if (filter2.logic === "and") {
30343
- if (each.includes(false)) return false;
30344
- return each.every((one) => one === true) ? true : "unknown";
30830
+ function formulaOutputType(field) {
30831
+ switch (field.formula.output_type) {
30832
+ case "number":
30833
+ return "number";
30834
+ case "boolean":
30835
+ return "boolean";
30836
+ case "date":
30837
+ return "date";
30838
+ case "datetime":
30839
+ return "datetime";
30840
+ case "text":
30841
+ return "text";
30842
+ case "select":
30843
+ return "select";
30844
+ default:
30845
+ return "json";
30846
+ }
30847
+ }
30848
+ function rollupColumnType(field) {
30849
+ if (rollupOutputType(field.aggregate_option.operation) === "number") return "number";
30850
+ return field.aggregate_field_format === "datetime" || field.aggregate_field_format === "datetime_range" ? "datetime" : "date";
30851
+ }
30852
+ function lookupOutputType(field) {
30853
+ const t = field.lookup_field_type;
30854
+ if (!t) return "json";
30855
+ switch (t) {
30856
+ case "text":
30857
+ return "text";
30858
+ case "number":
30859
+ return "number";
30860
+ case "boolean":
30861
+ return "boolean";
30862
+ case "date":
30863
+ return field.lookup_field_format === "datetime" || field.lookup_field_format === "datetime_range" ? "datetime" : "date";
30864
+ case "select":
30865
+ return "select";
30866
+ case "select_member":
30867
+ return "select_member";
30868
+ case "select_record_link":
30869
+ return "select_record_link";
30870
+ case "files":
30871
+ return "files";
30872
+ default:
30873
+ return "json";
30874
+ }
30875
+ }
30876
+
30877
+ // ../shared/src/field_value.ts
30878
+ function fieldValueInfo(field) {
30879
+ switch (field.type) {
30880
+ case "number":
30881
+ return { type: "number", notation: fieldNotation(field) };
30882
+ case "text":
30883
+ return { type: "text", format: field.format };
30884
+ case "date":
30885
+ return { type: "date", format: field.format };
30886
+ case "select":
30887
+ return { type: "select", options: field.options };
30888
+ case "formula": {
30889
+ const output = field.formula?.output_type;
30890
+ if (field.formula.options !== void 0) return { type: "select", options: field.formula.options };
30891
+ if (output === "date" || output === "datetime") {
30892
+ return { type: "date", format: output === "datetime" ? "datetime" : "date" };
30345
30893
  }
30346
- if (each.includes(true)) return true;
30347
- return each.every((one) => one === false) ? false : "unknown";
30894
+ if (output === "boolean") return { type: "boolean" };
30895
+ if (output === "text") return { type: "text", format: formulaLinks(field.formula) ? "link" : void 0 };
30896
+ return { type: "number", notation: fieldNotation(field) };
30897
+ }
30898
+ case "rollup": {
30899
+ const type = rollupOutputType(field.aggregate_option.operation);
30900
+ return type === "number" ? { type, notation: fieldNotation(field) } : { type, format: field.aggregate_field_format };
30348
30901
  }
30902
+ case "lookup":
30903
+ return {
30904
+ type: field.lookup_field_type ?? "text",
30905
+ ...field.lookup_field_type === "number" ? { notation: fieldNotation(field) } : { format: field.lookup_field_format },
30906
+ options: field.lookup_field_options,
30907
+ autonumber: field.lookup_field_autonumber
30908
+ };
30909
+ // The cell stores the composed DISPLAY string ("KH-001"), never the counter.
30910
+ case "autonumber":
30911
+ return { type: "text", autonumber: true };
30912
+ default:
30913
+ return { type: field.type };
30349
30914
  }
30350
30915
  }
30351
30916
 
30352
30917
  // ../shared/src/field_comparison.ts
30353
- var SIMPLE_LOOKUP_TYPES = /* @__PURE__ */ new Set([
30354
- "text",
30355
- "number",
30356
- "date",
30357
- "boolean",
30358
- "formula",
30359
- "rollup",
30360
- "files"
30361
- ]);
30362
- function isArrayFieldEmpty(value) {
30363
- if (value === void 0 || value === null) {
30364
- return true;
30365
- }
30366
- if (!Array.isArray(value)) {
30367
- return false;
30918
+ var MemberNamesUnavailableError = class extends Error {
30919
+ constructor(fieldKey) {
30920
+ super(
30921
+ `Comparing "${fieldKey}" orders members by name, and its cells hold member ids \u2014 the workspace's member names must be supplied to compare them.`
30922
+ );
30923
+ this.name = "MemberNamesUnavailableError";
30368
30924
  }
30369
- if (value.length === 0) {
30370
- return true;
30925
+ };
30926
+ function firstMember(cell) {
30927
+ const first2 = Array.isArray(cell) ? cell[0] : void 0;
30928
+ return typeof first2 === "string" ? first2 : void 0;
30929
+ }
30930
+ function firstLink(cell) {
30931
+ const first2 = Array.isArray(cell) ? cell[0] : void 0;
30932
+ if (typeof first2 !== "object" || first2 === null || !("id" in first2) || typeof first2.id !== "string") return void 0;
30933
+ const display = "display" in first2 && typeof first2.display === "string" ? first2.display : "";
30934
+ return { name: display, id: first2.id };
30935
+ }
30936
+ function folded(text2) {
30937
+ return text2.trim().toLowerCase();
30938
+ }
30939
+ function compareNamed(a, b) {
30940
+ const byName = folded(a.name).localeCompare(folded(b.name), "en-US");
30941
+ return byName !== 0 ? byName : a.id.localeCompare(b.id, "en-US");
30942
+ }
30943
+ function optionRank(cell, options) {
30944
+ const first2 = Array.isArray(cell) ? cell[0] : void 0;
30945
+ if (first2 === void 0 || first2 === null) return void 0;
30946
+ const at2 = options.findIndex((option) => option.key === first2);
30947
+ return { by: "figure", value: at2 === -1 ? options.length : at2 };
30948
+ }
30949
+ function valueRank(cell, type) {
30950
+ if (cell === void 0 || cell === null) return void 0;
30951
+ switch (type) {
30952
+ case "number":
30953
+ return typeof cell === "number" && Number.isFinite(cell) ? { by: "figure", value: cell } : void 0;
30954
+ case "boolean":
30955
+ return typeof cell === "boolean" ? { by: "figure", value: cell ? 1 : 0 } : void 0;
30956
+ case "date":
30957
+ case "datetime":
30958
+ return typeof cell === "string" && /^\d{4}/.test(cell) ? { by: "day", value: cell } : void 0;
30959
+ case "text":
30960
+ return { by: "text", value: folded(String(cell)) };
30961
+ case "select_member": {
30962
+ const id = firstMember(cell);
30963
+ return id === void 0 ? void 0 : { by: "member", id };
30964
+ }
30965
+ case "select_record_link": {
30966
+ const link2 = firstLink(cell);
30967
+ return link2 === void 0 ? void 0 : { by: "name", value: link2 };
30968
+ }
30969
+ case "files":
30970
+ return Array.isArray(cell) ? { by: "figure", value: cell.length } : void 0;
30971
+ case "select":
30972
+ case "json":
30973
+ return typeof cell === "number" ? { by: "figure", value: cell } : { by: "text", value: folded(String(cell)) };
30974
+ }
30975
+ }
30976
+ function autonumberTerms(cell) {
30977
+ if (cell === void 0 || cell === null) return [void 0, void 0];
30978
+ const text2 = String(cell);
30979
+ return /^-?\d+$/.test(text2) ? [{ by: "figure", value: Number(text2) }, void 0] : [void 0, { by: "text", value: text2 }];
30980
+ }
30981
+ function termsOf(cell, field) {
30982
+ const value = field.type === "lookup" && Array.isArray(cell) ? cell[0] : cell;
30983
+ if (fieldValueInfo(field).autonumber === true) return autonumberTerms(value);
30984
+ const options = effectiveSelectOptions(field);
30985
+ return [options === null ? valueRank(value, fieldTypeToColumnType(field)) : optionRank(value, options)];
30986
+ }
30987
+ function rankText(rank) {
30988
+ if (rank.by === "name") return folded(rank.value.name);
30989
+ if (rank.by === "member") return rank.id;
30990
+ return String(rank.value);
30991
+ }
30992
+ function compareFieldValues(aValue, bValue, field, { blank = "bottom", order = "asc", memberNames } = {}) {
30993
+ if (aValue === bValue) return 0;
30994
+ const a = termsOf(aValue, field);
30995
+ const b = termsOf(bValue, field);
30996
+ const member = (id) => {
30997
+ if (memberNames === void 0) throw new MemberNamesUnavailableError(field.key);
30998
+ return { name: memberNames.get(id) ?? "", id };
30999
+ };
31000
+ for (const [at2, left] of a.entries()) {
31001
+ const right = b[at2];
31002
+ if (left === void 0 && right === void 0) continue;
31003
+ if (left === void 0) return blank === "top" ? -1 : 1;
31004
+ if (right === void 0) return blank === "top" ? 1 : -1;
31005
+ const compared = compareRanks(left, right, member);
31006
+ if (compared !== 0) return order === "asc" ? compared : -compared;
30371
31007
  }
30372
- return value[0] === void 0 || value[0] === null;
31008
+ return 0;
30373
31009
  }
30374
- function compareFieldValues(aValue, bValue, field, blankPosition = "bottom", order = "asc") {
30375
- if (aValue === bValue) {
30376
- return 0;
30377
- }
30378
- const options = selectOptionsOf(field);
30379
- if (options !== void 0) {
30380
- const aSelectArray = Array.isArray(aValue) ? aValue : null;
30381
- const bSelectArray = Array.isArray(bValue) ? bValue : null;
30382
- const aSelectValue = aSelectArray?.[0];
30383
- const bSelectValue = bSelectArray?.[0];
30384
- const aIsEmpty2 = isArrayFieldEmpty(aSelectArray);
30385
- const bIsEmpty2 = isArrayFieldEmpty(bSelectArray);
30386
- if (aIsEmpty2 && bIsEmpty2) {
30387
- return 0;
30388
- }
30389
- if (aIsEmpty2) {
30390
- return blankPosition === "top" ? -1 : 1;
30391
- }
30392
- if (bIsEmpty2) {
30393
- return blankPosition === "top" ? 1 : -1;
30394
- }
30395
- const aIndex = options.findIndex((option) => option.key === aSelectValue);
30396
- const bIndex = options.findIndex((option) => option.key === bSelectValue);
30397
- const aPosition = aIndex === -1 ? Number.MAX_SAFE_INTEGER : aIndex;
30398
- const bPosition = bIndex === -1 ? Number.MAX_SAFE_INTEGER : bIndex;
30399
- const diff = aPosition - bPosition;
30400
- return order === "asc" ? diff : -diff;
30401
- }
30402
- const aIsEmpty = aValue === void 0 || aValue === null;
30403
- const bIsEmpty = bValue === void 0 || bValue === null;
30404
- if (aIsEmpty && bIsEmpty) {
30405
- return 0;
30406
- }
30407
- if (aIsEmpty) {
30408
- return blankPosition === "top" ? -1 : 1;
30409
- }
30410
- if (bIsEmpty) {
30411
- return blankPosition === "top" ? 1 : -1;
30412
- }
30413
- if (field.type === "number" || field.type === "rollup" || field.type === "formula") {
30414
- const diff = aValue - bValue;
30415
- return order === "asc" ? diff : -diff;
30416
- }
30417
- if (field.type === "boolean") {
30418
- const aBool = aValue;
30419
- const bBool = bValue;
30420
- if (aBool === bBool) return 0;
30421
- const diff = aBool === true ? 1 : -1;
30422
- return order === "asc" ? diff : -diff;
30423
- }
30424
- if (field.type === "date") {
30425
- const aStr = String(aValue);
30426
- const bStr = String(bValue);
30427
- const cmp = aStr < bStr ? -1 : aStr > bStr ? 1 : 0;
30428
- return order === "asc" ? cmp : -cmp;
30429
- }
30430
- if (field.type === "files") {
30431
- const aFiles = aValue;
30432
- const bFiles = bValue;
30433
- const aLength = aFiles?.length || 0;
30434
- const bLength = bFiles?.length || 0;
30435
- const diff = aLength - bLength;
30436
- return order === "asc" ? diff : -diff;
30437
- }
30438
- if (field.type === "select_member") {
30439
- const aArray = aValue ?? [];
30440
- const bArray = bValue ?? [];
30441
- const aIsEmpty2 = isArrayFieldEmpty(aArray);
30442
- const bIsEmpty2 = isArrayFieldEmpty(bArray);
30443
- if (aIsEmpty2 && bIsEmpty2) {
30444
- return 0;
30445
- }
30446
- if (aIsEmpty2) {
30447
- return blankPosition === "top" ? -1 : 1;
30448
- }
30449
- if (bIsEmpty2) {
30450
- return blankPosition === "top" ? 1 : -1;
30451
- }
30452
- const aString2 = String(aArray[0] || "");
30453
- const bString2 = String(bArray[0] || "");
30454
- const compareResult2 = aString2.localeCompare(bString2, "en-US");
30455
- return order === "asc" ? compareResult2 : -compareResult2;
30456
- }
30457
- if (field.type === "select_record_link") {
30458
- const aItems = aValue ?? [];
30459
- const bItems = bValue ?? [];
30460
- const aIsEmpty2 = aItems.length === 0;
30461
- const bIsEmpty2 = bItems.length === 0;
30462
- if (aIsEmpty2 && bIsEmpty2) {
30463
- return 0;
30464
- }
30465
- if (aIsEmpty2) {
30466
- return blankPosition === "top" ? -1 : 1;
30467
- }
30468
- if (bIsEmpty2) {
30469
- return blankPosition === "top" ? 1 : -1;
30470
- }
30471
- const aString2 = aItems[0]?.display ?? "";
30472
- const bString2 = bItems[0]?.display ?? "";
30473
- const compareResult2 = aString2.localeCompare(bString2, "en-US");
30474
- return order === "asc" ? compareResult2 : -compareResult2;
30475
- }
30476
- if (field.type === "lookup") {
30477
- const lookupField = field;
30478
- const aArr = Array.isArray(aValue) ? aValue : [aValue];
30479
- const bArr = Array.isArray(bValue) ? bValue : [bValue];
30480
- const aFirst = aArr[0] ?? null;
30481
- const bFirst = bArr[0] ?? null;
30482
- const sourceType = lookupField.lookup_field_type || "text";
30483
- if (SIMPLE_LOOKUP_TYPES.has(sourceType)) {
30484
- const syntheticField = { ...field, type: sourceType };
30485
- return compareFieldValues(aFirst, bFirst, syntheticField, blankPosition, order);
30486
- }
30487
- const aString2 = String(aFirst ?? "");
30488
- const bString2 = String(bFirst ?? "");
30489
- const cmp = aString2.localeCompare(bString2, "en-US");
30490
- return order === "asc" ? cmp : -cmp;
30491
- }
30492
- const aString = String(aValue || "");
30493
- const bString = String(bValue || "");
30494
- const compareResult = aString.localeCompare(bString, "en-US");
30495
- return order === "asc" ? compareResult : -compareResult;
31010
+ function compareRanks(a, b, member) {
31011
+ if (a.by === "figure" && b.by === "figure") return a.value - b.value;
31012
+ if (a.by === "day" && b.by === "day") return a.value < b.value ? -1 : a.value > b.value ? 1 : 0;
31013
+ if (a.by === "name" && b.by === "name") return compareNamed(a.value, b.value);
31014
+ if (a.by === "member" && b.by === "member") return compareNamed(member(a.id), member(b.id));
31015
+ return rankText(a).localeCompare(rankText(b), "en-US");
30496
31016
  }
30497
31017
 
30498
31018
  // ../shared/src/formula_references.ts
@@ -30566,7 +31086,7 @@ function prepareFormulaContext(fields) {
30566
31086
  nonFormulaFields
30567
31087
  };
30568
31088
  }
30569
- function computeFormulaFieldsWithContext(row2, fields, context, linkedTableRecords = /* @__PURE__ */ new Map(), linkedTableFields = /* @__PURE__ */ new Map(), timezone, loadedLinkKeys, linkExpandedTableIds, fileFacts) {
31089
+ function computeFormulaFieldsWithContext(row2, fields, context, linkedTableRecords = /* @__PURE__ */ new Map(), linkedTableFields = /* @__PURE__ */ new Map(), timezone, loadedLinkKeys, linkExpandedTableIds, fileFacts, memberNames) {
30570
31090
  const result = { ...row2 };
30571
31091
  const mayRecompute = (linkKey) => loadedLinkKeys === void 0 || loadedLinkKeys.has(linkKey) || isEmptyFieldValue(row2[linkKey]);
30572
31092
  for (const { field, sourceField } of context.rollupFields) {
@@ -30631,7 +31151,8 @@ function computeFormulaFieldsWithContext(row2, fields, context, linkedTableRecor
30631
31151
  field.lookup_field_key,
30632
31152
  linkedField,
30633
31153
  field.order_by,
30634
- linkedFieldsArray
31154
+ linkedFieldsArray,
31155
+ memberNames
30635
31156
  );
30636
31157
  result[field.key] = lookupValue;
30637
31158
  } else {
@@ -30961,7 +31482,7 @@ function calculateRollupValue(value, fieldType, allLinkedTableRecords, aggregate
30961
31482
  }
30962
31483
  return aggregateByField(relevantRecords, fieldType, aggregateOption, subject).result;
30963
31484
  }
30964
- function calculateLookupValue(value, linkedRecords, lookupFieldKey, linkedField, orderBy, linkedFields) {
31485
+ function calculateLookupValue(value, linkedRecords, lookupFieldKey, linkedField, orderBy, linkedFields, memberNames) {
30965
31486
  if (!linkedRecords.length || !lookupFieldKey) {
30966
31487
  return null;
30967
31488
  }
@@ -30986,13 +31507,11 @@ function calculateLookupValue(value, linkedRecords, lookupFieldKey, linkedField,
30986
31507
  return null;
30987
31508
  }
30988
31509
  const picked = [...relevantRecords].sort((a, b) => {
30989
- const cmp = compareFieldValues(
30990
- a.data[orderField.key],
30991
- b.data[orderField.key],
30992
- orderField,
30993
- "bottom",
30994
- orderBy.direction
30995
- );
31510
+ const cmp = compareFieldValues(a.data[orderField.key], b.data[orderField.key], orderField, {
31511
+ blank: "bottom",
31512
+ order: orderBy.direction,
31513
+ memberNames
31514
+ });
30996
31515
  if (cmp !== 0) return cmp;
30997
31516
  if (a.created_at !== b.created_at) return a.created_at < b.created_at ? 1 : -1;
30998
31517
  return a.id < b.id ? 1 : a.id > b.id ? -1 : 0;
@@ -31008,109 +31527,6 @@ function calculateLookupValue(value, linkedRecords, lookupFieldKey, linkedField,
31008
31527
  return values2.length > 0 ? values2 : null;
31009
31528
  }
31010
31529
 
31011
- // ../shared/src/interpolate.ts
31012
- function interpolateObject(obj, context) {
31013
- if (obj === null || obj === void 0) {
31014
- return obj;
31015
- }
31016
- if (typeof obj === "string") {
31017
- const expressionMatches = obj.match(/\{\{.*?\}\}/g);
31018
- if (expressionMatches && expressionMatches.length > 0) {
31019
- if (expressionMatches.length === 1 && obj === expressionMatches[0]) {
31020
- const expression = obj.slice(2, -2).trim();
31021
- return evaluateJsExpression(expression, context);
31022
- }
31023
- const parts = obj.split(/(\{\{.*?\}\})/g);
31024
- const evaluatedParts = parts.map((part) => {
31025
- if (part.startsWith("{{") && part.endsWith("}}")) {
31026
- const expression = part.slice(2, -2).trim();
31027
- const result = evaluateJsExpression(expression, context);
31028
- return result != null ? String(result) : "";
31029
- }
31030
- return part;
31031
- });
31032
- return evaluatedParts.join("");
31033
- }
31034
- return obj;
31035
- }
31036
- if (Array.isArray(obj)) {
31037
- return obj.map((item) => interpolateObject(item, context));
31038
- }
31039
- if (typeof obj === "object") {
31040
- const result = {};
31041
- for (const [key, value] of Object.entries(obj)) {
31042
- result[key] = interpolateObject(value, context);
31043
- }
31044
- return result;
31045
- }
31046
- return obj;
31047
- }
31048
-
31049
- // ../shared/src/app_query_ast.ts
31050
- function collectParamTokens(node, found) {
31051
- if (typeof node === "string") {
31052
- for (const tok of node.matchAll(/\{\{(.*?)\}\}/g)) {
31053
- for (const m of tok[1].matchAll(/\bparams\.([a-zA-Z_$][\w$]*)/g)) {
31054
- found.add(m[1]);
31055
- }
31056
- }
31057
- return;
31058
- }
31059
- if (Array.isArray(node)) {
31060
- for (const item of node) collectParamTokens(item, found);
31061
- return;
31062
- }
31063
- if (node && typeof node === "object") {
31064
- for (const value of Object.values(node)) collectParamTokens(value, found);
31065
- }
31066
- }
31067
- function paramTokens(node) {
31068
- const tokens = /* @__PURE__ */ new Set();
31069
- collectParamTokens(node, tokens);
31070
- return tokens;
31071
- }
31072
- function pruneFilterTree(filter2, survives) {
31073
- if (filter2.node_type === "group") {
31074
- const children = filter2.children.map((child) => pruneFilterTree(child, survives)).filter((c) => c !== void 0);
31075
- if (children.length === 0) return void 0;
31076
- return { ...filter2, children };
31077
- }
31078
- return survives.leaf(filter2) ? filter2 : void 0;
31079
- }
31080
- function statedParamNames(params) {
31081
- return new Set(Object.keys(params).filter((name) => !isUnstatedFilterValue(params[name])));
31082
- }
31083
- function statesNothing(leaf, params) {
31084
- const filled = interpolateObject(leaf.node_type === "traversal" ? leaf.condition : leaf, { params });
31085
- return isUnstatedConditionValue(filled.operator, filled.value);
31086
- }
31087
- function searchStatesNothing(search, params) {
31088
- const filled = interpolateObject(search, { params });
31089
- return typeof filled === "string" && searchWords(filled).length === 0;
31090
- }
31091
- function survivesCall(params) {
31092
- const stated = statedParamNames(params);
31093
- const readsOnlyStated = (node) => {
31094
- const tokens = paramTokens(node);
31095
- if (tokens.size === 0) return void 0;
31096
- return [...tokens].every((name) => stated.has(name));
31097
- };
31098
- return {
31099
- leaf: (leaf) => {
31100
- const reads = readsOnlyStated(leaf);
31101
- return reads === void 0 || reads && !statesNothing(leaf, params);
31102
- },
31103
- search: (search) => {
31104
- const reads = readsOnlyStated(search);
31105
- return reads === void 0 || reads && !searchStatesNothing(search, params);
31106
- }
31107
- };
31108
- }
31109
- function filterForCall(filter2, params) {
31110
- const pruned = pruneFilterTree(filter2, survivesCall(params));
31111
- return pruned === void 0 ? void 0 : interpolateObject(pruned, { params });
31112
- }
31113
-
31114
31530
  // src/overlay.ts
31115
31531
  var NONE2 = /* @__PURE__ */ new Set();
31116
31532
  function fieldKeyOf(shape, column, tableId) {