@stndrds/schema 0.1.0-alpha.15 → 0.1.0-alpha.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/chunk-4D23X5XN.mjs +7257 -0
  2. package/dist/chunk-6RPWHMTH.mjs +7101 -0
  3. package/dist/chunk-BTVPMGYW.mjs +6327 -0
  4. package/dist/chunk-C7HZ55GJ.mjs +6882 -0
  5. package/dist/chunk-CMO3JEJL.mjs +7259 -0
  6. package/dist/chunk-JDUTFH3J.mjs +6940 -0
  7. package/dist/chunk-JWRGX3UW.mjs +7265 -0
  8. package/dist/chunk-OUSLILLE.mjs +7259 -0
  9. package/dist/chunk-PMJZPJDP.mjs +7265 -0
  10. package/dist/chunk-QAGN53HE.mjs +6327 -0
  11. package/dist/chunk-UUOAVXAI.mjs +6327 -0
  12. package/dist/chunk-VYZ3BOSB.mjs +6946 -0
  13. package/dist/chunk-XAX75NSP.mjs +6321 -0
  14. package/dist/chunk-YEV46K3M.mjs +7259 -0
  15. package/dist/chunk-ZG3PT3RL.mjs +6944 -0
  16. package/dist/index.d.mts +268 -4
  17. package/dist/index.d.ts +268 -4
  18. package/dist/index.js +1136 -168
  19. package/dist/index.mjs +35 -1
  20. package/dist/runtime--rp-TG-h.d.mts +5181 -0
  21. package/dist/runtime--rp-TG-h.d.ts +5181 -0
  22. package/dist/runtime-BPYJWZfC.d.mts +5130 -0
  23. package/dist/runtime-BPYJWZfC.d.ts +5130 -0
  24. package/dist/runtime-DRc_OhnM.d.mts +5142 -0
  25. package/dist/runtime-DRc_OhnM.d.ts +5142 -0
  26. package/dist/runtime-D_S3m9jf.d.mts +5142 -0
  27. package/dist/runtime-D_S3m9jf.d.ts +5142 -0
  28. package/dist/runtime-Db3rRxbJ.d.mts +4821 -0
  29. package/dist/runtime-Db3rRxbJ.d.ts +4821 -0
  30. package/dist/runtime-DlCiIIfr.d.mts +5130 -0
  31. package/dist/runtime-DlCiIIfr.d.ts +5130 -0
  32. package/dist/runtime-WqYxjhjJ.d.mts +5125 -0
  33. package/dist/runtime-WqYxjhjJ.d.ts +5125 -0
  34. package/dist/runtime.d.mts +1 -1
  35. package/dist/runtime.d.ts +1 -1
  36. package/dist/runtime.js +855 -222
  37. package/dist/runtime.mjs +5 -1
  38. package/package.json +2 -2
package/dist/runtime.js CHANGED
@@ -121,6 +121,7 @@ __export(runtime_exports, {
121
121
  AuditService: () => AuditService,
122
122
  DEFAULT_LABEL_FALLBACK: () => DEFAULT_LABEL_FALLBACK,
123
123
  FileService: () => FileService,
124
+ FlowService: () => FlowService,
124
125
  GeocodingService: () => GeocodingService,
125
126
  GlobalSearchService: () => GlobalSearchService,
126
127
  NoopGeocodingAdapter: () => NoopGeocodingAdapter,
@@ -133,6 +134,7 @@ __export(runtime_exports, {
133
134
  ViewService: () => ViewService,
134
135
  buildAuditChanges: () => buildAuditChanges,
135
136
  createMockAdapter: () => createMockAdapter,
137
+ enrichValuesWithSelectLabels: () => enrichValuesWithSelectLabels,
136
138
  extractAttributeNames: () => extractAttributeNames,
137
139
  getSyncPreview: () => getSyncPreview,
138
140
  getViewSyncPreview: () => getViewSyncPreview,
@@ -164,6 +166,168 @@ function generateId() {
164
166
  });
165
167
  }
166
168
 
169
+ // src/format.ts
170
+ var import_constants = require("@stndrds/constants");
171
+ var EMPTY_VALUE_PLACEHOLDER = "\u2014";
172
+ function formatText(value) {
173
+ return String(value);
174
+ }
175
+ function formatCheckbox(value) {
176
+ return value ? "Yes" : "No";
177
+ }
178
+ function formatNumber(value, attribute) {
179
+ if (typeof value !== "number") return String(value);
180
+ const decimals = attribute.decimals;
181
+ return value.toLocaleString(void 0, {
182
+ minimumFractionDigits: decimals,
183
+ maximumFractionDigits: decimals
184
+ });
185
+ }
186
+ function formatCurrency(value, _attribute) {
187
+ if (typeof value !== "object" || value === null) return String(value);
188
+ const currency = value;
189
+ if (!("value" in currency && "code" in currency)) return String(value);
190
+ const formattedValue = currency.value.toLocaleString(void 0, {
191
+ minimumFractionDigits: 2,
192
+ maximumFractionDigits: 2
193
+ });
194
+ return `${formattedValue} ${currency.code}`;
195
+ }
196
+ function formatDate(value) {
197
+ if (value instanceof Date) {
198
+ return value.toISOString().split("T")[0];
199
+ }
200
+ if (typeof value === "string") {
201
+ const date = new Date(value);
202
+ if (!Number.isNaN(date.getTime())) {
203
+ return date.toISOString().split("T")[0];
204
+ }
205
+ }
206
+ return String(value);
207
+ }
208
+ function formatTimestamp(value) {
209
+ if (typeof value === "number") {
210
+ return new Date(value).toISOString();
211
+ }
212
+ if (value instanceof Date) {
213
+ return value.toISOString();
214
+ }
215
+ return String(value);
216
+ }
217
+ function formatPhone(value) {
218
+ if (typeof value !== "object" || value === null) return String(value);
219
+ const phone = value;
220
+ if (!("phoneNumber" in phone)) return String(value);
221
+ if (phone.countryCode) {
222
+ const country = (0, import_constants.getCountryByIso3)(phone.countryCode);
223
+ const dial = country?.phoneCode ?? "";
224
+ return `${dial} ${phone.phoneNumber}`.trim();
225
+ }
226
+ return phone.phoneNumber;
227
+ }
228
+ function formatLocation(value, attribute) {
229
+ if (typeof value !== "object" || value === null) return String(value);
230
+ const loc = value;
231
+ const granularity = attribute.granularity ?? "full";
232
+ const parts = [];
233
+ switch (granularity) {
234
+ case "country":
235
+ if (loc.country) parts.push(loc.country);
236
+ break;
237
+ case "state":
238
+ if (loc.state) parts.push(loc.state);
239
+ if (loc.country) parts.push(loc.country);
240
+ break;
241
+ case "city":
242
+ if (loc.city) parts.push(loc.city);
243
+ if (loc.state) parts.push(loc.state);
244
+ if (loc.country) parts.push(loc.country);
245
+ break;
246
+ case "coordinates":
247
+ if (loc.latitude !== void 0 && loc.longitude !== void 0) {
248
+ parts.push(`${loc.latitude}, ${loc.longitude}`);
249
+ }
250
+ break;
251
+ case "address":
252
+ if (loc.address) parts.push(loc.address);
253
+ if (loc.city) parts.push(loc.city);
254
+ if (loc.state) parts.push(loc.state);
255
+ if (loc.country) parts.push(loc.country);
256
+ break;
257
+ default:
258
+ if (loc.address) parts.push(loc.address);
259
+ if (loc.city) parts.push(loc.city);
260
+ if (loc.state) parts.push(loc.state);
261
+ if (loc.postalCode) parts.push(loc.postalCode);
262
+ if (loc.country) parts.push(loc.country);
263
+ break;
264
+ }
265
+ return parts.join(", ") || EMPTY_VALUE_PLACEHOLDER;
266
+ }
267
+ function formatSelect(value, attribute) {
268
+ if (typeof value !== "string") return String(value);
269
+ const option = attribute.options?.find((o) => o.value === value);
270
+ return option?.label ?? String(value);
271
+ }
272
+ function formatMultiselect(value, attribute) {
273
+ if (!Array.isArray(value)) return String(value);
274
+ if (attribute.options) {
275
+ const labels = value.map((v) => attribute.options.find((o) => o.value === v)?.label).filter(Boolean);
276
+ return labels.join(", ");
277
+ }
278
+ return value.join(", ");
279
+ }
280
+ function formatRating(value, attribute) {
281
+ if (typeof value !== "number") return String(value);
282
+ const max = attribute.max ?? 5;
283
+ return `${value}/${max}`;
284
+ }
285
+ function formatAttributeValue(value, attribute) {
286
+ if (value === null || value === void 0 || value === "") {
287
+ return EMPTY_VALUE_PLACEHOLDER;
288
+ }
289
+ switch (attribute.type) {
290
+ case "text":
291
+ case "textarea":
292
+ return formatText(value);
293
+ case "checkbox":
294
+ return formatCheckbox(value);
295
+ case "number":
296
+ return formatNumber(value, attribute);
297
+ case "currency":
298
+ return formatCurrency(value, attribute);
299
+ case "date":
300
+ return formatDate(value);
301
+ case "timestamp":
302
+ return formatTimestamp(value);
303
+ case "phone":
304
+ return formatPhone(value);
305
+ case "location":
306
+ return formatLocation(value, attribute);
307
+ case "select":
308
+ case "status":
309
+ return formatSelect(value, attribute);
310
+ case "multiselect":
311
+ return formatMultiselect(value, attribute);
312
+ case "rating":
313
+ return formatRating(value, attribute);
314
+ // Unsupported types - return value as-is or placeholder
315
+ case "file":
316
+ case "user":
317
+ case "relation":
318
+ if (Array.isArray(value)) {
319
+ return value.join(", ");
320
+ }
321
+ return String(value);
322
+ default: {
323
+ if (Array.isArray(value)) {
324
+ return value.join(", ");
325
+ }
326
+ return String(value);
327
+ }
328
+ }
329
+ }
330
+
167
331
  // src/runtime/template.ts
168
332
  var pipes = {
169
333
  /** Convert to uppercase */
@@ -215,6 +379,24 @@ function extractAttributeNames(template) {
215
379
  }
216
380
  return names;
217
381
  }
382
+ function hasOptions(attr) {
383
+ return "options" in attr && Array.isArray(attr.options) && attr.options.length > 0;
384
+ }
385
+ function enrichValuesWithSelectLabels(values, attributes) {
386
+ const enriched = { ...values };
387
+ for (const attr of attributes) {
388
+ const value = values[attr.name];
389
+ if (value == null) continue;
390
+ const isSelectLike = attr.type === "select" || attr.type === "status" || attr.type === "multiselect";
391
+ if (!(isSelectLike && hasOptions(attr))) continue;
392
+ if (attr.type === "multiselect" && Array.isArray(value) && value.length === 0) continue;
393
+ const formatted = formatAttributeValue(value, attr);
394
+ if (formatted && formatted !== EMPTY_VALUE_PLACEHOLDER) {
395
+ enriched[attr.name] = formatted;
396
+ }
397
+ }
398
+ return enriched;
399
+ }
218
400
 
219
401
  // src/runtime/mock-adapter.ts
220
402
  function createMockObjectsRepository(stores) {
@@ -691,14 +873,31 @@ function createMockObjectRecordsRepository(stores) {
691
873
  (options.offset ?? 0) + options.limit
692
874
  );
693
875
  }
876
+ const attributesByObjectId = /* @__PURE__ */ new Map();
877
+ for (const attr of stores.attributes.values()) {
878
+ if (!attributesByObjectId.has(attr.objectId)) {
879
+ attributesByObjectId.set(attr.objectId, []);
880
+ }
881
+ attributesByObjectId.get(attr.objectId)?.push(attr);
882
+ }
694
883
  const results = matchingRecords.map((r) => {
695
884
  const obj = objectsMap.get(r.objectId);
696
885
  const labelExpression = obj?.labelExpression ?? "{{ name }}";
886
+ const dbAttrs = attributesByObjectId.get(r.objectId) ?? [];
887
+ const attrs = dbAttrs.map((a) => ({
888
+ ...a.config,
889
+ id: a.id,
890
+ name: a.name,
891
+ type: a.type,
892
+ label: a.config.label ?? a.name,
893
+ required: a.config.required ?? false
894
+ }));
895
+ const enrichedValues = enrichValuesWithSelectLabels(r.values, attrs);
697
896
  return {
698
897
  objectId: r.objectId,
699
898
  objectName: obj?.name ?? "unknown",
700
899
  objectLabel: obj?.label ?? "Unknown",
701
- label: renderLabelExpression(labelExpression, r.values),
900
+ label: renderLabelExpression(labelExpression, enrichedValues),
702
901
  recordId: r.id,
703
902
  values: r.values,
704
903
  completionStatus: r.completionStatus,
@@ -1115,6 +1314,10 @@ var AuditService = class {
1115
1314
  this.options = options;
1116
1315
  this.buffer = [];
1117
1316
  this.flushTimer = null;
1317
+ /** Prevents concurrent flush operations */
1318
+ this.isFlushing = false;
1319
+ /** Pending flush promise to allow waiting on concurrent flush */
1320
+ this.flushPromise = null;
1118
1321
  if (options?.async && options.flushIntervalMs) {
1119
1322
  this.startFlushTimer();
1120
1323
  }
@@ -1267,14 +1470,23 @@ var AuditService = class {
1267
1470
  // ============================================================================
1268
1471
  /**
1269
1472
  * Flush buffered logs to the database
1473
+ * Protected against concurrent flush calls
1270
1474
  */
1271
1475
  async flush() {
1476
+ if (this.isFlushing && this.flushPromise) {
1477
+ return this.flushPromise;
1478
+ }
1272
1479
  if (this.buffer.length === 0 || !this.adapter.audit) {
1273
1480
  return;
1274
1481
  }
1482
+ this.isFlushing = true;
1275
1483
  const entries = [...this.buffer];
1276
1484
  this.buffer = [];
1277
- await this.adapter.audit.createMany(entries);
1485
+ this.flushPromise = this.adapter.audit.createMany(entries).finally(() => {
1486
+ this.isFlushing = false;
1487
+ this.flushPromise = null;
1488
+ });
1489
+ return this.flushPromise;
1278
1490
  }
1279
1491
  /**
1280
1492
  * Clean up resources (stop timer, flush remaining logs)
@@ -1296,6 +1508,12 @@ var AuditService = class {
1296
1508
  if (!this.adapter.audit) {
1297
1509
  return;
1298
1510
  }
1511
+ if (entry.actorId && !entry.actorEmail && entry.actorType === "user") {
1512
+ const profile = await this.adapter.userProfiles.findById(entry.actorId);
1513
+ if (profile) {
1514
+ entry.actorEmail = profile.email;
1515
+ }
1516
+ }
1299
1517
  if (this.options?.async) {
1300
1518
  this.buffer.push(entry);
1301
1519
  const batchSize = this.options.batchSize ?? 10;
@@ -1533,6 +1751,432 @@ var FileService = class {
1533
1751
  }
1534
1752
  };
1535
1753
 
1754
+ // src/exceptions.ts
1755
+ var SchemaErrorCode = {
1756
+ // Generic
1757
+ UNKNOWN: "SCHEMA_UNKNOWN_ERROR",
1758
+ // Not Found
1759
+ OBJECT_NOT_FOUND: "SCHEMA_OBJECT_NOT_FOUND",
1760
+ ATTRIBUTE_NOT_FOUND: "SCHEMA_ATTRIBUTE_NOT_FOUND",
1761
+ RECORD_NOT_FOUND: "SCHEMA_RECORD_NOT_FOUND",
1762
+ USER_PROFILE_NOT_FOUND: "SCHEMA_USER_PROFILE_NOT_FOUND",
1763
+ FILE_NOT_FOUND: "SCHEMA_FILE_NOT_FOUND",
1764
+ ROLE_NOT_FOUND: "SCHEMA_ROLE_NOT_FOUND",
1765
+ // Validation
1766
+ VALIDATION_FAILED: "SCHEMA_VALIDATION_FAILED",
1767
+ INVALID_ATTRIBUTE_NAME: "SCHEMA_INVALID_ATTRIBUTE_NAME",
1768
+ INVALID_OBJECT_NAME: "SCHEMA_INVALID_OBJECT_NAME",
1769
+ // Protected Resources
1770
+ PROTECTED_OBJECT: "SCHEMA_PROTECTED_OBJECT",
1771
+ PROTECTED_ATTRIBUTE: "SCHEMA_PROTECTED_ATTRIBUTE",
1772
+ PROTECTED_ROLE: "SCHEMA_PROTECTED_ROLE",
1773
+ // Permissions
1774
+ FORBIDDEN: "SCHEMA_FORBIDDEN",
1775
+ // Sync
1776
+ SYNC_FAILED: "SCHEMA_SYNC_FAILED",
1777
+ NOT_SYSTEM_OBJECT: "SCHEMA_NOT_SYSTEM_OBJECT",
1778
+ // Duplicates
1779
+ DUPLICATE_OBJECT: "SCHEMA_DUPLICATE_OBJECT",
1780
+ DUPLICATE_ATTRIBUTE: "SCHEMA_DUPLICATE_ATTRIBUTE"
1781
+ };
1782
+ var SchemaError = class extends Error {
1783
+ constructor(message, code = SchemaErrorCode.UNKNOWN, details) {
1784
+ super(message);
1785
+ this.name = "SchemaError";
1786
+ this.code = code;
1787
+ this.details = details;
1788
+ Object.setPrototypeOf(this, new.target.prototype);
1789
+ }
1790
+ toJSON() {
1791
+ return {
1792
+ name: this.name,
1793
+ code: this.code,
1794
+ message: this.message,
1795
+ details: this.details
1796
+ };
1797
+ }
1798
+ };
1799
+ var NotFoundError = class extends SchemaError {
1800
+ constructor(resourceType, resourceId, code = SchemaErrorCode.RECORD_NOT_FOUND) {
1801
+ super(`${resourceType} with id "${resourceId}" not found`, code, {
1802
+ resourceType,
1803
+ resourceId
1804
+ });
1805
+ this.name = "NotFoundError";
1806
+ this.resourceType = resourceType;
1807
+ this.resourceId = resourceId;
1808
+ }
1809
+ };
1810
+ var RecordNotFoundError = class extends NotFoundError {
1811
+ constructor(recordId) {
1812
+ super("Record", recordId, SchemaErrorCode.RECORD_NOT_FOUND);
1813
+ this.name = "RecordNotFoundError";
1814
+ }
1815
+ };
1816
+ var ValidationError = class _ValidationError extends SchemaError {
1817
+ constructor(message, errors) {
1818
+ super(message, SchemaErrorCode.VALIDATION_FAILED, { errors });
1819
+ this.name = "ValidationError";
1820
+ this.errors = errors;
1821
+ }
1822
+ /**
1823
+ * Create a validation error from Zod-style errors
1824
+ */
1825
+ static fromZodErrors(errors) {
1826
+ const details = errors.map((err) => ({
1827
+ path: err.path.map(String),
1828
+ message: err.message
1829
+ }));
1830
+ const message = `Validation failed: ${details.map((d) => `${d.path.join(".")}: ${d.message}`).join(", ")}`;
1831
+ return new _ValidationError(message, details);
1832
+ }
1833
+ };
1834
+ var ProtectedResourceError = class extends SchemaError {
1835
+ constructor(resourceType, resourceName, operation) {
1836
+ const code = resourceType === "object" ? SchemaErrorCode.PROTECTED_OBJECT : SchemaErrorCode.PROTECTED_ATTRIBUTE;
1837
+ super(`Cannot ${operation} system ${resourceType} "${resourceName}"`, code, {
1838
+ resourceType,
1839
+ resourceName,
1840
+ operation
1841
+ });
1842
+ this.name = "ProtectedResourceError";
1843
+ this.resourceType = resourceType;
1844
+ this.resourceName = resourceName;
1845
+ this.operation = operation;
1846
+ }
1847
+ };
1848
+ var ForbiddenError = class extends SchemaError {
1849
+ constructor(objectName, action, userId) {
1850
+ super(`No ${action} permission on object "${objectName}"`, SchemaErrorCode.FORBIDDEN, {
1851
+ objectName,
1852
+ action,
1853
+ userId
1854
+ });
1855
+ this.name = "ForbiddenError";
1856
+ this.objectName = objectName;
1857
+ this.action = action;
1858
+ this.userId = userId;
1859
+ }
1860
+ };
1861
+
1862
+ // src/runtime/services/flow.service.ts
1863
+ var FlowService = class {
1864
+ constructor(adapter, systemFlows = []) {
1865
+ this.adapter = adapter;
1866
+ this.systemFlows = new Map(systemFlows.map((f) => [f.name, f]));
1867
+ }
1868
+ /**
1869
+ * Get all flows for a tenant (system + custom)
1870
+ */
1871
+ async getAllFlows(tenantId) {
1872
+ const systemFlowsList = Array.from(this.systemFlows.values());
1873
+ if (!this.adapter.flows) {
1874
+ return systemFlowsList;
1875
+ }
1876
+ const dbFlows = await this.adapter.flows.findAllForTenant(tenantId);
1877
+ const customFlows = dbFlows.filter((f) => !f.system).map(this.convertDBFlowToDefinition);
1878
+ return [...systemFlowsList, ...customFlows];
1879
+ }
1880
+ /**
1881
+ * Get published flows only
1882
+ */
1883
+ async getPublishedFlows(tenantId) {
1884
+ const allFlows = await this.getAllFlows(tenantId);
1885
+ return allFlows.filter((f) => f.status === "published");
1886
+ }
1887
+ /**
1888
+ * Get a specific flow by name
1889
+ */
1890
+ async getFlow(name, tenantId) {
1891
+ const systemFlow = this.systemFlows.get(name);
1892
+ if (systemFlow) {
1893
+ return systemFlow;
1894
+ }
1895
+ if (!this.adapter.flows) {
1896
+ return null;
1897
+ }
1898
+ const dbFlow = await this.adapter.flows.findByName(tenantId, name);
1899
+ if (dbFlow) {
1900
+ return this.convertDBFlowToDefinition(dbFlow);
1901
+ }
1902
+ return null;
1903
+ }
1904
+ /**
1905
+ * Get a flow by ID
1906
+ */
1907
+ async getFlowById(flowId) {
1908
+ if (!this.adapter.flows) {
1909
+ return null;
1910
+ }
1911
+ const dbFlow = await this.adapter.flows.findById(flowId);
1912
+ if (dbFlow) {
1913
+ return this.convertDBFlowToDefinition(dbFlow);
1914
+ }
1915
+ return null;
1916
+ }
1917
+ /**
1918
+ * Create a new custom flow (as draft)
1919
+ */
1920
+ async createFlow(input, tenantId) {
1921
+ if (!this.adapter.flows) {
1922
+ throw new SchemaError(
1923
+ "Flows feature is not enabled. Database adapter does not support flows.",
1924
+ SchemaErrorCode.UNKNOWN,
1925
+ { feature: "flows" }
1926
+ );
1927
+ }
1928
+ this.validateFlowName(input.name);
1929
+ const existing = await this.adapter.flows.findByName(tenantId, input.name);
1930
+ if (existing) {
1931
+ throw new SchemaError(
1932
+ `Flow "${input.name}" already exists`,
1933
+ SchemaErrorCode.DUPLICATE_OBJECT,
1934
+ { flowName: input.name }
1935
+ );
1936
+ }
1937
+ if (this.systemFlows.has(input.name)) {
1938
+ throw new SchemaError(
1939
+ `Cannot create flow "${input.name}": a system flow with this name already exists`,
1940
+ SchemaErrorCode.DUPLICATE_OBJECT,
1941
+ { flowName: input.name, system: true }
1942
+ );
1943
+ }
1944
+ this.validateFlowStructure(input);
1945
+ const dbFlow = await this.adapter.flows.create({
1946
+ tenantId,
1947
+ name: input.name,
1948
+ label: input.label,
1949
+ description: input.description,
1950
+ icon: input.icon,
1951
+ status: "draft",
1952
+ version: 1,
1953
+ slots: input.slots,
1954
+ pages: input.pages,
1955
+ relations: input.relations,
1956
+ system: false,
1957
+ metadata: input.metadata
1958
+ });
1959
+ return this.convertDBFlowToDefinition(dbFlow);
1960
+ }
1961
+ /**
1962
+ * Update a custom flow
1963
+ */
1964
+ async updateFlow(flowId, input) {
1965
+ if (!this.adapter.flows) {
1966
+ throw new SchemaError("Flows feature is not enabled.", SchemaErrorCode.UNKNOWN, {
1967
+ feature: "flows"
1968
+ });
1969
+ }
1970
+ const dbFlow = await this.adapter.flows.findById(flowId);
1971
+ if (!dbFlow) {
1972
+ throw new NotFoundError("Flow", flowId);
1973
+ }
1974
+ if (dbFlow.system) {
1975
+ throw new ProtectedResourceError("object", dbFlow.name, "modify");
1976
+ }
1977
+ if (input.slots || input.pages || input.relations) {
1978
+ this.validateFlowStructure({
1979
+ name: dbFlow.name,
1980
+ label: input.label ?? dbFlow.label,
1981
+ slots: input.slots ?? dbFlow.slots,
1982
+ pages: input.pages ?? dbFlow.pages,
1983
+ relations: input.relations ?? dbFlow.relations
1984
+ });
1985
+ }
1986
+ const updated = await this.adapter.flows.update(flowId, {
1987
+ label: input.label,
1988
+ description: input.description,
1989
+ icon: input.icon,
1990
+ slots: input.slots,
1991
+ pages: input.pages,
1992
+ relations: input.relations,
1993
+ metadata: input.metadata
1994
+ });
1995
+ return this.convertDBFlowToDefinition(updated);
1996
+ }
1997
+ /**
1998
+ * Publish a flow
1999
+ */
2000
+ async publishFlow(flowId) {
2001
+ if (!this.adapter.flows) {
2002
+ throw new SchemaError("Flows feature is not enabled.", SchemaErrorCode.UNKNOWN, {
2003
+ feature: "flows"
2004
+ });
2005
+ }
2006
+ const dbFlow = await this.adapter.flows.findById(flowId);
2007
+ if (!dbFlow) {
2008
+ throw new NotFoundError("Flow", flowId);
2009
+ }
2010
+ if (dbFlow.system) {
2011
+ throw new ProtectedResourceError("object", dbFlow.name, "modify");
2012
+ }
2013
+ if (dbFlow.status === "published") {
2014
+ return this.convertDBFlowToDefinition(dbFlow);
2015
+ }
2016
+ this.validateFlowStructure({
2017
+ name: dbFlow.name,
2018
+ label: dbFlow.label,
2019
+ slots: dbFlow.slots,
2020
+ pages: dbFlow.pages,
2021
+ relations: dbFlow.relations
2022
+ });
2023
+ const updated = await this.adapter.flows.update(flowId, {
2024
+ status: "published",
2025
+ version: dbFlow.version + 1
2026
+ });
2027
+ return this.convertDBFlowToDefinition(updated);
2028
+ }
2029
+ /**
2030
+ * Archive a flow
2031
+ */
2032
+ async archiveFlow(flowId) {
2033
+ if (!this.adapter.flows) {
2034
+ throw new SchemaError("Flows feature is not enabled.", SchemaErrorCode.UNKNOWN, {
2035
+ feature: "flows"
2036
+ });
2037
+ }
2038
+ const dbFlow = await this.adapter.flows.findById(flowId);
2039
+ if (!dbFlow) {
2040
+ throw new NotFoundError("Flow", flowId);
2041
+ }
2042
+ if (dbFlow.system) {
2043
+ throw new ProtectedResourceError("object", dbFlow.name, "modify");
2044
+ }
2045
+ const updated = await this.adapter.flows.update(flowId, {
2046
+ status: "archived"
2047
+ });
2048
+ return this.convertDBFlowToDefinition(updated);
2049
+ }
2050
+ /**
2051
+ * Delete a custom flow
2052
+ */
2053
+ async deleteFlow(flowId) {
2054
+ if (!this.adapter.flows) {
2055
+ throw new SchemaError("Flows feature is not enabled.", SchemaErrorCode.UNKNOWN, {
2056
+ feature: "flows"
2057
+ });
2058
+ }
2059
+ const dbFlow = await this.adapter.flows.findById(flowId);
2060
+ if (!dbFlow) {
2061
+ throw new NotFoundError("Flow", flowId);
2062
+ }
2063
+ if (dbFlow.system) {
2064
+ throw new ProtectedResourceError("object", dbFlow.name, "delete");
2065
+ }
2066
+ await this.adapter.flows.delete(flowId);
2067
+ }
2068
+ // ============================================================================
2069
+ // PRIVATE HELPERS
2070
+ // ============================================================================
2071
+ /**
2072
+ * Validate flow name format (kebab-case)
2073
+ */
2074
+ validateFlowName(name) {
2075
+ if (!name || name.length === 0) {
2076
+ throw new ValidationError("Flow name cannot be empty", [
2077
+ { path: ["name"], message: "Flow name cannot be empty" }
2078
+ ]);
2079
+ }
2080
+ if (name.length > 63) {
2081
+ throw new ValidationError("Flow name is too long", [
2082
+ { path: ["name"], message: "Flow name is too long (max 63 characters)" }
2083
+ ]);
2084
+ }
2085
+ const kebabCaseRegex = /^[a-z][a-z0-9-]*$/;
2086
+ if (!kebabCaseRegex.test(name)) {
2087
+ throw new ValidationError("Invalid flow name format", [
2088
+ {
2089
+ path: ["name"],
2090
+ message: "Flow name must be in kebab-case (e.g., 'couple-creation', 'new-contact')"
2091
+ }
2092
+ ]);
2093
+ }
2094
+ }
2095
+ /**
2096
+ * Validate flow structure (slots, pages, relations)
2097
+ */
2098
+ validateFlowStructure(input) {
2099
+ if (!input.slots || input.slots.length === 0) {
2100
+ throw new ValidationError("Flow must have at least one slot", [
2101
+ { path: ["slots"], message: "Flow must have at least one slot" }
2102
+ ]);
2103
+ }
2104
+ if (!input.pages || input.pages.length === 0) {
2105
+ throw new ValidationError("Flow must have at least one page", [
2106
+ { path: ["pages"], message: "Flow must have at least one page" }
2107
+ ]);
2108
+ }
2109
+ const slotIds = new Set(input.slots.map((s) => s.id));
2110
+ if (slotIds.size !== input.slots.length) {
2111
+ throw new ValidationError("Duplicate slot IDs detected", [
2112
+ { path: ["slots"], message: "Duplicate slot IDs detected" }
2113
+ ]);
2114
+ }
2115
+ for (const page of input.pages) {
2116
+ for (const row of page.rows) {
2117
+ for (const field of row.fields) {
2118
+ if (!slotIds.has(field.slotId)) {
2119
+ throw new ValidationError("Field references unknown slot", [
2120
+ {
2121
+ path: ["pages", page.id, "rows", row.id, "fields", field.id],
2122
+ message: `Field "${field.id}" references unknown slot "${field.slotId}"`
2123
+ }
2124
+ ]);
2125
+ }
2126
+ }
2127
+ }
2128
+ }
2129
+ for (const relation of input.relations) {
2130
+ if (!slotIds.has(relation.sourceSlotId)) {
2131
+ throw new ValidationError("Relation references unknown source slot", [
2132
+ {
2133
+ path: ["relations", relation.id, "sourceSlotId"],
2134
+ message: `Relation references unknown source slot "${relation.sourceSlotId}"`
2135
+ }
2136
+ ]);
2137
+ }
2138
+ if (!slotIds.has(relation.targetSlotId)) {
2139
+ throw new ValidationError("Relation references unknown target slot", [
2140
+ {
2141
+ path: ["relations", relation.id, "targetSlotId"],
2142
+ message: `Relation references unknown target slot "${relation.targetSlotId}"`
2143
+ }
2144
+ ]);
2145
+ }
2146
+ if (relation.sourceSlotId === relation.targetSlotId) {
2147
+ throw new ValidationError("Relation cannot link a slot to itself", [
2148
+ {
2149
+ path: ["relations", relation.id],
2150
+ message: `Relation cannot link a slot to itself: "${relation.sourceSlotId}"`
2151
+ }
2152
+ ]);
2153
+ }
2154
+ }
2155
+ }
2156
+ /**
2157
+ * Convert database flow to FlowDefinition
2158
+ */
2159
+ convertDBFlowToDefinition(dbFlow) {
2160
+ return {
2161
+ id: dbFlow.id,
2162
+ name: dbFlow.name,
2163
+ label: dbFlow.label,
2164
+ description: dbFlow.description,
2165
+ icon: dbFlow.icon,
2166
+ status: dbFlow.status,
2167
+ version: dbFlow.version,
2168
+ slots: dbFlow.slots,
2169
+ pages: dbFlow.pages,
2170
+ relations: dbFlow.relations,
2171
+ system: dbFlow.system,
2172
+ tenantId: dbFlow.tenantId,
2173
+ metadata: dbFlow.metadata,
2174
+ createdAt: dbFlow.createdAt,
2175
+ updatedAt: dbFlow.updatedAt
2176
+ };
2177
+ }
2178
+ };
2179
+
1536
2180
  // src/runtime/services/geocoding.service.ts
1537
2181
  var GeocodingService = class {
1538
2182
  constructor(adapter) {
@@ -1649,11 +2293,14 @@ var GlobalSearchService = class {
1649
2293
  };
1650
2294
 
1651
2295
  // src/builders/attribute-validators.ts
1652
- var import_constants = require("@stndrds/constants");
1653
- var VALID_ICONS = new Set(import_constants.ICONS);
2296
+ var import_constants2 = require("@stndrds/constants");
2297
+ var VALID_ICONS = new Set(import_constants2.ICONS);
2298
+
2299
+ // src/builders/flow-builder.ts
2300
+ var import_zod = require("zod");
1654
2301
 
1655
2302
  // src/builders/object-builder.ts
1656
- var import_zod = __toESM(require("zod"));
2303
+ var import_zod2 = __toESM(require("zod"));
1657
2304
  var ObjectBuilder = class {
1658
2305
  constructor(config) {
1659
2306
  this.validateName(config.name);
@@ -1786,14 +2433,14 @@ The labelExpression defines how records are displayed in lists and relations.
1786
2433
  if (name === void 0) {
1787
2434
  return;
1788
2435
  }
1789
- const objectNameSchema = import_zod.default.string().min(1, "Object name cannot be empty").max(63, "Object name is too long (max 63 characters)").regex(/^[a-z][a-z0-9-]*$/, {
2436
+ const objectNameSchema = import_zod2.default.string().min(1, "Object name cannot be empty").max(63, "Object name is too long (max 63 characters)").regex(/^[a-z][a-z0-9-]*$/, {
1790
2437
  message: "Invalid object name format.\nName must be in kebab-case (slug format):\n \u2705 Valid: 'products', 'user-profiles', 'order-items'\n \u274C Invalid: 'Products', 'userProfiles', 'user_profiles'"
1791
2438
  });
1792
2439
  try {
1793
2440
  objectNameSchema.parse(name);
1794
2441
  } catch (error) {
1795
- if (error instanceof import_zod.default.ZodError) {
1796
- throw new Error(`[ObjectBuilder] ${error.errors[0].message}`);
2442
+ if (error instanceof import_zod2.default.ZodError) {
2443
+ throw new Error(`[ObjectBuilder] ${error.issues[0].message}`);
1797
2444
  }
1798
2445
  throw error;
1799
2446
  }
@@ -1804,7 +2451,7 @@ function object(config) {
1804
2451
  }
1805
2452
 
1806
2453
  // src/builders/view-builder.ts
1807
- var import_zod2 = require("zod");
2454
+ var import_zod3 = require("zod");
1808
2455
 
1809
2456
  // src/types/errors.ts
1810
2457
  var RecordReferencedError = class extends Error {
@@ -1839,35 +2486,42 @@ var ObjectReferencedError = class extends Error {
1839
2486
  };
1840
2487
 
1841
2488
  // src/validation/validators.ts
1842
- var import_zod3 = require("zod");
1843
- var baseConfigSchema = import_zod3.z.object({
1844
- disabled: import_zod3.z.boolean().optional(),
1845
- placeholder: import_zod3.z.string().optional(),
1846
- description: import_zod3.z.string().optional(),
1847
- defaultValue: import_zod3.z.unknown().optional(),
1848
- icon: import_zod3.z.string().optional(),
1849
- order: import_zod3.z.number().int().optional(),
1850
- hidden: import_zod3.z.boolean().optional(),
1851
- archived: import_zod3.z.boolean().optional(),
1852
- deprecated: import_zod3.z.boolean().optional(),
1853
- metadata: import_zod3.z.record(import_zod3.z.unknown()).optional()
2489
+ var import_zod4 = require("zod");
2490
+ var baseConfigSchema = import_zod4.z.object({
2491
+ disabled: import_zod4.z.boolean().optional(),
2492
+ placeholder: import_zod4.z.string().optional(),
2493
+ description: import_zod4.z.string().optional(),
2494
+ defaultValue: import_zod4.z.unknown().optional(),
2495
+ icon: import_zod4.z.string().optional(),
2496
+ order: import_zod4.z.number().int().optional(),
2497
+ hidden: import_zod4.z.boolean().optional(),
2498
+ archived: import_zod4.z.boolean().optional(),
2499
+ deprecated: import_zod4.z.boolean().optional(),
2500
+ metadata: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()).optional()
1854
2501
  });
1855
- var optionSchema = import_zod3.z.object({
1856
- id: import_zod3.z.string().min(1),
1857
- label: import_zod3.z.string().min(1),
1858
- value: import_zod3.z.string().min(1),
1859
- color: import_zod3.z.string().optional(),
1860
- icon: import_zod3.z.string().optional(),
1861
- description: import_zod3.z.string().optional(),
1862
- group: import_zod3.z.enum(["idle", "in_progress", "finished"]).optional()
2502
+ var optionSchema = import_zod4.z.object({
2503
+ id: import_zod4.z.string().min(1),
2504
+ label: import_zod4.z.string().min(1),
2505
+ value: import_zod4.z.string().min(1),
2506
+ color: import_zod4.z.string().optional(),
2507
+ icon: import_zod4.z.string().optional(),
2508
+ description: import_zod4.z.string().optional(),
2509
+ group: import_zod4.z.enum(["idle", "in_progress", "finished"]).optional()
1863
2510
  });
1864
- var relationTargetSchema = import_zod3.z.object({
1865
- object: import_zod3.z.string().min(1),
1866
- displayTemplate: import_zod3.z.string().optional(),
1867
- filter: import_zod3.z.record(import_zod3.z.unknown()).optional()
2511
+ var optionsArraySchema = import_zod4.z.array(optionSchema).min(1).refine(
2512
+ (options) => {
2513
+ const values = options.map((o) => o.value);
2514
+ return new Set(values).size === values.length;
2515
+ },
2516
+ { message: "Duplicate option values are not allowed" }
2517
+ );
2518
+ var relationTargetSchema = import_zod4.z.object({
2519
+ object: import_zod4.z.string().min(1),
2520
+ displayTemplate: import_zod4.z.string().optional(),
2521
+ filter: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()).optional()
1868
2522
  });
1869
- var documentTypeConfigSchema = import_zod3.z.object({
1870
- type: import_zod3.z.enum([
2523
+ var documentTypeConfigSchema = import_zod4.z.object({
2524
+ type: import_zod4.z.enum([
1871
2525
  "id_card",
1872
2526
  "passport",
1873
2527
  "incorporation_certificate",
@@ -1878,86 +2532,86 @@ var documentTypeConfigSchema = import_zod3.z.object({
1878
2532
  "proof_of_address",
1879
2533
  "custom"
1880
2534
  ]),
1881
- label: import_zod3.z.string(),
1882
- faces: import_zod3.z.array(import_zod3.z.enum(["front", "back", "single"])),
1883
- attributeMapping: import_zod3.z.array(
1884
- import_zod3.z.object({
1885
- attributeId: import_zod3.z.string(),
1886
- extractedKey: import_zod3.z.string(),
1887
- face: import_zod3.z.enum(["front", "back", "single"]).optional(),
1888
- required: import_zod3.z.boolean().optional()
2535
+ label: import_zod4.z.string(),
2536
+ faces: import_zod4.z.array(import_zod4.z.enum(["front", "back", "single"])),
2537
+ attributeMapping: import_zod4.z.array(
2538
+ import_zod4.z.object({
2539
+ attributeId: import_zod4.z.string(),
2540
+ extractedKey: import_zod4.z.string(),
2541
+ face: import_zod4.z.enum(["front", "back", "single"]).optional(),
2542
+ required: import_zod4.z.boolean().optional()
1889
2543
  })
1890
2544
  ).optional()
1891
2545
  });
1892
- var fileVerificationConfigSchema = import_zod3.z.object({
1893
- enabled: import_zod3.z.boolean(),
1894
- documentTypes: import_zod3.z.array(documentTypeConfigSchema),
1895
- autoExtract: import_zod3.z.boolean().optional(),
1896
- autoValidate: import_zod3.z.boolean().optional()
2546
+ var fileVerificationConfigSchema = import_zod4.z.object({
2547
+ enabled: import_zod4.z.boolean(),
2548
+ documentTypes: import_zod4.z.array(documentTypeConfigSchema),
2549
+ autoExtract: import_zod4.z.boolean().optional(),
2550
+ autoValidate: import_zod4.z.boolean().optional()
1897
2551
  });
1898
2552
  var textConfigSchema = baseConfigSchema.extend({
1899
- minLength: import_zod3.z.number().int().min(0).optional(),
1900
- maxLength: import_zod3.z.number().int().min(1).optional(),
1901
- pattern: import_zod3.z.string().optional()
2553
+ minLength: import_zod4.z.number().int().min(0).optional(),
2554
+ maxLength: import_zod4.z.number().int().min(1).optional(),
2555
+ pattern: import_zod4.z.string().optional()
1902
2556
  });
1903
2557
  var textareaConfigSchema = baseConfigSchema;
1904
2558
  var numberConfigSchema = baseConfigSchema.extend({
1905
- min: import_zod3.z.number().optional(),
1906
- max: import_zod3.z.number().optional(),
1907
- unit: import_zod3.z.enum(["integer", "decimal", "percentage"]).optional(),
1908
- decimals: import_zod3.z.number().int().min(0).optional()
2559
+ min: import_zod4.z.number().optional(),
2560
+ max: import_zod4.z.number().optional(),
2561
+ unit: import_zod4.z.enum(["integer", "decimal", "percentage"]).optional(),
2562
+ decimals: import_zod4.z.number().int().min(0).optional()
1909
2563
  });
1910
2564
  var checkboxConfigSchema = baseConfigSchema;
1911
2565
  var dateConfigSchema = baseConfigSchema.extend({
1912
- dateFormat: import_zod3.z.enum(["short", "long", "full", "relative"]).optional(),
1913
- minDate: import_zod3.z.string().optional(),
1914
- maxDate: import_zod3.z.string().optional()
2566
+ dateFormat: import_zod4.z.enum(["short", "long", "full", "relative"]).optional(),
2567
+ minDate: import_zod4.z.string().optional(),
2568
+ maxDate: import_zod4.z.string().optional()
1915
2569
  });
1916
2570
  var phoneConfigSchema = baseConfigSchema.extend({
1917
- defaultCountryCode: import_zod3.z.string().length(3).optional()
2571
+ defaultCountryCode: import_zod4.z.string().length(3).optional()
1918
2572
  });
1919
2573
  var currencyConfigSchema = baseConfigSchema.extend({
1920
- defaultCurrency: import_zod3.z.string().length(3).optional(),
1921
- allowedCurrencies: import_zod3.z.array(import_zod3.z.string().length(3)).optional()
2574
+ defaultCurrency: import_zod4.z.string().length(3).optional(),
2575
+ allowedCurrencies: import_zod4.z.array(import_zod4.z.string().length(3)).optional()
1922
2576
  });
1923
2577
  var statusConfigSchema = baseConfigSchema.extend({
1924
- options: import_zod3.z.array(optionSchema).min(1)
2578
+ options: optionsArraySchema
1925
2579
  });
1926
2580
  var locationConfigSchema = baseConfigSchema.extend({
1927
- granularity: import_zod3.z.enum(["full", "address", "city", "state", "country", "coordinates"]),
1928
- enableAutocomplete: import_zod3.z.boolean().optional(),
1929
- enableMap: import_zod3.z.boolean().optional(),
1930
- defaultCountry: import_zod3.z.string().length(3).optional(),
1931
- allowedCountries: import_zod3.z.array(import_zod3.z.string().length(3)).optional(),
1932
- displayFormat: import_zod3.z.enum(["single_line", "multi_line", "compact"]).optional()
2581
+ granularity: import_zod4.z.enum(["full", "address", "city", "state", "country", "coordinates"]),
2582
+ enableAutocomplete: import_zod4.z.boolean().optional(),
2583
+ enableMap: import_zod4.z.boolean().optional(),
2584
+ defaultCountry: import_zod4.z.string().length(3).optional(),
2585
+ allowedCountries: import_zod4.z.array(import_zod4.z.string().length(3)).optional(),
2586
+ displayFormat: import_zod4.z.enum(["single_line", "multi_line", "compact"]).optional()
1933
2587
  });
1934
2588
  var timestampConfigSchema = baseConfigSchema.extend({
1935
- autoUpdate: import_zod3.z.boolean().optional()
2589
+ autoUpdate: import_zod4.z.boolean().optional()
1936
2590
  });
1937
2591
  var selectConfigSchema = baseConfigSchema.extend({
1938
- options: import_zod3.z.array(optionSchema).min(1)
2592
+ options: optionsArraySchema
1939
2593
  });
1940
2594
  var multiselectConfigSchema = baseConfigSchema.extend({
1941
- options: import_zod3.z.array(optionSchema).min(1)
2595
+ options: optionsArraySchema
1942
2596
  });
1943
2597
  var fileConfigSchema = baseConfigSchema.extend({
1944
- maxFiles: import_zod3.z.number().int().min(1).optional(),
1945
- maxSize: import_zod3.z.number().int().min(1).optional(),
1946
- allowedTypes: import_zod3.z.array(import_zod3.z.string()).optional(),
2598
+ maxFiles: import_zod4.z.number().int().min(1).optional(),
2599
+ maxSize: import_zod4.z.number().int().min(1).optional(),
2600
+ allowedTypes: import_zod4.z.array(import_zod4.z.string()).optional(),
1947
2601
  verification: fileVerificationConfigSchema.optional()
1948
2602
  });
1949
2603
  var userConfigSchema = baseConfigSchema.extend({
1950
- allowedRoles: import_zod3.z.array(import_zod3.z.string()).optional()
2604
+ allowedRoles: import_zod4.z.array(import_zod4.z.string()).optional()
1951
2605
  });
1952
2606
  var relationConfigSchema = baseConfigSchema.extend({
1953
- targets: import_zod3.z.array(relationTargetSchema).min(1),
1954
- cardinality: import_zod3.z.enum(["one", "many"]),
1955
- minItems: import_zod3.z.number().int().min(0).optional(),
1956
- maxItems: import_zod3.z.number().int().min(1).optional()
2607
+ targets: import_zod4.z.array(relationTargetSchema).min(1),
2608
+ cardinality: import_zod4.z.enum(["one", "many"]),
2609
+ minItems: import_zod4.z.number().int().min(0).optional(),
2610
+ maxItems: import_zod4.z.number().int().min(1).optional()
1957
2611
  });
1958
2612
  var ratingConfigSchema = baseConfigSchema.extend({
1959
- max: import_zod3.z.number().int().min(1).optional(),
1960
- iconType: import_zod3.z.enum(["star", "heart", "thumbs", "number"]).optional()
2613
+ max: import_zod4.z.number().int().min(1).optional(),
2614
+ iconType: import_zod4.z.enum(["star", "heart", "thumbs", "number"]).optional()
1961
2615
  });
1962
2616
  var attributeConfigSchemas = {
1963
2617
  text: textConfigSchema,
@@ -1985,7 +2639,7 @@ function parseAttributeConfig(type, config) {
1985
2639
  return schema.strip().parse(config);
1986
2640
  }
1987
2641
  function createTextValidator(attr) {
1988
- let schema = import_zod3.z.string();
2642
+ let schema = import_zod4.z.string();
1989
2643
  if (attr.minLength !== void 0) {
1990
2644
  schema = schema.min(
1991
2645
  attr.minLength,
@@ -2004,7 +2658,7 @@ function createTextValidator(attr) {
2004
2658
  return schema;
2005
2659
  }
2006
2660
  function createNumberValidator(attr) {
2007
- let schema = import_zod3.z.number();
2661
+ let schema = import_zod4.z.number();
2008
2662
  if (attr.min !== void 0) {
2009
2663
  schema = schema.min(attr.min, `${attr.label} must be at least ${attr.min}`);
2010
2664
  }
@@ -2017,81 +2671,75 @@ function createNumberValidator(attr) {
2017
2671
  return schema;
2018
2672
  }
2019
2673
  function createCheckboxValidator(_attr) {
2020
- return import_zod3.z.boolean();
2674
+ return import_zod4.z.boolean();
2021
2675
  }
2022
2676
  function createDateValidator(attr) {
2023
- return import_zod3.z.string().datetime({ message: `${attr.label} must be a valid ISO date` });
2677
+ return import_zod4.z.string().datetime({ message: `${attr.label} must be a valid ISO date` });
2024
2678
  }
2025
2679
  function createPhoneValidator(_attr) {
2026
- return import_zod3.z.object({
2027
- countryCode: import_zod3.z.string().length(3),
2028
- phoneNumber: import_zod3.z.string().min(1)
2680
+ return import_zod4.z.object({
2681
+ countryCode: import_zod4.z.string().length(3),
2682
+ phoneNumber: import_zod4.z.string().min(1)
2029
2683
  });
2030
2684
  }
2031
2685
  function createCurrencyValidator(_attr) {
2032
- return import_zod3.z.object({
2033
- code: import_zod3.z.string().length(3),
2034
- value: import_zod3.z.number().min(0)
2686
+ return import_zod4.z.object({
2687
+ code: import_zod4.z.string().length(3),
2688
+ value: import_zod4.z.number().min(0)
2035
2689
  });
2036
2690
  }
2037
2691
  function createStatusValidator(attr) {
2038
2692
  const validValues = attr.options.map((opt) => opt.value);
2039
- return import_zod3.z.enum(validValues, {
2040
- errorMap: () => ({
2041
- message: `${attr.label} must be one of: ${validValues.join(", ")}`
2042
- })
2693
+ return import_zod4.z.enum(validValues, {
2694
+ error: `${attr.label} must be one of: ${validValues.join(", ")}`
2043
2695
  });
2044
2696
  }
2045
2697
  function createSelectValidator(attr) {
2046
2698
  const validValues = attr.options.map((opt) => opt.value);
2047
- return import_zod3.z.enum(validValues, {
2048
- errorMap: () => ({
2049
- message: `${attr.label} must be one of: ${validValues.join(", ")}`
2050
- })
2699
+ return import_zod4.z.enum(validValues, {
2700
+ error: `${attr.label} must be one of: ${validValues.join(", ")}`
2051
2701
  });
2052
2702
  }
2053
2703
  function createMultiselectValidator(attr) {
2054
2704
  const validValues = attr.options.map((opt) => opt.value);
2055
- return import_zod3.z.array(
2056
- import_zod3.z.enum(validValues, {
2057
- errorMap: () => ({
2058
- message: `Each value must be one of: ${validValues.join(", ")}`
2059
- })
2705
+ return import_zod4.z.array(
2706
+ import_zod4.z.enum(validValues, {
2707
+ error: `Each value must be one of: ${validValues.join(", ")}`
2060
2708
  })
2061
2709
  );
2062
2710
  }
2063
2711
  function createLocationValidator(_attr) {
2064
- return import_zod3.z.object({
2065
- address: import_zod3.z.string().optional(),
2066
- address2: import_zod3.z.string().optional(),
2067
- city: import_zod3.z.string().optional(),
2068
- state: import_zod3.z.string().optional(),
2069
- postalCode: import_zod3.z.string().optional(),
2070
- country: import_zod3.z.string().length(3).optional(),
2071
- latitude: import_zod3.z.number().optional(),
2072
- longitude: import_zod3.z.number().optional()
2712
+ return import_zod4.z.object({
2713
+ address: import_zod4.z.string().optional(),
2714
+ address2: import_zod4.z.string().optional(),
2715
+ city: import_zod4.z.string().optional(),
2716
+ state: import_zod4.z.string().optional(),
2717
+ postalCode: import_zod4.z.string().optional(),
2718
+ country: import_zod4.z.string().length(3).optional(),
2719
+ latitude: import_zod4.z.number().optional(),
2720
+ longitude: import_zod4.z.number().optional()
2073
2721
  });
2074
2722
  }
2075
2723
  function createTimestampValidator(_attr) {
2076
- return import_zod3.z.number().int().positive();
2724
+ return import_zod4.z.number().int().positive();
2077
2725
  }
2078
2726
  function createFileValidator(_attr) {
2079
- return import_zod3.z.string().uuid();
2727
+ return import_zod4.z.string().uuid();
2080
2728
  }
2081
2729
  function createUserValidator(_attr) {
2082
- return import_zod3.z.string().uuid();
2730
+ return import_zod4.z.string().uuid();
2083
2731
  }
2084
2732
  function createSingleRelationValidator(attr) {
2085
- const uuidSchema = import_zod3.z.string().uuid({
2733
+ const uuidSchema = import_zod4.z.string().uuid({
2086
2734
  message: `${attr.label} must be a valid record ID`
2087
2735
  });
2088
- return import_zod3.z.union([uuidSchema, import_zod3.z.null()]);
2736
+ return import_zod4.z.union([uuidSchema, import_zod4.z.null()]);
2089
2737
  }
2090
2738
  function createMultiRelationValidator(attr) {
2091
- const uuidSchema = import_zod3.z.string().uuid({
2739
+ const uuidSchema = import_zod4.z.string().uuid({
2092
2740
  message: `Each ${attr.label} item must be a valid record ID`
2093
2741
  });
2094
- let arraySchema = import_zod3.z.array(uuidSchema);
2742
+ let arraySchema = import_zod4.z.array(uuidSchema);
2095
2743
  if (attr.minItems !== void 0) {
2096
2744
  arraySchema = arraySchema.min(
2097
2745
  attr.minItems,
@@ -2113,7 +2761,7 @@ function createRelationValidator(attr) {
2113
2761
  return createSingleRelationValidator(attr);
2114
2762
  }
2115
2763
  function createRatingValidator(attr) {
2116
- let schema = import_zod3.z.number().int().min(0);
2764
+ let schema = import_zod4.z.number().int().min(0);
2117
2765
  if (attr.max !== void 0) {
2118
2766
  schema = schema.max(attr.max, `${attr.label} must be at most ${attr.max}`);
2119
2767
  }
@@ -2152,7 +2800,7 @@ function createAttributeValidator(attr) {
2152
2800
  case "rating":
2153
2801
  return createRatingValidator(attr);
2154
2802
  default:
2155
- return import_zod3.z.unknown();
2803
+ return import_zod4.z.unknown();
2156
2804
  }
2157
2805
  }
2158
2806
  function createObjectValidator(objectDef) {
@@ -2164,7 +2812,7 @@ function createObjectValidator(objectDef) {
2164
2812
  }
2165
2813
  shape[attr.name] = validator;
2166
2814
  }
2167
- return import_zod3.z.object(shape);
2815
+ return import_zod4.z.object(shape);
2168
2816
  }
2169
2817
  function validateObject(objectDef, data) {
2170
2818
  const validator = createObjectValidator(objectDef);
@@ -2177,7 +2825,7 @@ function validateObject(objectDef, data) {
2177
2825
  }
2178
2826
  return {
2179
2827
  success: false,
2180
- errors: result.error.errors.map((err) => ({
2828
+ errors: result.error.issues.map((err) => ({
2181
2829
  path: err.path.map(String),
2182
2830
  message: err.message
2183
2831
  }))
@@ -2198,7 +2846,7 @@ function createDraftValidator(objectDef) {
2198
2846
  const validator = createAttributeValidator(attr).optional();
2199
2847
  shape[attr.name] = validator;
2200
2848
  }
2201
- return import_zod3.z.object(shape);
2849
+ return import_zod4.z.object(shape);
2202
2850
  }
2203
2851
  function validateDraft(objectDef, data) {
2204
2852
  const validator = createDraftValidator(objectDef);
@@ -2211,7 +2859,7 @@ function validateDraft(objectDef, data) {
2211
2859
  }
2212
2860
  return {
2213
2861
  success: false,
2214
- errors: result.error.errors.map((err) => ({
2862
+ errors: result.error.issues.map((err) => ({
2215
2863
  path: err.path.map(String),
2216
2864
  message: err.message
2217
2865
  }))
@@ -2606,14 +3254,15 @@ var ObjectSchemaService = class {
2606
3254
  });
2607
3255
  }
2608
3256
  }
3257
+ const dbAttributes = await this.adapter.attributes.findByObjectId(objectId);
3258
+ const attributes = dbAttributes.map((attr) => this.convertDBAttributeToAttribute(attr));
2609
3259
  if (updates.labelExpression !== void 0 && updates.labelExpression !== oldValues.labelExpression) {
2610
3260
  const newExpression = updates.labelExpression;
2611
- await this.adapter.objectRecords.batchRefreshLabels(
2612
- objectId,
2613
- (values) => renderLabelExpression(newExpression, values)
2614
- );
3261
+ await this.adapter.objectRecords.batchRefreshLabels(objectId, (values) => {
3262
+ const enrichedValues = enrichValuesWithSelectLabels(values, attributes);
3263
+ return renderLabelExpression(newExpression, enrichedValues);
3264
+ });
2615
3265
  }
2616
- const dbAttributes = await this.adapter.attributes.findByObjectId(objectId);
2617
3266
  return this.convertDBObjectToDefinition(updatedDbObject, dbAttributes);
2618
3267
  }
2619
3268
  /**
@@ -2926,71 +3575,14 @@ var ObjectSchemaService = class {
2926
3575
  }
2927
3576
  };
2928
3577
 
2929
- // src/exceptions.ts
2930
- var SchemaErrorCode = {
2931
- // Generic
2932
- UNKNOWN: "SCHEMA_UNKNOWN_ERROR",
2933
- // Not Found
2934
- OBJECT_NOT_FOUND: "SCHEMA_OBJECT_NOT_FOUND",
2935
- ATTRIBUTE_NOT_FOUND: "SCHEMA_ATTRIBUTE_NOT_FOUND",
2936
- RECORD_NOT_FOUND: "SCHEMA_RECORD_NOT_FOUND",
2937
- USER_PROFILE_NOT_FOUND: "SCHEMA_USER_PROFILE_NOT_FOUND",
2938
- FILE_NOT_FOUND: "SCHEMA_FILE_NOT_FOUND",
2939
- ROLE_NOT_FOUND: "SCHEMA_ROLE_NOT_FOUND",
2940
- // Validation
2941
- VALIDATION_FAILED: "SCHEMA_VALIDATION_FAILED",
2942
- INVALID_ATTRIBUTE_NAME: "SCHEMA_INVALID_ATTRIBUTE_NAME",
2943
- INVALID_OBJECT_NAME: "SCHEMA_INVALID_OBJECT_NAME",
2944
- // Protected Resources
2945
- PROTECTED_OBJECT: "SCHEMA_PROTECTED_OBJECT",
2946
- PROTECTED_ATTRIBUTE: "SCHEMA_PROTECTED_ATTRIBUTE",
2947
- PROTECTED_ROLE: "SCHEMA_PROTECTED_ROLE",
2948
- // Permissions
2949
- FORBIDDEN: "SCHEMA_FORBIDDEN",
2950
- // Sync
2951
- SYNC_FAILED: "SCHEMA_SYNC_FAILED",
2952
- NOT_SYSTEM_OBJECT: "SCHEMA_NOT_SYSTEM_OBJECT",
2953
- // Duplicates
2954
- DUPLICATE_OBJECT: "SCHEMA_DUPLICATE_OBJECT",
2955
- DUPLICATE_ATTRIBUTE: "SCHEMA_DUPLICATE_ATTRIBUTE"
2956
- };
2957
- var SchemaError = class extends Error {
2958
- constructor(message, code = SchemaErrorCode.UNKNOWN, details) {
2959
- super(message);
2960
- this.name = "SchemaError";
2961
- this.code = code;
2962
- this.details = details;
2963
- Object.setPrototypeOf(this, new.target.prototype);
2964
- }
2965
- toJSON() {
2966
- return {
2967
- name: this.name,
2968
- code: this.code,
2969
- message: this.message,
2970
- details: this.details
2971
- };
2972
- }
2973
- };
2974
- var ForbiddenError = class extends SchemaError {
2975
- constructor(objectName, action, userId) {
2976
- super(`No ${action} permission on object "${objectName}"`, SchemaErrorCode.FORBIDDEN, {
2977
- objectName,
2978
- action,
2979
- userId
2980
- });
2981
- this.name = "ForbiddenError";
2982
- this.objectName = objectName;
2983
- this.action = action;
2984
- this.userId = userId;
2985
- }
2986
- };
2987
-
2988
3578
  // src/runtime/services/permission.service.ts
2989
3579
  var PermissionService = class {
2990
3580
  constructor(adapter, tenantId, options) {
2991
3581
  this.adapter = adapter;
2992
3582
  this.tenantId = tenantId;
2993
3583
  this.cache = /* @__PURE__ */ new Map();
3584
+ /** Track pending permission fetches to prevent duplicate concurrent requests */
3585
+ this.pendingFetches = /* @__PURE__ */ new Map();
2994
3586
  if (!adapter.permissions) {
2995
3587
  throw new Error(
2996
3588
  "PermissionService requires a DatabaseAdapter with permissions repository. Make sure your adapter implements the permissions property."
@@ -3139,6 +3731,23 @@ var PermissionService = class {
3139
3731
  if (cached && cached.expiresAt > Date.now()) {
3140
3732
  return cached.permissions;
3141
3733
  }
3734
+ const pendingFetch = this.pendingFetches.get(cacheKey);
3735
+ if (pendingFetch) {
3736
+ return pendingFetch;
3737
+ }
3738
+ const fetchPromise = this.fetchAndCachePermissions(userProfileId, cacheKey);
3739
+ this.pendingFetches.set(cacheKey, fetchPromise);
3740
+ try {
3741
+ return await fetchPromise;
3742
+ } finally {
3743
+ this.pendingFetches.delete(cacheKey);
3744
+ }
3745
+ }
3746
+ /**
3747
+ * Fetch permissions from database and cache the result
3748
+ * @internal
3749
+ */
3750
+ async fetchAndCachePermissions(userProfileId, cacheKey) {
3142
3751
  const permissions = await this.permissionsRepo.getEffectivePermissions(
3143
3752
  userProfileId,
3144
3753
  this.tenantId
@@ -3667,8 +4276,11 @@ var RelationService = class {
3667
4276
  async validateRelationsOrThrow(schema, data) {
3668
4277
  const result = await this.validateRelations(schema, data);
3669
4278
  if (!result.valid) {
3670
- const messages = result.errors.map((e) => `${e.attribute}: ${e.message}`).join("; ");
3671
- throw new Error(`Relation validation failed: ${messages}`);
4279
+ const errors = result.errors.map((e) => ({
4280
+ path: [e.attribute],
4281
+ message: e.message
4282
+ }));
4283
+ throw new ValidationError("Relation validation failed", errors);
3672
4284
  }
3673
4285
  }
3674
4286
  // ============================================================================
@@ -3716,7 +4328,8 @@ var RelationService = class {
3716
4328
  totalCount += result.total;
3717
4329
  for (const record of result.records) {
3718
4330
  const template = target.displayTemplate || objectSchema.labelExpression;
3719
- const label = renderLabelExpression(template, record.values);
4331
+ const enrichedValues = enrichValuesWithSelectLabels(record.values, objectSchema.attributes);
4332
+ const label = renderLabelExpression(template, enrichedValues);
3720
4333
  allOptions.push({
3721
4334
  id: record.id,
3722
4335
  objectId: objectSchema.id,
@@ -3780,7 +4393,8 @@ var RelationService = class {
3780
4393
  }
3781
4394
  }
3782
4395
  for (const record of objectRecords) {
3783
- const label = renderLabelExpression(template, record.values);
4396
+ const enrichedValues = enrichValuesWithSelectLabels(record.values, objectSchema.attributes);
4397
+ const label = renderLabelExpression(template, enrichedValues);
3784
4398
  resolved.push({
3785
4399
  id: record.id,
3786
4400
  objectId: record.objectId,
@@ -3801,10 +4415,14 @@ var RelationService = class {
3801
4415
  if (!attribute || attribute.type !== "relation") {
3802
4416
  return null;
3803
4417
  }
3804
- return {
4418
+ const merged = {
3805
4419
  ...attribute.config,
3806
4420
  ...attribute
3807
4421
  };
4422
+ if (!("targets" in merged && Array.isArray(merged.targets) && "cardinality" in merged)) {
4423
+ return null;
4424
+ }
4425
+ return merged;
3808
4426
  }
3809
4427
  };
3810
4428
 
@@ -3817,7 +4435,7 @@ var RecordService = class {
3817
4435
  this.relationService = new RelationService(adapter, registry);
3818
4436
  this.hookRegistry = options?.hookRegistry ?? new NoopHookRegistry();
3819
4437
  this.permissionService = options?.permissionService;
3820
- this.auditService = options?.auditService;
4438
+ this.auditService = options?.auditService ?? (adapter.audit ? new AuditService(adapter, tenantId) : void 0);
3821
4439
  this.userId = options?.userId;
3822
4440
  this.userEmail = options?.userEmail;
3823
4441
  }
@@ -3884,21 +4502,23 @@ var RecordService = class {
3884
4502
  /**
3885
4503
  * Compute display label from schema expression
3886
4504
  * Automatically resolves relation attribute values to their labels
4505
+ * and select/multiselect values to their option labels
3887
4506
  * @internal
3888
4507
  */
3889
4508
  async computeLabel(schema, values) {
3890
4509
  const attrNames = extractAttributeNames(schema.labelExpression);
4510
+ let enrichedValues = enrichValuesWithSelectLabels(values, schema.attributes);
3891
4511
  const relationAttrs = schema.attributes.filter(
3892
4512
  (attr) => attr.type === "relation" && attrNames.includes(attr.name)
3893
4513
  );
3894
4514
  if (relationAttrs.length === 0) {
3895
- return renderLabelExpression(schema.labelExpression, values);
4515
+ return renderLabelExpression(schema.labelExpression, enrichedValues);
3896
4516
  }
3897
4517
  const resolvedMap = await this.resolveRelationLabels(relationAttrs, values);
3898
4518
  if (resolvedMap.size === 0) {
3899
- return renderLabelExpression(schema.labelExpression, values);
4519
+ return renderLabelExpression(schema.labelExpression, enrichedValues);
3900
4520
  }
3901
- const enrichedValues = { ...values };
4521
+ enrichedValues = { ...enrichedValues };
3902
4522
  for (const attr of relationAttrs) {
3903
4523
  const val = values[attr.name];
3904
4524
  const ids = this.extractRelationIds(val);
@@ -4011,7 +4631,7 @@ var RecordService = class {
4011
4631
  async getRecordOrThrow(recordId) {
4012
4632
  const record = await this.getRecord(recordId);
4013
4633
  if (!record) {
4014
- throw new Error(`Record with id "${recordId}" not found`);
4634
+ throw new RecordNotFoundError(recordId);
4015
4635
  }
4016
4636
  return record;
4017
4637
  }
@@ -4189,7 +4809,7 @@ var RecordService = class {
4189
4809
  await this.checkPermission(schema.name, "delete");
4190
4810
  if (options?.checkSystem) {
4191
4811
  if (schema.system) {
4192
- throw new Error(`Cannot delete record of system object "${schema.label}"`);
4812
+ throw new ProtectedResourceError("object", schema.name, "delete");
4193
4813
  }
4194
4814
  }
4195
4815
  if (!options?.skipReferenceCheck) {
@@ -4238,7 +4858,11 @@ var RecordService = class {
4238
4858
  async restoreRecord(recordId, options) {
4239
4859
  const record = await this.getRecordOrThrow(recordId);
4240
4860
  if (!record.deletedAt) {
4241
- throw new Error(`Record "${recordId}" is not deleted`);
4861
+ throw new SchemaError(
4862
+ `Record "${recordId}" is not deleted`,
4863
+ SchemaErrorCode.VALIDATION_FAILED,
4864
+ { recordId, reason: "not_deleted" }
4865
+ );
4242
4866
  }
4243
4867
  const schema = await this.schemaService.getObjectSchema(record.objectId);
4244
4868
  await this.checkPermission(schema.name, "update");
@@ -4676,16 +5300,19 @@ var ViewService = class {
4676
5300
  this.validateViewName(input.name);
4677
5301
  const existing = await this.adapter.views.findByName(tenantId, input.objectName, input.name);
4678
5302
  if (existing) {
4679
- throw new Error(`View "${input.name}" already exists for object "${input.objectName}"`);
5303
+ throw new SchemaError(
5304
+ `View "${input.name}" already exists for object "${input.objectName}"`,
5305
+ SchemaErrorCode.DUPLICATE_ATTRIBUTE,
5306
+ { viewName: input.name, objectName: input.objectName }
5307
+ );
4680
5308
  }
4681
5309
  if (this.nativeViews.has(input.objectName, input.name)) {
4682
- throw new Error(
4683
- `Cannot create view "${input.name}": a system view with this name already exists`
5310
+ throw new SchemaError(
5311
+ `Cannot create view "${input.name}": a system view with this name already exists`,
5312
+ SchemaErrorCode.DUPLICATE_ATTRIBUTE,
5313
+ { viewName: input.name, objectName: input.objectName, system: true }
4684
5314
  );
4685
5315
  }
4686
- if (!input.tabs || input.tabs.length === 0) {
4687
- throw new Error("View must have at least one tab");
4688
- }
4689
5316
  const dbView = await this.adapter.views.create({
4690
5317
  tenantId,
4691
5318
  objectName: input.objectName,
@@ -4693,7 +5320,7 @@ var ViewService = class {
4693
5320
  label: input.label,
4694
5321
  description: input.description,
4695
5322
  icon: input.icon,
4696
- tabs: input.tabs,
5323
+ tabs: input.tabs ?? [],
4697
5324
  default: input.default ?? false,
4698
5325
  system: false,
4699
5326
  // Custom views are never system
@@ -4711,13 +5338,10 @@ var ViewService = class {
4711
5338
  async updateView(viewId, input) {
4712
5339
  const dbView = await this.adapter.views.findById(viewId);
4713
5340
  if (!dbView) {
4714
- throw new Error(`View with id "${viewId}" not found`);
5341
+ throw new NotFoundError("View", viewId);
4715
5342
  }
4716
5343
  if (dbView.system) {
4717
- throw new Error("Cannot modify system views. System views are protected.");
4718
- }
4719
- if (input.tabs && input.tabs.length === 0) {
4720
- throw new Error("View must have at least one tab");
5344
+ throw new ProtectedResourceError("attribute", dbView.name, "modify");
4721
5345
  }
4722
5346
  const updated = await this.adapter.views.update(viewId, {
4723
5347
  label: input.label,
@@ -4737,10 +5361,10 @@ var ViewService = class {
4737
5361
  async deleteView(viewId) {
4738
5362
  const dbView = await this.adapter.views.findById(viewId);
4739
5363
  if (!dbView) {
4740
- throw new Error(`View with id "${viewId}" not found`);
5364
+ throw new NotFoundError("View", viewId);
4741
5365
  }
4742
5366
  if (dbView.system) {
4743
- throw new Error("Cannot delete system views. System views are protected.");
5367
+ throw new ProtectedResourceError("attribute", dbView.name, "delete");
4744
5368
  }
4745
5369
  await this.adapter.views.delete(viewId);
4746
5370
  }
@@ -4754,7 +5378,7 @@ var ViewService = class {
4754
5378
  async setDefaultView(viewId, tenantId) {
4755
5379
  const dbView = await this.adapter.views.findById(viewId);
4756
5380
  if (!dbView) {
4757
- throw new Error(`View with id "${viewId}" not found`);
5381
+ throw new NotFoundError("View", viewId);
4758
5382
  }
4759
5383
  const currentViews = await this.adapter.views.findByObjectName(tenantId, dbView.objectName);
4760
5384
  for (const v of currentViews) {
@@ -4773,16 +5397,23 @@ var ViewService = class {
4773
5397
  */
4774
5398
  validateViewName(name) {
4775
5399
  if (!name || name.length === 0) {
4776
- throw new Error("View name cannot be empty");
5400
+ throw new ValidationError("View name cannot be empty", [
5401
+ { path: ["name"], message: "View name cannot be empty" }
5402
+ ]);
4777
5403
  }
4778
5404
  if (name.length > 63) {
4779
- throw new Error("View name is too long (max 63 characters)");
5405
+ throw new ValidationError("View name is too long", [
5406
+ { path: ["name"], message: "View name is too long (max 63 characters)" }
5407
+ ]);
4780
5408
  }
4781
5409
  const kebabCaseRegex = /^[a-z][a-z0-9-]*$/;
4782
5410
  if (!kebabCaseRegex.test(name)) {
4783
- throw new Error(
4784
- "Invalid view name format. Name must be in kebab-case (e.g., 'detail', 'list-view')"
4785
- );
5411
+ throw new ValidationError("Invalid view name format", [
5412
+ {
5413
+ path: ["name"],
5414
+ message: "View name must be in kebab-case (e.g., 'detail', 'list-view')"
5415
+ }
5416
+ ]);
4786
5417
  }
4787
5418
  }
4788
5419
  /**
@@ -5149,6 +5780,7 @@ var NoopGeocodingAdapter = class {
5149
5780
  AuditService,
5150
5781
  DEFAULT_LABEL_FALLBACK,
5151
5782
  FileService,
5783
+ FlowService,
5152
5784
  GeocodingService,
5153
5785
  GlobalSearchService,
5154
5786
  NoopGeocodingAdapter,
@@ -5161,6 +5793,7 @@ var NoopGeocodingAdapter = class {
5161
5793
  ViewService,
5162
5794
  buildAuditChanges,
5163
5795
  createMockAdapter,
5796
+ enrichValuesWithSelectLabels,
5164
5797
  extractAttributeNames,
5165
5798
  getSyncPreview,
5166
5799
  getViewSyncPreview,