@fayz-ai/core 0.10.0 → 0.11.0

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 (93) hide show
  1. package/dist/chunk-BEGXYHS4.js +566 -0
  2. package/dist/chunk-BEGXYHS4.js.map +1 -0
  3. package/dist/chunk-ED6NBH6U.js +348 -0
  4. package/dist/chunk-ED6NBH6U.js.map +1 -0
  5. package/dist/chunk-EWKX4VUM.js +429 -0
  6. package/dist/chunk-EWKX4VUM.js.map +1 -0
  7. package/dist/chunk-LARZWDHA.js +319 -0
  8. package/dist/chunk-LARZWDHA.js.map +1 -0
  9. package/dist/{chunk-732GZEUI.js → chunk-RRYFXUQX.js} +22 -367
  10. package/dist/chunk-RRYFXUQX.js.map +1 -0
  11. package/dist/{chunk-M27JPX3K.js → chunk-TDI6F6PT.js} +363 -24
  12. package/dist/chunk-TDI6F6PT.js.map +1 -0
  13. package/dist/{chunk-JO7Z2AOE.js → chunk-UVKH6RDX.js} +211 -335
  14. package/dist/chunk-UVKH6RDX.js.map +1 -0
  15. package/dist/chunk-V53GR4UT.js +289 -0
  16. package/dist/chunk-V53GR4UT.js.map +1 -0
  17. package/dist/connector-runtime/index.d.ts +15 -0
  18. package/dist/connector-runtime/index.d.ts.map +1 -0
  19. package/dist/connector-runtime/index.js +3 -0
  20. package/dist/connector-runtime/index.js.map +1 -0
  21. package/dist/data/archetype.d.ts.map +1 -1
  22. package/dist/data/bulk.d.ts +24 -0
  23. package/dist/data/bulk.d.ts.map +1 -1
  24. package/dist/data/cached.d.ts.map +1 -1
  25. package/dist/data/custom-fields.d.ts +5 -0
  26. package/dist/data/custom-fields.d.ts.map +1 -0
  27. package/dist/data/index.d.ts +2 -1
  28. package/dist/data/index.d.ts.map +1 -1
  29. package/dist/data/index.js +3 -1
  30. package/dist/data/mock.d.ts.map +1 -1
  31. package/dist/data/platform-api.d.ts.map +1 -1
  32. package/dist/data/resolve.d.ts.map +1 -1
  33. package/dist/data/supabase.d.ts.map +1 -1
  34. package/dist/data/types.d.ts +8 -0
  35. package/dist/data/types.d.ts.map +1 -1
  36. package/dist/events/log.d.ts.map +1 -1
  37. package/dist/fields/store.d.ts +45 -0
  38. package/dist/fields/store.d.ts.map +1 -0
  39. package/dist/i18n/index.d.ts.map +1 -1
  40. package/dist/i18n/index.js +1 -1
  41. package/dist/i18n/shell-translations.en.d.ts.map +1 -1
  42. package/dist/i18n/shell-translations.pt-br.d.ts.map +1 -1
  43. package/dist/index.d.ts +22 -8
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +180 -29
  46. package/dist/index.js.map +1 -1
  47. package/dist/integrations/events.d.ts +7 -0
  48. package/dist/integrations/events.d.ts.map +1 -0
  49. package/dist/integrations/index.d.ts +663 -0
  50. package/dist/integrations/index.d.ts.map +1 -0
  51. package/dist/integrations/index.js +3 -1
  52. package/dist/integrations/permissions.d.ts +132 -0
  53. package/dist/integrations/permissions.d.ts.map +1 -0
  54. package/dist/integrations/request.d.ts +170 -0
  55. package/dist/integrations/request.d.ts.map +1 -0
  56. package/dist/integrations/runtime-token.d.ts +33 -0
  57. package/dist/integrations/runtime-token.d.ts.map +1 -0
  58. package/dist/integrations/runtime.d.ts +398 -0
  59. package/dist/integrations/runtime.d.ts.map +1 -0
  60. package/dist/integrations/settings.d.ts +102 -0
  61. package/dist/integrations/settings.d.ts.map +1 -0
  62. package/dist/integrations/store.d.ts +145 -0
  63. package/dist/integrations/store.d.ts.map +1 -0
  64. package/dist/plugin/index.js +2 -1
  65. package/dist/plugin/runtime.d.ts.map +1 -1
  66. package/dist/registry/entities.d.ts +41 -15
  67. package/dist/registry/entities.d.ts.map +1 -1
  68. package/dist/registry/index.d.ts +2 -2
  69. package/dist/registry/index.d.ts.map +1 -1
  70. package/dist/{shell-translations.pt-br-2GWRQRRP.js → shell-translations.pt-br-BM5ZFSKJ.js} +72 -3
  71. package/dist/shell-translations.pt-br-BM5ZFSKJ.js.map +1 -0
  72. package/dist/telemetry/errors.d.ts +87 -0
  73. package/dist/telemetry/errors.d.ts.map +1 -0
  74. package/dist/testing/index.js +4 -1
  75. package/dist/testing.d.ts +2 -0
  76. package/dist/testing.d.ts.map +1 -1
  77. package/dist/types/analytics.d.ts +17 -0
  78. package/dist/types/analytics.d.ts.map +1 -1
  79. package/dist/types/crud.d.ts +15 -1
  80. package/dist/types/crud.d.ts.map +1 -1
  81. package/dist/types/plugins.d.ts +83 -0
  82. package/dist/types/plugins.d.ts.map +1 -1
  83. package/package.json +7 -3
  84. package/dist/chunk-6RBW2Y5K.js +0 -42
  85. package/dist/chunk-6RBW2Y5K.js.map +0 -1
  86. package/dist/chunk-732GZEUI.js.map +0 -1
  87. package/dist/chunk-CPMHCTX3.js +0 -77
  88. package/dist/chunk-CPMHCTX3.js.map +0 -1
  89. package/dist/chunk-JO7Z2AOE.js.map +0 -1
  90. package/dist/chunk-M27JPX3K.js.map +0 -1
  91. package/dist/integrations.d.ts +0 -159
  92. package/dist/integrations.d.ts.map +0 -1
  93. package/dist/shell-translations.pt-br-2GWRQRRP.js.map +0 -1
@@ -1,313 +1,5 @@
1
- import * as React from 'react';
2
-
3
- // src/data/filters.ts
4
- var LOWER_INCLUSIVE = ["from", "gte", "start", "after", "since", "min"];
5
- var LOWER_EXCLUSIVE = ["gt"];
6
- var UPPER_INCLUSIVE = ["to", "lte", "end", "until", "before", "max"];
7
- var UPPER_EXCLUSIVE = ["lt"];
8
- function pick(source, names) {
9
- for (const name of names) {
10
- if (source[name] !== void 0 && source[name] !== null) return source[name];
11
- const dollar = `$${name}`;
12
- if (source[dollar] !== void 0 && source[dollar] !== null) return source[dollar];
13
- }
14
- return void 0;
15
- }
16
- function parseRangeFilter(value) {
17
- if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
18
- const source = value;
19
- const range = {};
20
- const gte = pick(source, LOWER_INCLUSIVE);
21
- const gt = pick(source, LOWER_EXCLUSIVE);
22
- const lte = pick(source, UPPER_INCLUSIVE);
23
- const lt = pick(source, UPPER_EXCLUSIVE);
24
- if (gte !== void 0) range.gte = gte;
25
- if (gt !== void 0) range.gt = gt;
26
- if (lte !== void 0) range.lte = lte;
27
- if (lt !== void 0) range.lt = lt;
28
- return Object.keys(range).length ? range : null;
29
- }
30
- function isFilterObject(value) {
31
- return typeof value === "object" && value !== null && !Array.isArray(value);
32
- }
33
- function comparable(value) {
34
- if (typeof value === "number") return value;
35
- if (value instanceof Date) return value.getTime();
36
- const ts = Date.parse(String(value));
37
- if (!Number.isNaN(ts)) return ts;
38
- return Number(value);
39
- }
40
- function matchesRange(raw, range) {
41
- const value = comparable(raw);
42
- if (Number.isNaN(value)) return false;
43
- if (range.gte !== void 0 && value < comparable(range.gte)) return false;
44
- if (range.gt !== void 0 && value <= comparable(range.gt)) return false;
45
- if (range.lte !== void 0 && value > comparable(range.lte)) return false;
46
- if (range.lt !== void 0 && value >= comparable(range.lt)) return false;
47
- return true;
48
- }
49
- function applyRangeToQuery(query, column, range) {
50
- let q = query;
51
- for (const op of ["gte", "gt", "lte", "lt"]) {
52
- const bound = range[op];
53
- if (bound === void 0) continue;
54
- q = q[op](column, bound);
55
- }
56
- return q;
57
- }
58
-
59
- // src/data/supabase.ts
60
- function camelToSnake(str) {
61
- return str.replace(/[A-Z]/g, (c) => "_" + c.toLowerCase());
62
- }
63
- function snakeToCamel(str) {
64
- return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
65
- }
66
- function mapRow(row, columnMap) {
67
- const result = {};
68
- for (const [key, value] of Object.entries(row)) {
69
- const camelKey = columnMap?.[key] ?? snakeToCamel(key);
70
- result[camelKey] = value;
71
- }
72
- return result;
73
- }
74
- function mapEntity(entity, columnMap) {
75
- const reverseMap = {};
76
- if (columnMap) {
77
- for (const [snake, camel] of Object.entries(columnMap)) {
78
- reverseMap[camel] = snake;
79
- }
80
- }
81
- const result = {};
82
- for (const [key, value] of Object.entries(entity)) {
83
- if (key === "id" || key === "createdAt" || key === "updatedAt" || key === "tenantId") continue;
84
- const snakeKey = reverseMap[key] ?? camelToSnake(key);
85
- result[snakeKey] = value;
86
- }
87
- return result;
88
- }
89
- var _globalSupabaseClient = null;
90
- function setGlobalSupabaseClient(client) {
91
- _globalSupabaseClient = client;
92
- }
93
- function getSupabaseClientOptional() {
94
- return _globalSupabaseClient;
95
- }
96
- function createSupabaseProvider(table, config) {
97
- const schema = config?.schema ?? "public";
98
- const tenantIdCol = config?.tenantIdColumn ?? "tenant_id";
99
- const searchCols = config?.searchColumns ?? [];
100
- const selectCols = config?.selectColumns ?? "*";
101
- const columnMap = config?.columnMap;
102
- function resolveTenantId2() {
103
- if (!config?.tenantId) return void 0;
104
- return typeof config.tenantId === "function" ? config.tenantId() : config.tenantId;
105
- }
106
- function getClient() {
107
- const supabase = config?.supabaseClient ?? _globalSupabaseClient;
108
- if (!supabase) throw new Error(`[@fayz-ai/core] Supabase client not available. Call createFayzApp with a provider first.`);
109
- return schema === "public" ? supabase : supabase.schema(schema);
110
- }
111
- return {
112
- async list(query) {
113
- const wantCount = query.countMode !== "none";
114
- let q = getClient().from(table).select(selectCols, wantCount ? { count: "exact" } : void 0);
115
- const tenantId = resolveTenantId2();
116
- if (tenantId) q = q.eq(tenantIdCol, tenantId);
117
- if (config?.filters) {
118
- for (const [col, val] of Object.entries(config.filters)) {
119
- q = q.eq(col, val);
120
- }
121
- }
122
- if (query.filters) {
123
- for (const [key, val] of Object.entries(query.filters)) {
124
- if (val == null || val === "") continue;
125
- const col = columnMap?.[key] ?? camelToSnake(key);
126
- const range = parseRangeFilter(val);
127
- if (range) {
128
- q = applyRangeToQuery(q, col, range);
129
- continue;
130
- }
131
- if (isFilterObject(val)) {
132
- throw new Error(
133
- `[@fayz-ai/core] Filter "${key}" is an object with no recognised bound. Use {from,to} / {gte,lte,gt,lt} for ranges, or a plain value for equality.`
134
- );
135
- }
136
- q = q.eq(col, val);
137
- }
138
- }
139
- if (query.search && searchCols.length > 0) {
140
- const term = `%${query.search}%`;
141
- const orClause = searchCols.map((col) => `${camelToSnake(col)}.ilike.${term}`).join(",");
142
- q = q.or(orClause);
143
- }
144
- if (query.sortBy) {
145
- q = q.order(camelToSnake(query.sortBy), { ascending: query.sortDir !== "desc" });
146
- } else {
147
- q = q.order("created_at", { ascending: false });
148
- }
149
- const page = query.page ?? 1;
150
- const pageSize = query.pageSize ?? 50;
151
- const from = (page - 1) * pageSize;
152
- q = q.range(from, from + pageSize - 1);
153
- const { data, error, count } = await q;
154
- if (error) throw error;
155
- return {
156
- data: (data ?? []).map((row) => mapRow(row, columnMap)),
157
- total: count ?? 0
158
- };
159
- },
160
- async create(data) {
161
- const row = mapEntity(data, columnMap);
162
- const tenantId = resolveTenantId2();
163
- if (tenantId) row[tenantIdCol] = tenantId;
164
- if (config?.defaults) {
165
- for (const [col, val] of Object.entries(config.defaults)) {
166
- if (row[col] === void 0) row[col] = val;
167
- }
168
- }
169
- const { data: created, error } = await getClient().from(table).insert(row).select().single();
170
- if (error) throw error;
171
- return mapRow(created, columnMap);
172
- },
173
- async createMany(rows, options) {
174
- const tenantId = resolveTenantId2();
175
- const size = Math.max(1, options?.chunkSize ?? 200);
176
- const prepared = rows.map((r) => {
177
- const row = mapEntity(r, columnMap);
178
- if (tenantId) row[tenantIdCol] = tenantId;
179
- if (config?.defaults) {
180
- for (const [col, val] of Object.entries(config.defaults)) {
181
- if (row[col] === void 0) row[col] = val;
182
- }
183
- }
184
- return row;
185
- });
186
- let created = 0;
187
- const failed = [];
188
- for (let start = 0; start < prepared.length; start += size) {
189
- const chunk = prepared.slice(start, start + size);
190
- const { error } = await getClient().from(table).insert(chunk);
191
- if (!error) {
192
- created += chunk.length;
193
- } else {
194
- const message = error instanceof Error ? error.message : String(error);
195
- for (let i = 0; i < chunk.length; i++) {
196
- const { error: rowError } = await getClient().from(table).insert(chunk[i]);
197
- if (rowError) {
198
- failed.push({
199
- index: start + i,
200
- message: rowError instanceof Error ? rowError.message : String(rowError ?? message)
201
- });
202
- } else created += 1;
203
- }
204
- }
205
- options?.onProgress?.(Math.min(start + chunk.length, prepared.length), prepared.length);
206
- }
207
- return { created, failed };
208
- },
209
- async update(id, data) {
210
- const row = mapEntity(data, columnMap);
211
- const { data: updated, error } = await getClient().from(table).update(row).eq("id", id).select().single();
212
- if (error) throw error;
213
- return mapRow(updated, columnMap);
214
- },
215
- async remove(id) {
216
- const { error } = await getClient().from(table).delete().eq("id", id);
217
- if (error) throw error;
218
- }
219
- };
220
- }
221
-
222
- // src/lib/cache.ts
223
- function sortDeep(value) {
224
- if (value === null || value === void 0 || typeof value !== "object") return value;
225
- if (Array.isArray(value)) return value.map(sortDeep);
226
- const sorted = {};
227
- for (const k of Object.keys(value).sort()) {
228
- sorted[k] = sortDeep(value[k]);
229
- }
230
- return sorted;
231
- }
232
- function stableKey(...parts) {
233
- return parts.map((p) => typeof p === "string" ? p : JSON.stringify(sortDeep(p))).join(":");
234
- }
235
- function createCacheStore(config) {
236
- const defaultTTL = config?.defaultTTL ?? 6e4;
237
- const maxEntries = config?.maxEntries ?? 500;
238
- const entries = /* @__PURE__ */ new Map();
239
- function evictLRU() {
240
- if (entries.size <= maxEntries) return;
241
- let oldestKey = null;
242
- let oldestAccess = Infinity;
243
- for (const [key, entry] of entries) {
244
- if (entry.lastAccess < oldestAccess) {
245
- oldestAccess = entry.lastAccess;
246
- oldestKey = key;
247
- }
248
- }
249
- if (oldestKey) entries.delete(oldestKey);
250
- }
251
- return {
252
- get(key) {
253
- const entry = entries.get(key);
254
- if (!entry) return void 0;
255
- if (Date.now() > entry.expiresAt) {
256
- entries.delete(key);
257
- return void 0;
258
- }
259
- entry.lastAccess = Date.now();
260
- return entry.data;
261
- },
262
- set(key, data, ttl) {
263
- const now = Date.now();
264
- entries.set(key, {
265
- data,
266
- expiresAt: now + (ttl ?? defaultTTL),
267
- lastAccess: now
268
- });
269
- evictLRU();
270
- },
271
- invalidate(prefix) {
272
- for (const key of entries.keys()) {
273
- if (key.startsWith(prefix)) {
274
- entries.delete(key);
275
- }
276
- }
277
- },
278
- clear() {
279
- entries.clear();
280
- },
281
- size() {
282
- return entries.size;
283
- }
284
- };
285
- }
286
- var globalCache = createCacheStore();
287
- function clearGlobalCache() {
288
- globalCache.clear();
289
- }
290
- var _activeTenantId;
291
- var listeners = /* @__PURE__ */ new Set();
292
- function setActiveTenantId(id) {
293
- if (id !== _activeTenantId) {
294
- _activeTenantId = id;
295
- clearGlobalCache();
296
- for (const l of listeners) l();
297
- }
298
- }
299
- function getActiveTenantId() {
300
- return _activeTenantId;
301
- }
302
- function subscribeActiveTenant(listener) {
303
- listeners.add(listener);
304
- return () => {
305
- listeners.delete(listener);
306
- };
307
- }
308
- function useActiveTenantId() {
309
- return React.useSyncExternalStore(subscribeActiveTenant, getActiveTenantId, getActiveTenantId);
310
- }
1
+ import { getActiveTenantId, getSupabaseClientOptional, parseRangeFilter, matchesRange, applyRangeToQuery, isFilterObject, globalCache, stableKey, createSupabaseProvider } from './chunk-LARZWDHA.js';
2
+ import { declaredCustomFields, validateEntityRecordData } from './chunk-EWKX4VUM.js';
311
3
 
312
4
  // src/data/count.ts
313
5
  var TTL_MS = 15e3;
@@ -511,6 +203,17 @@ function createFayzApiProvider(entityOrTable, config = {}) {
511
203
  headers: await headers(runtimeToken),
512
204
  body: JSON.stringify({ rows: [primaryKeys(id)] })
513
205
  }));
206
+ },
207
+ async removeMany(ids) {
208
+ if (!ids.length) return;
209
+ const runtimeToken = await resolveRuntimeToken(config);
210
+ const runtime = Boolean(runtimeToken);
211
+ for (const id of ids) await assertTenantScopedRowExists(id, runtime);
212
+ await parseResponse(await fetcher(tableRowsUrl(runtime), {
213
+ method: "DELETE",
214
+ headers: await headers(runtimeToken),
215
+ body: JSON.stringify({ rows: ids.map((id) => primaryKeys(id)) })
216
+ }));
514
217
  }
515
218
  };
516
219
  }
@@ -577,6 +280,10 @@ function createMockProvider(entityDefOrSearchKeys, initialData = []) {
577
280
  },
578
281
  async remove(id) {
579
282
  items = items.filter((i) => i.id !== id);
283
+ },
284
+ async removeMany(ids) {
285
+ const drop = new Set(ids);
286
+ items = items.filter((i) => !drop.has(i.id));
580
287
  }
581
288
  };
582
289
  }
@@ -593,26 +300,28 @@ var ARCHETYPE_CONFIG = {
593
300
  transaction: { table: "transactions", fkColumn: "transaction_id" },
594
301
  schedule: { table: "schedules", fkColumn: "schedule_id" }
595
302
  };
303
+ var CUSTOM_FIELDS_COLUMN = "custom_fields";
304
+ var CUSTOM_FIELD_FILTER_PREFIX = "custom.";
596
305
  var ARCHETYPE_COLUMNS = {
597
- person: /* @__PURE__ */ new Set(["name", "email", "phone", "document_number", "avatar_url", "date_of_birth", "address", "city", "state", "country", "postal_code", "tags", "is_active", "notes", "metadata"]),
598
- category: /* @__PURE__ */ new Set(["name", "slug", "parent_id", "icon", "color", "sort_order", "is_active", "metadata"]),
599
- product: /* @__PURE__ */ new Set(["name", "description", "sku", "price", "cost", "currency", "unit", "image_url", "stock", "min_stock", "status", "is_active", "tags", "metadata", "category_id"]),
600
- service: /* @__PURE__ */ new Set(["name", "description", "price", "cost", "currency", "duration_minutes", "image_url", "status", "is_active", "tags", "metadata", "category_id"]),
601
- location: /* @__PURE__ */ new Set(["name", "email", "phone", "address", "city", "state", "country", "postal_code", "is_headquarters", "is_active", "tags", "notes", "metadata"]),
602
- order: /* @__PURE__ */ new Set(["reference_number", "status", "party_id", "assignee_id", "location_id", "subtotal", "discount", "tax", "total", "currency", "due_at", "completed_at", "notes", "tags", "metadata"]),
603
- transaction: /* @__PURE__ */ new Set(["order_id", "party_id", "amount", "currency", "payment_method", "reference", "status", "transacted_at", "notes", "metadata"]),
604
- schedule: /* @__PURE__ */ new Set(["assignee_id", "location_id", "day_of_week", "specific_date", "starts_at", "ends_at", "is_active", "metadata"])
306
+ person: /* @__PURE__ */ new Set(["name", "email", "phone", "document_number", "avatar_url", "date_of_birth", "address", "city", "state", "country", "postal_code", "tags", "is_active", "notes", "metadata", CUSTOM_FIELDS_COLUMN]),
307
+ category: /* @__PURE__ */ new Set(["name", "slug", "parent_id", "icon", "color", "sort_order", "is_active", "metadata", CUSTOM_FIELDS_COLUMN]),
308
+ product: /* @__PURE__ */ new Set(["name", "description", "sku", "price", "cost", "currency", "unit", "image_url", "stock", "min_stock", "status", "is_active", "tags", "metadata", "category_id", CUSTOM_FIELDS_COLUMN]),
309
+ service: /* @__PURE__ */ new Set(["name", "description", "price", "cost", "currency", "duration_minutes", "image_url", "status", "is_active", "tags", "metadata", "category_id", CUSTOM_FIELDS_COLUMN]),
310
+ location: /* @__PURE__ */ new Set(["name", "email", "phone", "address", "city", "state", "country", "postal_code", "is_headquarters", "is_active", "tags", "notes", "metadata", CUSTOM_FIELDS_COLUMN]),
311
+ order: /* @__PURE__ */ new Set(["reference_number", "status", "party_id", "assignee_id", "location_id", "subtotal", "discount", "tax", "total", "currency", "due_at", "completed_at", "notes", "tags", "metadata", CUSTOM_FIELDS_COLUMN]),
312
+ transaction: /* @__PURE__ */ new Set(["order_id", "party_id", "amount", "currency", "payment_method", "reference", "status", "transacted_at", "notes", "metadata", CUSTOM_FIELDS_COLUMN]),
313
+ schedule: /* @__PURE__ */ new Set(["assignee_id", "location_id", "day_of_week", "specific_date", "starts_at", "ends_at", "is_active", "metadata", CUSTOM_FIELDS_COLUMN])
605
314
  };
606
- function camelToSnake2(str) {
315
+ function camelToSnake(str) {
607
316
  return str.replace(/[A-Z]/g, (c) => "_" + c.toLowerCase());
608
317
  }
609
- function snakeToCamel2(str) {
318
+ function snakeToCamel(str) {
610
319
  return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
611
320
  }
612
- function mapRow2(row) {
321
+ function mapRow(row) {
613
322
  const result = {};
614
323
  for (const [key, value] of Object.entries(row)) {
615
- result[snakeToCamel2(key)] = value;
324
+ result[snakeToCamel(key)] = value;
616
325
  }
617
326
  return result;
618
327
  }
@@ -632,8 +341,8 @@ function createArchetypeProvider(config) {
632
341
  const archetypeData = {};
633
342
  const projectData = {};
634
343
  for (const [key, value] of Object.entries(data)) {
635
- if (["id", "createdAt", "updatedAt", "tenantId", fkColumn].includes(key)) continue;
636
- const snakeKey = camelToSnake2(key);
344
+ if (["id", "createdAt", "updatedAt", "createdBy", "tenantId", fkColumn].includes(key)) continue;
345
+ const snakeKey = camelToSnake(key);
637
346
  if (archetypeColumns.has(snakeKey)) {
638
347
  archetypeData[snakeKey] = value;
639
348
  } else {
@@ -660,7 +369,7 @@ function createArchetypeProvider(config) {
660
369
  if (query.filters) {
661
370
  for (const [key, val] of Object.entries(query.filters)) {
662
371
  if (val == null || val === "") continue;
663
- const col = camelToSnake2(key);
372
+ const col = key.startsWith(CUSTOM_FIELD_FILTER_PREFIX) ? `${CUSTOM_FIELDS_COLUMN}->>${key.slice(CUSTOM_FIELD_FILTER_PREFIX.length)}` : camelToSnake(key);
664
373
  const range = parseRangeFilter(val);
665
374
  if (range) {
666
375
  q = applyRangeToQuery(q, col, range);
@@ -676,13 +385,13 @@ function createArchetypeProvider(config) {
676
385
  }
677
386
  if (query.search && config.searchColumns && config.searchColumns.length > 0) {
678
387
  const term = `%${query.search}%`;
679
- const orClauses = config.searchColumns.map((col) => camelToSnake2(col)).map((col) => `${col}.ilike.${term}`);
388
+ const orClauses = config.searchColumns.map((col) => camelToSnake(col)).map((col) => `${col}.ilike.${term}`);
680
389
  if (orClauses.length > 0) {
681
390
  q = q.or(orClauses.join(","));
682
391
  }
683
392
  }
684
393
  if (query.sortBy) {
685
- q = q.order(camelToSnake2(query.sortBy), { ascending: query.sortDir !== "desc" });
394
+ q = q.order(camelToSnake(query.sortBy), { ascending: query.sortDir !== "desc" });
686
395
  } else {
687
396
  q = q.order("created_at", { ascending: false });
688
397
  }
@@ -692,7 +401,7 @@ function createArchetypeProvider(config) {
692
401
  q = q.range(from, from + pageSize - 1);
693
402
  const { data, error, count } = await q;
694
403
  if (error) throw error;
695
- const rows = (data ?? []).map((row) => mapRow2(row));
404
+ const rows = (data ?? []).map((row) => mapRow(row));
696
405
  return { data: rows, total: count ?? 0 };
697
406
  },
698
407
  async create(data) {
@@ -729,16 +438,24 @@ function createArchetypeProvider(config) {
729
438
  }
730
439
  const { data: archetypeRow } = await coreClient().from(ac.table).select("*").eq("id", id).single();
731
440
  if (isPure) {
732
- return mapRow2({ ...archetypeRow, id });
441
+ return mapRow({ ...archetypeRow, id });
733
442
  }
734
443
  const { data: projectRow } = await getClient().from(config.projectTable).select("*").eq(fkColumn, id).single();
735
444
  const flat = { ...archetypeRow, ...projectRow, id };
445
+ if (archetypeRow && CUSTOM_FIELDS_COLUMN in archetypeRow) {
446
+ flat[CUSTOM_FIELDS_COLUMN] = archetypeRow[CUSTOM_FIELDS_COLUMN];
447
+ }
736
448
  delete flat[fkColumn];
737
- return mapRow2(flat);
449
+ return mapRow(flat);
738
450
  },
739
451
  async remove(id) {
740
452
  const { error } = await coreClient().from(ac.table).delete().eq("id", id);
741
453
  if (error) throw error;
454
+ },
455
+ async removeMany(ids) {
456
+ if (!ids.length) return;
457
+ const { error } = await coreClient().from(ac.table).delete().in("id", ids);
458
+ if (error) throw error;
742
459
  }
743
460
  };
744
461
  }
@@ -762,6 +479,16 @@ function withCache(provider, options) {
762
479
  globalCache.invalidate(prefix());
763
480
  return result;
764
481
  },
482
+ // Forwarded, not dropped: an optional method that disappears behind the
483
+ // wrapper silently downgrades every bulk write to a loop of single writes,
484
+ // and nothing upstream can tell — `createMany` was doing exactly that.
485
+ ...provider.createMany && {
486
+ async createMany(rows, options2) {
487
+ const result = await provider.createMany(rows, options2);
488
+ globalCache.invalidate(prefix());
489
+ return result;
490
+ }
491
+ },
765
492
  async update(id, data) {
766
493
  const result = await provider.update(id, data);
767
494
  globalCache.invalidate(prefix());
@@ -770,6 +497,126 @@ function withCache(provider, options) {
770
497
  async remove(id) {
771
498
  await provider.remove(id);
772
499
  globalCache.invalidate(prefix());
500
+ },
501
+ ...provider.removeMany && {
502
+ async removeMany(ids) {
503
+ await provider.removeMany(ids);
504
+ globalCache.invalidate(prefix());
505
+ }
506
+ }
507
+ };
508
+ }
509
+
510
+ // src/fields/store.ts
511
+ var FIELDS_TABLE = "plg_entity_fields";
512
+ var CustomFieldsError = class extends Error {
513
+ constructor(reference, issues) {
514
+ super(`${reference} custom fields: ${issues.map((i) => i.message).join("; ")}`);
515
+ this.name = "CustomFieldsError";
516
+ this.issues = issues;
517
+ }
518
+ };
519
+ function readOptions(raw) {
520
+ if (!Array.isArray(raw)) return void 0;
521
+ const options = raw.map((entry) => {
522
+ if (typeof entry === "string") return { value: entry };
523
+ const record = entry;
524
+ return typeof record?.value === "string" ? { value: record.value, label: typeof record.label === "string" ? record.label : void 0 } : null;
525
+ }).filter((option) => option !== null);
526
+ return options.length ? options : void 0;
527
+ }
528
+ function rowToField(row) {
529
+ const field = { type: row.type };
530
+ if (row.label) field.label = row.label;
531
+ if (row.help) field.help = row.help;
532
+ if (row.required) field.required = true;
533
+ if (row.default_value !== null && row.default_value !== void 0) field.default = row.default_value;
534
+ const options = readOptions(row.options);
535
+ if (options) field.options = options;
536
+ if (row.validation && Object.keys(row.validation).length) {
537
+ field.validation = row.validation;
538
+ }
539
+ return field;
540
+ }
541
+ var EMPTY = { reference: "", fields: {}, order: [] };
542
+ var cache2 = /* @__PURE__ */ new Map();
543
+ function clearCustomFieldsCache() {
544
+ cache2.clear();
545
+ }
546
+ async function load(reference, tenantId) {
547
+ const declared = declaredCustomFields(reference);
548
+ const supabase = getSupabaseClientOptional();
549
+ if (!supabase) return { reference, fields: { ...declared }, order: Object.keys(declared) };
550
+ const { data, error } = await supabase.from(FIELDS_TABLE).select("reference,key,type,label,help,required,default_value,validation,options,position,origin").eq("reference", reference).is("archived_at", null).order("position", { ascending: true });
551
+ if (error) return { reference, fields: { ...declared }, order: Object.keys(declared) };
552
+ const fields = {};
553
+ const order = [];
554
+ for (const row of data ?? []) {
555
+ if (fields[row.key]) continue;
556
+ fields[row.key] = rowToField(row);
557
+ order.push(row.key);
558
+ }
559
+ for (const [key, field] of Object.entries(declared)) {
560
+ if (!fields[key]) order.push(key);
561
+ fields[key] = field;
562
+ }
563
+ return { reference, fields, order };
564
+ }
565
+ function resolveCustomFields(reference, tenantId) {
566
+ const tenant = tenantId ?? getActiveTenantId();
567
+ if (!tenant) return Promise.resolve({ ...EMPTY, reference, fields: { ...declaredCustomFields(reference) } });
568
+ const cacheKey2 = `${tenant}:${reference}`;
569
+ let pending = cache2.get(cacheKey2);
570
+ if (!pending) {
571
+ pending = load(reference).catch((err) => {
572
+ cache2.delete(cacheKey2);
573
+ throw err;
574
+ });
575
+ cache2.set(cacheKey2, pending);
576
+ }
577
+ return pending;
578
+ }
579
+ async function validateCustomFields(reference, values, tenantId) {
580
+ const { fields } = await resolveCustomFields(reference, tenantId);
581
+ const issues = validateEntityRecordData({ fields }, values);
582
+ if (issues.length) throw new CustomFieldsError(reference, issues);
583
+ }
584
+ function mergeCustomFields(current, patch) {
585
+ const merged = { ...current ?? {} };
586
+ for (const [key, value] of Object.entries(patch)) {
587
+ if (value === null) delete merged[key];
588
+ else merged[key] = value;
589
+ }
590
+ return merged;
591
+ }
592
+
593
+ // src/data/custom-fields.ts
594
+ var KEY = "customFields";
595
+ function patchOf(data) {
596
+ const value = data?.[KEY];
597
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
598
+ }
599
+ function withCustomFields(provider, reference) {
600
+ return {
601
+ ...provider,
602
+ async create(data) {
603
+ const patch = patchOf(data);
604
+ if (!patch) return provider.create(data);
605
+ await validateCustomFields(reference, patch);
606
+ return provider.create(data);
607
+ },
608
+ async update(id, data) {
609
+ const patch = patchOf(data);
610
+ if (!patch) return provider.update(id, data);
611
+ await validateCustomFields(reference, patch);
612
+ const { data: rows } = await provider.list({
613
+ filters: { id },
614
+ pageSize: 1,
615
+ countMode: "none"
616
+ });
617
+ const current = rows[0]?.[KEY];
618
+ const merged = mergeCustomFields(current, patch);
619
+ return provider.update(id, { ...data, [KEY]: merged });
773
620
  }
774
621
  };
775
622
  }
@@ -813,13 +660,13 @@ function resolveDataProvider(entityDef, mockData, options = {}) {
813
660
  };
814
661
  const cached = (p) => options.cache === false ? p : withCache(p, cacheOptions);
815
662
  if (entityDef.data.archetype && entityDef.data.archetypeKind && !entityDef.data.schema) {
816
- return cached(createArchetypeProvider({
663
+ return cached(withCustomFields(createArchetypeProvider({
817
664
  archetype: entityDef.data.archetype,
818
665
  archetypeKind: entityDef.data.archetypeKind,
819
666
  projectTable: entityDef.data.table,
820
667
  tenantId,
821
668
  searchColumns: entityDef.data.searchColumns ?? entityDef.fields.filter((field) => field.searchable).map((field) => field.key)
822
- }));
669
+ }), entityDef.data.archetype));
823
670
  }
824
671
  return cached(createSupabaseProvider(entityDef.data.table, {
825
672
  schema: entityDef.data.schema,
@@ -857,7 +704,36 @@ async function bulkCreate(provider, rows, options) {
857
704
  }
858
705
  return { created, failed };
859
706
  }
707
+ async function bulkRemove(provider, ids, options) {
708
+ if (!ids.length) return { removed: 0, failed: [] };
709
+ const failed = [];
710
+ const message = (error) => error instanceof Error ? error.message : String(error);
711
+ if (provider.removeMany) {
712
+ const chunkSize = options?.chunkSize ?? 100;
713
+ let removed2 = 0;
714
+ for (let i = 0; i < ids.length; i += chunkSize) {
715
+ const chunk = ids.slice(i, i + chunkSize);
716
+ try {
717
+ await provider.removeMany(chunk);
718
+ removed2 += chunk.length;
719
+ } catch (error) {
720
+ for (const id of chunk) failed.push({ id, message: message(error) });
721
+ }
722
+ }
723
+ return { removed: removed2, failed };
724
+ }
725
+ let removed = 0;
726
+ for (const id of ids) {
727
+ try {
728
+ await provider.remove(id);
729
+ removed += 1;
730
+ } catch (error) {
731
+ failed.push({ id, message: message(error) });
732
+ }
733
+ }
734
+ return { removed, failed };
735
+ }
860
736
 
861
- export { applyRangeToQuery, bulkCreate, clearGlobalCache, countByTenant, createArchetypeProvider, createCacheStore, createFayzApiProvider, createMockProvider, createSupabaseProvider, getActiveTenantId, getSupabaseClientOptional, globalCache, invalidateCount, isFilterObject, matchesRange, parseRangeFilter, resolveDataProvider, setActiveTenantId, setGlobalSupabaseClient, stableKey, subscribeActiveTenant, useActiveTenantId, withCache };
862
- //# sourceMappingURL=chunk-JO7Z2AOE.js.map
863
- //# sourceMappingURL=chunk-JO7Z2AOE.js.map
737
+ export { CustomFieldsError, FIELDS_TABLE, bulkCreate, bulkRemove, clearCustomFieldsCache, countByTenant, createArchetypeProvider, createFayzApiProvider, createMockProvider, invalidateCount, mergeCustomFields, resolveCustomFields, resolveDataProvider, validateCustomFields, withCache };
738
+ //# sourceMappingURL=chunk-UVKH6RDX.js.map
739
+ //# sourceMappingURL=chunk-UVKH6RDX.js.map