@lotics/app-sdk 0.115.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,179 +24236,67 @@ function everyOptionMarked(nameOf2) {
23971
24236
  };
23972
24237
  }
23973
24238
 
23974
- // ../shared/src/schemas/iam.ts
23975
- var jsonSchemaSchema = zod_default.string();
23976
- var organizationSchema = zod_default.object({
23977
- id: zod_default.string().describe("Unique identifier for the organization"),
23978
- name: zod_default.string().describe("Display name of the organization"),
23979
- slug: zod_default.string().nullable().describe("Slug for the organization")
23980
- });
23981
- var memberAccountTypeSchema = zod_default.enum(["self_managed", "admin_managed", "service"]).describe(
23982
- "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."
23983
- );
23984
- var ALL_ACCOUNT_TYPES = memberAccountTypeSchema.options;
23985
- var memberSchema = zod_default.object({
23986
- member_id: zod_default.string().describe("Unique identifier for the member"),
23987
- organization_id: zod_default.string().describe("Organization ID the member belongs to"),
23988
- name: zod_default.string().describe("Display name of the member"),
23989
- email_address: zod_default.string().nullable().describe(
23990
- "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."
23991
- ),
23992
- role: zod_default.enum(["owner", "admin", "member"]).describe("Organization role"),
23993
- image: zod_default.string().nullable().optional().describe("URL to the member's profile picture"),
23994
- account_type: memberAccountTypeSchema,
23995
- archived: zod_default.boolean().optional().describe(
23996
- "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."
23997
- ),
23998
- joined_at: zod_default.string().optional().describe(
23999
- "ISO timestamp of when this membership began. Optional because an older server omits it \u2014 absent means not told, never 'joined at the epoch'."
24000
- )
24001
- });
24002
- var pendingInvitationSchema = zod_default.object({
24003
- invitation_id: zod_default.string().describe("Unique identifier for the pending invitation"),
24004
- organization_id: zod_default.string().describe("Organization the invitee will join on accept"),
24005
- email: zod_default.string().describe("Email the invitation was sent to"),
24006
- name: zod_default.string().nullable().describe("Admin-supplied display name (carried onto member.name on accept; invitee can override at accept time)"),
24007
- role: zod_default.enum(["owner", "admin", "member"]).describe("Role the invitee will receive on accept"),
24008
- expires_at: zod_default.string().describe("ISO timestamp when the invitation expires")
24009
- });
24010
- var ianaTimezoneSchema = zod_default.string().refine(
24011
- (tz) => {
24012
- try {
24013
- new Intl.DateTimeFormat("en-US", { timeZone: tz });
24014
- return true;
24015
- } catch {
24016
- return false;
24017
- }
24018
- },
24019
- { message: "must be an IANA timezone name, e.g. Asia/Ho_Chi_Minh or America/New_York" }
24020
- ).describe("IANA timezone name (e.g., 'America/New_York', 'Asia/Ho_Chi_Minh')");
24021
- 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.");
24022
- var workspaceSchema = zod_default.object({
24023
- id: zod_default.string().describe("Unique identifier for the workspace"),
24024
- name: zod_default.string().describe("Display name of the workspace"),
24025
- default_currency: zod_default.string().describe("Default currency code for the workspace"),
24026
- // Plain string on the way OUT, deliberately. This is a response schema, so
24027
- // validating here would make the server reject its own stored rows — and on
24028
- // the workspace LIST one bad row fails the entire array. The zone is enforced
24029
- // where it is written; a read is too late to do anything about it anyway.
24030
- timezone: zod_default.string().describe("IANA timezone name (e.g., 'America/New_York', 'Asia/Ho_Chi_Minh')"),
24031
- organization_id: zod_default.string().describe("Organization ID this workspace belongs to"),
24032
- created_at: zod_default.string().describe("Timestamp when workspace was created")
24033
- });
24034
- var memberGroupSchema = zod_default.object({
24035
- id: zod_default.string().describe("Unique identifier for the member group"),
24036
- name: zod_default.string().describe("Display name of the group"),
24037
- description: zod_default.string().optional().describe("Optional description of the group's purpose"),
24038
- member_ids: zod_default.array(zod_default.string()).describe("Array of member IDs in this group"),
24039
- invitation_ids: zod_default.array(zod_default.string()).describe("Array of pending invitation IDs pre-granted membership; migrated to member_ids on accept"),
24040
- organization_id: zod_default.string().describe("ID of the organization this group belongs to"),
24041
- created_at: zod_default.string().describe("Timestamp when group was created"),
24042
- created_by: zod_default.string().describe("Member ID of the user who created this group")
24043
- });
24044
- var principalGroupSchema = zod_default.object({
24045
- id: zod_default.string().describe("Unique identifier for the member group"),
24046
- name: zod_default.string().describe("Display name of the group"),
24047
- description: zod_default.string().optional().describe("Optional description of the group's purpose"),
24048
- member_count: zod_default.number().int().describe("Number of accepted members in the group"),
24049
- invitation_count: zod_default.number().int().describe("Number of pending invitations pre-granted membership")
24050
- });
24051
- var PUBLIC_PRINCIPAL_ID = "public";
24052
- var principalSchema = zod_default.discriminatedUnion("type", [
24053
- zod_default.object({
24054
- type: zod_default.literal("member").describe("Principal type: individual member"),
24055
- id: zod_default.string().describe("Member ID")
24056
- }),
24057
- zod_default.object({
24058
- type: zod_default.literal("member_group").describe("Principal type: group of members"),
24059
- id: zod_default.string().describe("Member group ID")
24060
- }),
24061
- zod_default.object({
24062
- type: zod_default.literal("organization").describe("Principal type: all members in organization"),
24063
- id: zod_default.string().describe("Organization ID")
24064
- }),
24065
- zod_default.object({
24066
- type: zod_default.literal("public").describe("Principal type: anyone, including unauthenticated visitors"),
24067
- id: zod_default.literal(PUBLIC_PRINCIPAL_ID).describe("Always the `public` sentinel \u2014 no entity")
24068
- }),
24069
- // Pending invitation as principal: lets admins pre-grant group membership and
24070
- // resource access before the invitee accepts. Never resolved as a Subject —
24071
- // a pending invitee cannot authenticate, so these bindings stay dormant until
24072
- // accept-time migration rewrites them to type:"member".
24073
- zod_default.object({
24074
- type: zod_default.literal("invitation").describe("Principal type: pending invitation (not yet accepted)"),
24075
- id: zod_default.string().describe("Invitation ID")
24076
- })
24077
- ]);
24078
- var subjectSchema = zod_default.discriminatedUnion("type", [
24079
- zod_default.object({
24080
- type: zod_default.literal("member").describe("Subject type: authenticated member"),
24081
- id: zod_default.string().describe("Member ID")
24082
- }),
24083
- zod_default.object({
24084
- type: zod_default.literal("public").describe("Subject type: unauthenticated visitor")
24085
- })
24086
- ]);
24087
- var resourceTypeSchema = zod_default.enum(["table", "table_view", "app", "automation", "skill", "document_template", "instruction", "knowledge_doc", "connected_account"]);
24088
- var RESOURCE_ROLE_ACTIONS = {
24089
- table: {
24090
- editor: ["read_data", "write_data"],
24091
- viewer: ["read_data"]
24092
- },
24093
- app: { manager: ["use", "manage"], user: ["use"] },
24094
- skill: { manager: ["use", "manage"], user: ["use"] },
24095
- document_template: { manager: ["use", "manage"], user: ["use"] },
24096
- instruction: { manager: ["use", "manage"], user: ["use"] },
24097
- knowledge_doc: { manager: ["use", "manage"], user: ["use"] },
24098
- connected_account: { manager: ["use", "manage"], user: ["use"] },
24099
- table_view: {},
24100
- automation: {}
24101
- };
24102
- function shareableRolesFor(type) {
24103
- return Object.keys(RESOURCE_ROLE_ACTIONS[type]);
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;
24104
24299
  }
24105
- var SHAREABLE_RESOURCE_TYPES = resourceTypeSchema.options.filter(
24106
- (type) => shareableRolesFor(type).length > 0
24107
- );
24108
- var accessPrincipalSchema = zod_default.discriminatedUnion("type", [
24109
- zod_default.object({
24110
- type: zod_default.literal("member").describe("Principal type: an individual member"),
24111
- id: zod_default.string().describe("Member ID")
24112
- }),
24113
- zod_default.object({
24114
- type: zod_default.literal("member_group").describe("Principal type: a member group"),
24115
- id: zod_default.string().describe("Group ID")
24116
- }),
24117
- zod_default.object({
24118
- type: zod_default.literal("organization").describe("Principal type: everyone in the organization"),
24119
- id: zod_default.string().describe("Organization ID")
24120
- })
24121
- ]);
24122
- var resourceAccessSchema = zod_default.object({
24123
- resource_id: zod_default.string().describe("ID of the resource these principals can reach"),
24124
- principals: zod_default.array(accessPrincipalSchema).describe("Members, groups and the organization that have been granted access")
24125
- });
24126
- var contentResourceTypeSchema = zod_default.enum(["knowledge_doc", "document_template"]);
24127
- var contentItemSchema = zod_default.object({
24128
- id: zod_default.string().describe("ID of the knowledge doc or template"),
24129
- name: zod_default.string().describe("Its name"),
24130
- owner_member_id: zod_default.string().nullable().describe("Member who owns it; null when nobody does"),
24131
- created_at: zod_default.string().describe("When it was created"),
24132
- updated_at: zod_default.string().nullable().describe("When a knowledge doc was last changed; null for a template"),
24133
- hidden_at: zod_default.string().nullable().describe("When a knowledge doc was hidden; null when it is in use, and for a template"),
24134
- template_type: zod_default.string().nullable().describe("A template's type (html, email, word, excel, pdf-form); null for a knowledge doc"),
24135
- can_open: zod_default.boolean().describe("Whether the caller may open it; false means it is not shared with them"),
24136
- principals: zod_default.array(accessPrincipalSchema).describe("Members, groups and the organization it has been shared with, besides its owner")
24137
- });
24138
- var resourceRoleBindingSchema = zod_default.object({
24139
- id: zod_default.string().describe("Unique identifier for the role binding"),
24140
- principal: principalSchema.describe(
24141
- "Who this role binding applies to (member, group, or organization)"
24142
- ),
24143
- resource_type: resourceTypeSchema.describe("Type of resource being accessed"),
24144
- resource_id: zod_default.string().describe("ID of the specific resource"),
24145
- role: zod_default.string().describe("Role name (e.g., 'viewer', 'editor', 'manager', 'user')")
24146
- });
24147
24300
 
24148
24301
  // ../shared/src/diacritics.ts
24149
24302
  var COMBINING_MARKS = /[̀-ͯ]/g;
@@ -24738,22 +24891,35 @@ function perRowRefusals(value) {
24738
24891
  ...currencyField.map((message2) => ({ key: "currency_field", message: message2 }))
24739
24892
  ];
24740
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
+ }
24741
24898
  function figureIssue(path) {
24742
24899
  return (value, ctx) => {
24743
- const refused = numberUnitRefusal(value.format, value.unit);
24744
- if (refused !== void 0) ctx.addIssue({ code: "custom", message: refused, path: [...path, "unit"] });
24745
- 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] });
24901
+ };
24902
+ }
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"] });
24746
24909
  };
24747
24910
  }
24748
- var tableNumberFieldSchema = tableFieldBaseSchema.extend({
24911
+ var tableNumberFieldShape = tableFieldBaseSchema.extend({
24749
24912
  type: zod_default.literal("number"),
24750
- format: numberFormatSchema,
24751
- currency: zod_default.string().nullish().describe("ISO 4217 code. null clears it."),
24752
- unit: zod_default.string().nullish().describe(`${UNIT_DESCRIPTION} null clears it.`),
24753
- unit_field: zod_default.string().nullish().describe(`${UNIT_FIELD_DESCRIPTION} null clears it.`),
24754
- 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}`),
24755
24919
  default_value: numberDefaultValueSchema
24756
- }).superRefine(figureIssue([]));
24920
+ });
24921
+ var tableNumberFieldSchema = tableNumberFieldShape.superRefine(figureIssue([]));
24922
+ var tableNumberFieldInputSchema = tableNumberFieldShape.extend({ format: numberFormatSchema.optional().describe(BEFORE_NOTATION) }).superRefine(figureIssue([]));
24757
24923
  var tableTextFieldSchema = tableFieldBaseSchema.extend({
24758
24924
  type: zod_default.literal("text"),
24759
24925
  unique: zod_default.boolean().optional().describe("Unique values required"),
@@ -24846,6 +25012,9 @@ var tableRollupFieldSchema = tableFieldBaseSchema.extend({
24846
25012
  aggregate_field_type: tableFieldTypeNameSchema.optional().describe(
24847
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."
24848
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
+ ),
24849
25018
  aggregate_field_format: zod_default.string().optional().describe(
24850
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."
24851
25020
  ),
@@ -24873,6 +25042,9 @@ var tableLookupFieldSchema = tableFieldBaseSchema.extend({
24873
25042
  lookup_field_type: tableFieldTypeNameSchema.optional().describe(
24874
25043
  "The type of the target lookup field (e.g. 'text', 'number', 'date'). Resolved from the linked table's field definition at read time."
24875
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
+ ),
24876
25048
  lookup_field_format: zod_default.string().optional().describe(
24877
25049
  "The format of the target lookup field (e.g. 'currency', 'percentage', 'datetime'). Resolved from the linked table's field definition at read time."
24878
25050
  ),
@@ -24885,6 +25057,9 @@ var tableLookupFieldSchema = tableFieldBaseSchema.extend({
24885
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(
24886
25058
  "Options for select-type lookup fields. Synced from the source select field's options."
24887
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
+ ),
24888
25063
  order_by: lookupOrderBySchema.optional().describe(
24889
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."
24890
25065
  )
@@ -24897,25 +25072,36 @@ var formulaInputSchema = zod_default.object({
24897
25072
  expression: zod_default.string().describe(
24898
25073
  "JavaScript expression. Uses {Field Name} for field references."
24899
25074
  ),
24900
- format: zod_default.enum(["number", "currency", "percentage", "link"]).optional().describe(
24901
- "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."
24902
- ),
24903
- currency: zod_default.string().optional().describe("ISO 4217 currency code, e.g. USD, VND, EUR"),
24904
- unit: zod_default.string().optional().describe(UNIT_DESCRIPTION),
24905
- unit_field: zod_default.string().optional().describe(UNIT_FIELD_DESCRIPTION),
24906
- 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}`),
24907
25082
  options: zod_default.array(tableSelectFieldOptionSchema).min(1).optional().describe(
24908
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."
24909
25084
  )
24910
25085
  });
25086
+ var formulaToolInputSchema = formulaInputSchema.omit({ format: true, currency: true, unit: true, unit_field: true, currency_field: true });
24911
25087
  var formulaSchema = formulaInputSchema.extend({
24912
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`."),
24913
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.")
24914
25090
  });
24915
- var tableFormulaFieldSchema = tableFieldBaseSchema.extend({
25091
+ var tableFormulaFieldShape = tableFieldBaseSchema.extend({
24916
25092
  type: zod_default.literal("formula").describe("Field type for calculated values based on expressions"),
24917
25093
  formula: formulaSchema.describe("Formula configuration and expression")
24918
- }).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));
24919
25105
  var workflowMetadataSchema = zod_default.object({
24920
25106
  table_ids: zod_default.array(zod_default.string()).describe("IDs of the tables this workflow affects"),
24921
25107
  staged_for_app_id: zod_default.string().optional().describe(
@@ -24971,7 +25157,7 @@ var tableFieldSchema = zod_default.discriminatedUnion("type", [
24971
25157
  tableAutonumberFieldSchema
24972
25158
  ]);
24973
25159
  var tableFieldInputSchema = zod_default.discriminatedUnion("type", [
24974
- tableNumberFieldSchema,
25160
+ tableNumberFieldInputSchema,
24975
25161
  tableTextFieldSchema,
24976
25162
  tableDateFieldSchema,
24977
25163
  tableBooleanFieldSchema,
@@ -24981,16 +25167,28 @@ var tableFieldInputSchema = zod_default.discriminatedUnion("type", [
24981
25167
  tableRollupFieldSchema,
24982
25168
  tableLookupFieldSchema,
24983
25169
  tableFilesFieldSchema,
24984
- tableFormulaFieldSchema,
25170
+ tableFormulaFieldInputSchema,
24985
25171
  tableButtonFieldSchema,
24986
25172
  tableAutonumberFieldSchema
24987
25173
  ]);
24988
25174
  var tableFieldsSchema = zod_default.array(tableFieldSchema);
24989
- function selectOptionsOf(field) {
24990
- if (field.type === "select") return field.options;
24991
- if (field.type === "formula") return field.formula.options;
24992
- return void 0;
24993
- }
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
+ );
24994
25192
  var tableFieldCreateBaseSchema = zod_default.object({
24995
25193
  name: zod_default.string().describe("Display name"),
24996
25194
  description: zod_default.string().describe("Field description"),
@@ -25004,13 +25202,9 @@ var tableSelectFieldOptionCreateSchema = zod_default.object({
25004
25202
  var tableFieldTypeConfigSchema = zod_default.discriminatedUnion("type", [
25005
25203
  zod_default.object({
25006
25204
  type: zod_default.literal("number"),
25007
- format: numberFormatSchema.default("number"),
25008
- currency: currencyCodeSchema.optional(),
25009
- unit: zod_default.string().optional().describe(UNIT_DESCRIPTION),
25010
- unit_field: zod_default.string().optional().describe(UNIT_FIELD_DESCRIPTION),
25011
- currency_field: zod_default.string().optional().describe(CURRENCY_FIELD_DESCRIPTION),
25205
+ notation: notationSchema.optional().describe(`${NOTATION_DESCRIPTION} Omitted, a decimal.`),
25012
25206
  default_value: numberDefaultValueSchema
25013
- }).superRefine(figureIssue([])),
25207
+ }).superRefine(notationStatedIssue([])),
25014
25208
  zod_default.object({
25015
25209
  type: zod_default.literal("text"),
25016
25210
  unique: zod_default.boolean().optional().describe("Unique values required"),
@@ -25074,8 +25268,8 @@ var tableFieldTypeConfigSchema = zod_default.discriminatedUnion("type", [
25074
25268
  }),
25075
25269
  zod_default.object({
25076
25270
  type: zod_default.literal("formula"),
25077
- formula: formulaInputSchema.extend({ currency: currencyCodeSchema.optional() })
25078
- }).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)),
25079
25273
  // `button` is deliberately absent: button fields are legacy — this union
25080
25274
  // shapes CREATE input only, and new buttons can no longer be created
25081
25275
  // (see LEGACY_FIELD_TYPES). Stored button fields still parse via the
@@ -28289,6 +28483,9 @@ async function predictWorkflowEffects(input) {
28289
28483
  }
28290
28484
 
28291
28485
  // ../shared/src/lookup_display.ts
28486
+ function flattenLookupValues(values2) {
28487
+ return values2.flatMap((value) => Array.isArray(value) ? flattenLookupValues(value) : [value]);
28488
+ }
28292
28489
  function lookupElementToString(element) {
28293
28490
  if (element === null || element === void 0) return "";
28294
28491
  if (typeof element === "boolean") return element ? "True" : "False";
@@ -30027,9 +30224,6 @@ function evaluateCondition(record2, fieldByKey, looseCondition, memberContext, f
30027
30224
  }
30028
30225
  return matchCell(columnValue, field, filterCondition, looseCondition, memberContext, fileFacts);
30029
30226
  }
30030
- function flattenLookupValues(values2) {
30031
- return values2.flatMap((value) => Array.isArray(value) ? flattenLookupValues(value) : [value]);
30032
- }
30033
30227
  function matchLookup(cell, field, filterCondition, looseCondition, memberContext, fileFacts) {
30034
30228
  switch (filterCondition.type) {
30035
30229
  case "select":
@@ -30494,150 +30688,331 @@ function recordFilterTruth(filter2, subject) {
30494
30688
  }
30495
30689
  }
30496
30690
 
30497
- // ../shared/src/field_comparison.ts
30498
- var SIMPLE_LOOKUP_TYPES = /* @__PURE__ */ new Set([
30499
- "text",
30500
- "number",
30501
- "date",
30502
- "boolean",
30503
- "formula",
30504
- "rollup",
30505
- "files"
30506
- ]);
30507
- function isArrayFieldEmpty(value) {
30508
- if (value === void 0 || value === null) {
30509
- return true;
30691
+ // ../shared/src/interpolate.ts
30692
+ function interpolateObject(obj, context) {
30693
+ if (obj === null || obj === void 0) {
30694
+ return obj;
30510
30695
  }
30511
- if (!Array.isArray(value)) {
30512
- return false;
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;
30513
30715
  }
30514
- if (value.length === 0) {
30515
- return true;
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;
30516
30725
  }
30517
- return value[0] === void 0 || value[0] === null;
30726
+ return obj;
30518
30727
  }
30519
- function compareFieldValues(aValue, bValue, field, blankPosition = "bottom", order = "asc") {
30520
- if (aValue === bValue) {
30521
- return 0;
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;
30522
30738
  }
30523
- const options = selectOptionsOf(field);
30524
- if (options !== void 0) {
30525
- const aSelectArray = Array.isArray(aValue) ? aValue : null;
30526
- const bSelectArray = Array.isArray(bValue) ? bValue : null;
30527
- const aSelectValue = aSelectArray?.[0];
30528
- const bSelectValue = bSelectArray?.[0];
30529
- const aIsEmpty2 = isArrayFieldEmpty(aSelectArray);
30530
- const bIsEmpty2 = isArrayFieldEmpty(bSelectArray);
30531
- if (aIsEmpty2 && bIsEmpty2) {
30532
- return 0;
30533
- }
30534
- if (aIsEmpty2) {
30535
- return blankPosition === "top" ? -1 : 1;
30536
- }
30537
- if (bIsEmpty2) {
30538
- return blankPosition === "top" ? 1 : -1;
30539
- }
30540
- const aIndex = options.findIndex((option) => option.key === aSelectValue);
30541
- const bIndex = options.findIndex((option) => option.key === bSelectValue);
30542
- const aPosition = aIndex === -1 ? Number.MAX_SAFE_INTEGER : aIndex;
30543
- const bPosition = bIndex === -1 ? Number.MAX_SAFE_INTEGER : bIndex;
30544
- const diff = aPosition - bPosition;
30545
- return order === "asc" ? diff : -diff;
30546
- }
30547
- const aIsEmpty = aValue === void 0 || aValue === null;
30548
- const bIsEmpty = bValue === void 0 || bValue === null;
30549
- if (aIsEmpty && bIsEmpty) {
30550
- return 0;
30739
+ if (Array.isArray(node)) {
30740
+ for (const item of node) collectParamTokens(item, found);
30741
+ return;
30551
30742
  }
30552
- if (aIsEmpty) {
30553
- return blankPosition === "top" ? -1 : 1;
30743
+ if (node && typeof node === "object") {
30744
+ for (const value of Object.values(node)) collectParamTokens(value, found);
30554
30745
  }
30555
- if (bIsEmpty) {
30556
- return blankPosition === "top" ? 1 : -1;
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 });
30792
+ }
30793
+
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
+ }
30557
30828
  }
30558
- if (field.type === "number" || field.type === "rollup" || field.type === "formula") {
30559
- const diff = aValue - bValue;
30560
- return order === "asc" ? diff : -diff;
30829
+ }
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";
30561
30846
  }
30562
- if (field.type === "boolean") {
30563
- const aBool = aValue;
30564
- const bBool = bValue;
30565
- if (aBool === bBool) return 0;
30566
- const diff = aBool === true ? 1 : -1;
30567
- return order === "asc" ? diff : -diff;
30568
- }
30569
- if (field.type === "date") {
30570
- const aStr = String(aValue);
30571
- const bStr = String(bValue);
30572
- const cmp = aStr < bStr ? -1 : aStr > bStr ? 1 : 0;
30573
- return order === "asc" ? cmp : -cmp;
30574
- }
30575
- if (field.type === "files") {
30576
- const aFiles = aValue;
30577
- const bFiles = bValue;
30578
- const aLength = aFiles?.length || 0;
30579
- const bLength = bFiles?.length || 0;
30580
- const diff = aLength - bLength;
30581
- return order === "asc" ? diff : -diff;
30582
- }
30583
- if (field.type === "select_member") {
30584
- const aArray = aValue ?? [];
30585
- const bArray = bValue ?? [];
30586
- const aIsEmpty2 = isArrayFieldEmpty(aArray);
30587
- const bIsEmpty2 = isArrayFieldEmpty(bArray);
30588
- if (aIsEmpty2 && bIsEmpty2) {
30589
- return 0;
30590
- }
30591
- if (aIsEmpty2) {
30592
- return blankPosition === "top" ? -1 : 1;
30593
- }
30594
- if (bIsEmpty2) {
30595
- return blankPosition === "top" ? 1 : -1;
30596
- }
30597
- const aString2 = String(aArray[0] || "");
30598
- const bString2 = String(bArray[0] || "");
30599
- const compareResult2 = aString2.localeCompare(bString2, "en-US");
30600
- return order === "asc" ? compareResult2 : -compareResult2;
30601
- }
30602
- if (field.type === "select_record_link") {
30603
- const aItems = aValue ?? [];
30604
- const bItems = bValue ?? [];
30605
- const aIsEmpty2 = aItems.length === 0;
30606
- const bIsEmpty2 = bItems.length === 0;
30607
- if (aIsEmpty2 && bIsEmpty2) {
30608
- return 0;
30609
- }
30610
- if (aIsEmpty2) {
30611
- return blankPosition === "top" ? -1 : 1;
30612
- }
30613
- if (bIsEmpty2) {
30614
- return blankPosition === "top" ? 1 : -1;
30615
- }
30616
- const aString2 = aItems[0]?.display ?? "";
30617
- const bString2 = bItems[0]?.display ?? "";
30618
- const compareResult2 = aString2.localeCompare(bString2, "en-US");
30619
- return order === "asc" ? compareResult2 : -compareResult2;
30620
- }
30621
- if (field.type === "lookup") {
30622
- const lookupField = field;
30623
- const aArr = Array.isArray(aValue) ? aValue : [aValue];
30624
- const bArr = Array.isArray(bValue) ? bValue : [bValue];
30625
- const aFirst = aArr[0] ?? null;
30626
- const bFirst = bArr[0] ?? null;
30627
- const sourceType = lookupField.lookup_field_type || "text";
30628
- if (SIMPLE_LOOKUP_TYPES.has(sourceType)) {
30629
- const syntheticField = { ...field, type: sourceType };
30630
- return compareFieldValues(aFirst, bFirst, syntheticField, blankPosition, order);
30631
- }
30632
- const aString2 = String(aFirst ?? "");
30633
- const bString2 = String(bFirst ?? "");
30634
- const cmp = aString2.localeCompare(bString2, "en-US");
30635
- return order === "asc" ? cmp : -cmp;
30636
- }
30637
- const aString = String(aValue || "");
30638
- const bString = String(bValue || "");
30639
- const compareResult = aString.localeCompare(bString, "en-US");
30640
- return order === "asc" ? compareResult : -compareResult;
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" };
30893
+ }
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 };
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 };
30914
+ }
30915
+ }
30916
+
30917
+ // ../shared/src/field_comparison.ts
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";
30924
+ }
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;
31007
+ }
31008
+ return 0;
31009
+ }
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");
30641
31016
  }
30642
31017
 
30643
31018
  // ../shared/src/formula_references.ts
@@ -30711,7 +31086,7 @@ function prepareFormulaContext(fields) {
30711
31086
  nonFormulaFields
30712
31087
  };
30713
31088
  }
30714
- 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) {
30715
31090
  const result = { ...row2 };
30716
31091
  const mayRecompute = (linkKey) => loadedLinkKeys === void 0 || loadedLinkKeys.has(linkKey) || isEmptyFieldValue(row2[linkKey]);
30717
31092
  for (const { field, sourceField } of context.rollupFields) {
@@ -30776,7 +31151,8 @@ function computeFormulaFieldsWithContext(row2, fields, context, linkedTableRecor
30776
31151
  field.lookup_field_key,
30777
31152
  linkedField,
30778
31153
  field.order_by,
30779
- linkedFieldsArray
31154
+ linkedFieldsArray,
31155
+ memberNames
30780
31156
  );
30781
31157
  result[field.key] = lookupValue;
30782
31158
  } else {
@@ -31106,7 +31482,7 @@ function calculateRollupValue(value, fieldType, allLinkedTableRecords, aggregate
31106
31482
  }
31107
31483
  return aggregateByField(relevantRecords, fieldType, aggregateOption, subject).result;
31108
31484
  }
31109
- function calculateLookupValue(value, linkedRecords, lookupFieldKey, linkedField, orderBy, linkedFields) {
31485
+ function calculateLookupValue(value, linkedRecords, lookupFieldKey, linkedField, orderBy, linkedFields, memberNames) {
31110
31486
  if (!linkedRecords.length || !lookupFieldKey) {
31111
31487
  return null;
31112
31488
  }
@@ -31131,13 +31507,11 @@ function calculateLookupValue(value, linkedRecords, lookupFieldKey, linkedField,
31131
31507
  return null;
31132
31508
  }
31133
31509
  const picked = [...relevantRecords].sort((a, b) => {
31134
- const cmp = compareFieldValues(
31135
- a.data[orderField.key],
31136
- b.data[orderField.key],
31137
- orderField,
31138
- "bottom",
31139
- orderBy.direction
31140
- );
31510
+ const cmp = compareFieldValues(a.data[orderField.key], b.data[orderField.key], orderField, {
31511
+ blank: "bottom",
31512
+ order: orderBy.direction,
31513
+ memberNames
31514
+ });
31141
31515
  if (cmp !== 0) return cmp;
31142
31516
  if (a.created_at !== b.created_at) return a.created_at < b.created_at ? 1 : -1;
31143
31517
  return a.id < b.id ? 1 : a.id > b.id ? -1 : 0;
@@ -31153,109 +31527,6 @@ function calculateLookupValue(value, linkedRecords, lookupFieldKey, linkedField,
31153
31527
  return values2.length > 0 ? values2 : null;
31154
31528
  }
31155
31529
 
31156
- // ../shared/src/interpolate.ts
31157
- function interpolateObject(obj, context) {
31158
- if (obj === null || obj === void 0) {
31159
- return obj;
31160
- }
31161
- if (typeof obj === "string") {
31162
- const expressionMatches = obj.match(/\{\{.*?\}\}/g);
31163
- if (expressionMatches && expressionMatches.length > 0) {
31164
- if (expressionMatches.length === 1 && obj === expressionMatches[0]) {
31165
- const expression = obj.slice(2, -2).trim();
31166
- return evaluateJsExpression(expression, context);
31167
- }
31168
- const parts = obj.split(/(\{\{.*?\}\})/g);
31169
- const evaluatedParts = parts.map((part) => {
31170
- if (part.startsWith("{{") && part.endsWith("}}")) {
31171
- const expression = part.slice(2, -2).trim();
31172
- const result = evaluateJsExpression(expression, context);
31173
- return result != null ? String(result) : "";
31174
- }
31175
- return part;
31176
- });
31177
- return evaluatedParts.join("");
31178
- }
31179
- return obj;
31180
- }
31181
- if (Array.isArray(obj)) {
31182
- return obj.map((item) => interpolateObject(item, context));
31183
- }
31184
- if (typeof obj === "object") {
31185
- const result = {};
31186
- for (const [key, value] of Object.entries(obj)) {
31187
- result[key] = interpolateObject(value, context);
31188
- }
31189
- return result;
31190
- }
31191
- return obj;
31192
- }
31193
-
31194
- // ../shared/src/app_query_ast.ts
31195
- function collectParamTokens(node, found) {
31196
- if (typeof node === "string") {
31197
- for (const tok of node.matchAll(/\{\{(.*?)\}\}/g)) {
31198
- for (const m of tok[1].matchAll(/\bparams\.([a-zA-Z_$][\w$]*)/g)) {
31199
- found.add(m[1]);
31200
- }
31201
- }
31202
- return;
31203
- }
31204
- if (Array.isArray(node)) {
31205
- for (const item of node) collectParamTokens(item, found);
31206
- return;
31207
- }
31208
- if (node && typeof node === "object") {
31209
- for (const value of Object.values(node)) collectParamTokens(value, found);
31210
- }
31211
- }
31212
- function paramTokens(node) {
31213
- const tokens = /* @__PURE__ */ new Set();
31214
- collectParamTokens(node, tokens);
31215
- return tokens;
31216
- }
31217
- function pruneFilterTree(filter2, survives) {
31218
- if (filter2.node_type === "group") {
31219
- const children = filter2.children.map((child) => pruneFilterTree(child, survives)).filter((c) => c !== void 0);
31220
- if (children.length === 0) return void 0;
31221
- return { ...filter2, children };
31222
- }
31223
- return survives.leaf(filter2) ? filter2 : void 0;
31224
- }
31225
- function statedParamNames(params) {
31226
- return new Set(Object.keys(params).filter((name) => !isUnstatedFilterValue(params[name])));
31227
- }
31228
- function statesNothing(leaf, params) {
31229
- const filled = interpolateObject(leaf.node_type === "traversal" ? leaf.condition : leaf, { params });
31230
- return isUnstatedConditionValue(filled.operator, filled.value);
31231
- }
31232
- function searchStatesNothing(search, params) {
31233
- const filled = interpolateObject(search, { params });
31234
- return typeof filled === "string" && searchWords(filled).length === 0;
31235
- }
31236
- function survivesCall(params) {
31237
- const stated = statedParamNames(params);
31238
- const readsOnlyStated = (node) => {
31239
- const tokens = paramTokens(node);
31240
- if (tokens.size === 0) return void 0;
31241
- return [...tokens].every((name) => stated.has(name));
31242
- };
31243
- return {
31244
- leaf: (leaf) => {
31245
- const reads = readsOnlyStated(leaf);
31246
- return reads === void 0 || reads && !statesNothing(leaf, params);
31247
- },
31248
- search: (search) => {
31249
- const reads = readsOnlyStated(search);
31250
- return reads === void 0 || reads && !searchStatesNothing(search, params);
31251
- }
31252
- };
31253
- }
31254
- function filterForCall(filter2, params) {
31255
- const pruned = pruneFilterTree(filter2, survivesCall(params));
31256
- return pruned === void 0 ? void 0 : interpolateObject(pruned, { params });
31257
- }
31258
-
31259
31530
  // src/overlay.ts
31260
31531
  var NONE2 = /* @__PURE__ */ new Set();
31261
31532
  function fieldKeyOf(shape, column, tableId) {