@fayz-ai/core 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access/denial.d.ts +21 -0
- package/dist/access/denial.d.ts.map +1 -0
- package/dist/access/engine.d.ts +63 -0
- package/dist/access/engine.d.ts.map +1 -0
- package/dist/access/index.cjs +60 -0
- package/dist/access/index.cjs.map +1 -0
- package/dist/access/index.d.ts +6 -0
- package/dist/access/index.d.ts.map +1 -0
- package/dist/access/index.js +51 -0
- package/dist/access/index.js.map +1 -0
- package/dist/access/limits.d.ts +10 -0
- package/dist/access/limits.d.ts.map +1 -0
- package/dist/address/index.d.ts +52 -0
- package/dist/address/index.d.ts.map +1 -0
- package/dist/{chunk-QR7U2URY.cjs → chunk-CATASHPK.cjs} +345 -4
- package/dist/chunk-CATASHPK.cjs.map +1 -0
- package/dist/{chunk-WKZVSKEU.js → chunk-CU64QI4A.js} +2 -2
- package/dist/chunk-CU64QI4A.js.map +1 -0
- package/dist/{chunk-KNIVT6XZ.js → chunk-DZALZ6Q6.js} +345 -4
- package/dist/chunk-DZALZ6Q6.js.map +1 -0
- package/dist/{chunk-54SRL7AJ.cjs → chunk-KSI2L3E4.cjs} +145 -86
- package/dist/chunk-KSI2L3E4.cjs.map +1 -0
- package/dist/{chunk-ZBIWYDAR.js → chunk-TYPVSWLG.js} +144 -87
- package/dist/chunk-TYPVSWLG.js.map +1 -0
- package/dist/{chunk-2NTHNWEH.cjs → chunk-UKU4RIWM.cjs} +2 -2
- package/dist/chunk-UKU4RIWM.cjs.map +1 -0
- package/dist/{chunk-CHGX6UYR.js → chunk-YH25Y4FW.js} +6 -3
- package/dist/chunk-YH25Y4FW.js.map +1 -0
- package/dist/{chunk-5X2VX3RQ.cjs → chunk-ZOPJB5FN.cjs} +6 -3
- package/dist/chunk-ZOPJB5FN.cjs.map +1 -0
- package/dist/data/archetype.d.ts.map +1 -1
- package/dist/data/count.d.ts +34 -0
- package/dist/data/count.d.ts.map +1 -0
- package/dist/data/index.cjs +18 -10
- package/dist/data/index.d.ts +2 -0
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +1 -1
- package/dist/data/refresh.d.ts +36 -0
- package/dist/data/refresh.d.ts.map +1 -0
- package/dist/data/resolve.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/data/types.d.ts +6 -0
- package/dist/data/types.d.ts.map +1 -1
- package/dist/entity/index.cjs +6 -6
- package/dist/entity/index.js +1 -1
- package/dist/entity/registry.d.ts +9 -0
- package/dist/entity/registry.d.ts.map +1 -1
- package/dist/i18n/index.cjs +10 -10
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/shell-translations.d.ts.map +1 -1
- package/dist/index.cjs +1517 -180
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +15 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1412 -110
- package/dist/index.js.map +1 -1
- package/dist/manifest/index.d.ts +109 -3
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/serialize.d.ts +14 -0
- package/dist/manifest/serialize.d.ts.map +1 -0
- package/dist/plugin/index.cjs +7 -7
- package/dist/plugin/index.js +1 -1
- package/dist/plugin/runtime.d.ts.map +1 -1
- package/dist/search/engine.d.ts +12 -0
- package/dist/search/engine.d.ts.map +1 -0
- package/dist/search/index.d.ts +5 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/text.d.ts +37 -0
- package/dist/search/text.d.ts.map +1 -0
- package/dist/search/types.d.ts +72 -0
- package/dist/search/types.d.ts.map +1 -0
- package/dist/types/billing.d.ts +20 -0
- package/dist/types/billing.d.ts.map +1 -1
- package/dist/types/crud.d.ts +47 -1
- package/dist/types/crud.d.ts.map +1 -1
- package/dist/types/entitlements.d.ts +45 -0
- package/dist/types/entitlements.d.ts.map +1 -0
- package/dist/types/index.d.ts +4 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/org.d.ts +28 -0
- package/dist/types/org.d.ts.map +1 -1
- package/dist/types/plugins.d.ts +88 -0
- package/dist/types/plugins.d.ts.map +1 -1
- package/package.json +14 -4
- package/src/access/denial.ts +33 -0
- package/src/access/engine.test.ts +48 -0
- package/src/access/engine.ts +121 -0
- package/src/access/index.ts +13 -0
- package/src/access/limits.ts +18 -0
- package/src/address/index.ts +188 -0
- package/src/data/archetype.ts +4 -2
- package/src/data/count.test.ts +152 -0
- package/src/data/count.ts +131 -0
- package/src/data/index.ts +2 -0
- package/src/data/refresh.ts +105 -0
- package/src/data/resolve.ts +7 -1
- package/src/data/supabase.ts +3 -1
- package/src/data/types.ts +6 -0
- package/src/entity/registry.ts +7 -0
- package/src/i18n/shell-translations.ts +345 -2
- package/src/index.ts +37 -4
- package/src/manifest/app-manifest.schema.json +618 -45
- package/src/manifest/index.ts +127 -3
- package/src/manifest/serialize.ts +52 -0
- package/src/plugin/runtime.ts +5 -1
- package/src/search/engine.ts +551 -0
- package/src/search/index.ts +25 -0
- package/src/search/text.ts +206 -0
- package/src/search/types.ts +75 -0
- package/src/types/billing.ts +22 -0
- package/src/types/crud.ts +48 -1
- package/src/types/entitlements.ts +45 -0
- package/src/types/index.ts +4 -3
- package/src/types/org.ts +29 -0
- package/src/types/plugins.ts +83 -0
- package/dist/chunk-2NTHNWEH.cjs.map +0 -1
- package/dist/chunk-54SRL7AJ.cjs.map +0 -1
- package/dist/chunk-5X2VX3RQ.cjs.map +0 -1
- package/dist/chunk-CHGX6UYR.js.map +0 -1
- package/dist/chunk-KNIVT6XZ.js.map +0 -1
- package/dist/chunk-QR7U2URY.cjs.map +0 -1
- package/dist/chunk-WKZVSKEU.js.map +0 -1
- package/dist/chunk-ZBIWYDAR.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export { withAfterHooks } from './chunk-BBDNC3FP.js';
|
|
2
2
|
export { PluginContractError, assertConnectorContract, assertPluginManifestContract } from './chunk-X5THHG6T.js';
|
|
3
|
-
import { getSupabaseClientOptional } from './chunk-
|
|
4
|
-
export { FAYZ_CLOUD_PUBLISHABLE_KEY, FAYZ_CLOUD_URL, clearGlobalCache, createArchetypeProvider, createCacheStore, createFayzApiProvider, createMockProvider, createSupabaseProvider, getActiveTenantId, getFayzCloudClient, getSupabaseClientOptional, globalCache, resolveDataProvider, setActiveTenantId, setGlobalSupabaseClient, stableKey, withCache } from './chunk-
|
|
5
|
-
export { clearEntityRegistry, deriveEntityKey, getAllEntities, getEntityByKey, registerEntity } from './chunk-
|
|
6
|
-
import { getBlock, getScaffold } from './chunk-
|
|
7
|
-
export { PLUGIN_API_VERSION, PluginRuntimeProvider, Registry, blockRegistry, componentRegistry, definePlugin, getBlock, getComponent, getDashboardWidgets, getMetric, getPage, getPlugin, getPluginFactory, getScaffold, getWidgetsForZone, isCustomId, listBlocks, listMetrics, listPluginFactories, listPlugins, metricRegistry, pageRegistry, pluginFactoryRegistry, pluginRegistry, registerBlock, registerComponent, registerMetric, registerPage, registerPlugin, registerPluginFactory, registerScaffold, resolvePluginComponent, resolvePluginRuntime, scaffoldRegistry, useConnectorsForPlugin, usePluginRuntime, usePluginRuntimeOptional } from './chunk-
|
|
8
|
-
import { getCurrentLocale } from './chunk-
|
|
9
|
-
export { I18nProvider, coreTranslations, getCurrentLocale, mergeTranslations, registerTranslations, setCurrentLocale, setLocaleStore, useI18nConfig, useTranslation } from './chunk-
|
|
3
|
+
import { globalCache, getActiveTenantId, getSupabaseClientOptional, resolveDataProvider } from './chunk-TYPVSWLG.js';
|
|
4
|
+
export { FAYZ_CLOUD_PUBLISHABLE_KEY, FAYZ_CLOUD_URL, clearGlobalCache, countByTenant, createArchetypeProvider, createCacheStore, createFayzApiProvider, createMockProvider, createSupabaseProvider, getActiveTenantId, getFayzCloudClient, getSupabaseClientOptional, globalCache, invalidateCount, resolveDataProvider, setActiveTenantId, setGlobalSupabaseClient, stableKey, withCache } from './chunk-TYPVSWLG.js';
|
|
5
|
+
export { clearEntityRegistry, deriveEntityKey, getAllEntities, getEntityByKey, registerEntity } from './chunk-CU64QI4A.js';
|
|
6
|
+
import { getBlock, getScaffold } from './chunk-YH25Y4FW.js';
|
|
7
|
+
export { PLUGIN_API_VERSION, PluginRuntimeProvider, Registry, blockRegistry, componentRegistry, definePlugin, getBlock, getComponent, getDashboardWidgets, getMetric, getPage, getPlugin, getPluginFactory, getScaffold, getWidgetsForZone, isCustomId, listBlocks, listMetrics, listPluginFactories, listPlugins, metricRegistry, pageRegistry, pluginFactoryRegistry, pluginRegistry, registerBlock, registerComponent, registerMetric, registerPage, registerPlugin, registerPluginFactory, registerScaffold, resolvePluginComponent, resolvePluginRuntime, scaffoldRegistry, useConnectorsForPlugin, usePluginRuntime, usePluginRuntimeOptional } from './chunk-YH25Y4FW.js';
|
|
8
|
+
import { getCurrentLocale } from './chunk-DZALZ6Q6.js';
|
|
9
|
+
export { I18nProvider, coreTranslations, getCurrentLocale, mergeTranslations, registerTranslations, setCurrentLocale, setLocaleStore, useI18nConfig, useTranslation } from './chunk-DZALZ6Q6.js';
|
|
10
10
|
export { hashRouterAdapter, windowRouterAdapter } from './chunk-Y2C2FBQP.js';
|
|
11
11
|
export { FayzRuntimeError, createFayzRuntimeClient } from './chunk-7RTUHSKY.js';
|
|
12
|
-
import * as
|
|
12
|
+
import * as React3 from 'react';
|
|
13
13
|
import { useEffect } from 'react';
|
|
14
14
|
import { create } from 'zustand';
|
|
15
15
|
|
|
@@ -25,6 +25,97 @@ var WidgetZone = {
|
|
|
25
25
|
SETTINGS_AFTER: "settings.after",
|
|
26
26
|
FLOATING: "shell.floating"
|
|
27
27
|
};
|
|
28
|
+
function createEventBus() {
|
|
29
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
30
|
+
const bus = {
|
|
31
|
+
emit(event, payload) {
|
|
32
|
+
const set = handlers.get(event);
|
|
33
|
+
if (!set) return;
|
|
34
|
+
for (const handler of Array.from(set)) {
|
|
35
|
+
try {
|
|
36
|
+
handler(payload);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.error(`[fayz] event handler for "${event}" threw:`, err);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
on(event, handler) {
|
|
43
|
+
let set = handlers.get(event);
|
|
44
|
+
if (!set) {
|
|
45
|
+
set = /* @__PURE__ */ new Set();
|
|
46
|
+
handlers.set(event, set);
|
|
47
|
+
}
|
|
48
|
+
set.add(handler);
|
|
49
|
+
return () => bus.off(event, handler);
|
|
50
|
+
},
|
|
51
|
+
once(event, handler) {
|
|
52
|
+
const wrapped = (payload) => {
|
|
53
|
+
bus.off(event, wrapped);
|
|
54
|
+
handler(payload);
|
|
55
|
+
};
|
|
56
|
+
return bus.on(event, wrapped);
|
|
57
|
+
},
|
|
58
|
+
off(event, handler) {
|
|
59
|
+
handlers.get(event)?.delete(handler);
|
|
60
|
+
},
|
|
61
|
+
clear(event) {
|
|
62
|
+
if (event) handlers.delete(event);
|
|
63
|
+
else handlers.clear();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
return bus;
|
|
67
|
+
}
|
|
68
|
+
var eventBus = createEventBus();
|
|
69
|
+
function useOnEvent(event, handler, deps = []) {
|
|
70
|
+
const ref = React3.useRef(handler);
|
|
71
|
+
ref.current = handler;
|
|
72
|
+
React3.useEffect(
|
|
73
|
+
() => eventBus.on(event, (payload) => ref.current(payload)),
|
|
74
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
75
|
+
[event, ...deps]
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/data/refresh.ts
|
|
80
|
+
var DATA_CHANGED_EVENT = "data:changed";
|
|
81
|
+
function matchesDataChange(match, payload) {
|
|
82
|
+
const attributed = payload.table ?? payload.entityKey ?? payload.archetype;
|
|
83
|
+
if (!attributed) return true;
|
|
84
|
+
if (match.table && payload.table && match.table === payload.table) return true;
|
|
85
|
+
if (match.entityKey && payload.entityKey && match.entityKey === payload.entityKey) return true;
|
|
86
|
+
if (match.archetype && payload.archetype && match.archetype === payload.archetype) return true;
|
|
87
|
+
return !match.table && !match.entityKey && !match.archetype;
|
|
88
|
+
}
|
|
89
|
+
var pending = [];
|
|
90
|
+
var flushTimer = null;
|
|
91
|
+
function flush() {
|
|
92
|
+
const batch = pending;
|
|
93
|
+
pending = [];
|
|
94
|
+
flushTimer = null;
|
|
95
|
+
for (const payload of batch) eventBus.emit(DATA_CHANGED_EVENT, payload);
|
|
96
|
+
}
|
|
97
|
+
function emitDataChanged(payload = {}) {
|
|
98
|
+
if (payload.table) {
|
|
99
|
+
globalCache.invalidate(`${getActiveTenantId() ?? "_"}:${payload.table}`);
|
|
100
|
+
} else {
|
|
101
|
+
globalCache.clear();
|
|
102
|
+
}
|
|
103
|
+
pending.push({ op: "unknown", source: "agent", ...payload });
|
|
104
|
+
if (flushTimer) return;
|
|
105
|
+
flushTimer = setTimeout(flush, 120);
|
|
106
|
+
}
|
|
107
|
+
function useDataChanged(match, handler, deps = []) {
|
|
108
|
+
const handlerRef = React3.useRef(handler);
|
|
109
|
+
handlerRef.current = handler;
|
|
110
|
+
const { table, entityKey, archetype } = match;
|
|
111
|
+
React3.useEffect(
|
|
112
|
+
() => eventBus.on(DATA_CHANGED_EVENT, (payload) => {
|
|
113
|
+
if (matchesDataChange({ table, entityKey, archetype }, payload)) handlerRef.current(payload);
|
|
114
|
+
}),
|
|
115
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
116
|
+
[table, entityKey, archetype, ...deps]
|
|
117
|
+
);
|
|
118
|
+
}
|
|
28
119
|
|
|
29
120
|
// src/plugin/createSafeDataProvider.ts
|
|
30
121
|
function createSafeDataProvider(makeSupabase, makeMock) {
|
|
@@ -84,6 +175,92 @@ function maskDigitCount(mask) {
|
|
|
84
175
|
for (const ch of mask) if (ch === "#") n++;
|
|
85
176
|
return n;
|
|
86
177
|
}
|
|
178
|
+
|
|
179
|
+
// src/address/index.ts
|
|
180
|
+
function normalizePostalCode(value) {
|
|
181
|
+
return (value || "").replace(/\D/g, "");
|
|
182
|
+
}
|
|
183
|
+
function formatPostalCode(value) {
|
|
184
|
+
const digits = normalizePostalCode(value).slice(0, 8);
|
|
185
|
+
return digits.length > 5 ? `${digits.slice(0, 5)}-${digits.slice(5)}` : digits;
|
|
186
|
+
}
|
|
187
|
+
function isValidPostalCode(value) {
|
|
188
|
+
return normalizePostalCode(value).length === 8;
|
|
189
|
+
}
|
|
190
|
+
var CACHE_KEY = "fayz.postal.cache.v1";
|
|
191
|
+
var memoryCache = /* @__PURE__ */ new Map();
|
|
192
|
+
function readCache(code) {
|
|
193
|
+
const hit = memoryCache.get(code);
|
|
194
|
+
if (hit) return hit;
|
|
195
|
+
try {
|
|
196
|
+
const raw = globalThis.localStorage?.getItem(CACHE_KEY);
|
|
197
|
+
if (!raw) return void 0;
|
|
198
|
+
const all = JSON.parse(raw);
|
|
199
|
+
const stored = all[code];
|
|
200
|
+
if (stored) memoryCache.set(code, stored);
|
|
201
|
+
return stored;
|
|
202
|
+
} catch {
|
|
203
|
+
return void 0;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function writeCache(code, address) {
|
|
207
|
+
memoryCache.set(code, address);
|
|
208
|
+
try {
|
|
209
|
+
const store = globalThis.localStorage;
|
|
210
|
+
if (!store) return;
|
|
211
|
+
const raw = store.getItem(CACHE_KEY);
|
|
212
|
+
const all = raw ? JSON.parse(raw) : {};
|
|
213
|
+
all[code] = address;
|
|
214
|
+
store.setItem(CACHE_KEY, JSON.stringify(all));
|
|
215
|
+
} catch {
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function clearPostalCache() {
|
|
219
|
+
memoryCache.clear();
|
|
220
|
+
try {
|
|
221
|
+
globalThis.localStorage?.removeItem(CACHE_KEY);
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function createViaCepProvider(options) {
|
|
226
|
+
const fetcher = options?.fetcher ?? globalThis.fetch;
|
|
227
|
+
return {
|
|
228
|
+
async lookup(postalCode) {
|
|
229
|
+
const code = normalizePostalCode(postalCode);
|
|
230
|
+
if (code.length !== 8) return null;
|
|
231
|
+
const response = await fetcher(`https://viacep.com.br/ws/${code}/json/`);
|
|
232
|
+
if (!response.ok) throw new Error(`Consulta de CEP falhou (${response.status})`);
|
|
233
|
+
const data = await response.json();
|
|
234
|
+
if (data.erro === true || data.erro === "true" || !data.localidade) return null;
|
|
235
|
+
return {
|
|
236
|
+
postalCode: code,
|
|
237
|
+
street: data.logradouro ?? "",
|
|
238
|
+
district: data.bairro ?? "",
|
|
239
|
+
city: data.localidade,
|
|
240
|
+
state: (data.uf ?? "").toUpperCase(),
|
|
241
|
+
country: "BR",
|
|
242
|
+
cityCode: data.ibge || void 0
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
var _provider = null;
|
|
248
|
+
function setPostalLookupProvider(provider) {
|
|
249
|
+
_provider = provider;
|
|
250
|
+
}
|
|
251
|
+
function getPostalLookupProvider() {
|
|
252
|
+
if (!_provider) _provider = createViaCepProvider();
|
|
253
|
+
return _provider;
|
|
254
|
+
}
|
|
255
|
+
async function lookupPostalCode(postalCode) {
|
|
256
|
+
const code = normalizePostalCode(postalCode);
|
|
257
|
+
if (code.length !== 8) return null;
|
|
258
|
+
const cached = readCache(code);
|
|
259
|
+
if (cached) return cached;
|
|
260
|
+
const address = await getPostalLookupProvider().lookup(code);
|
|
261
|
+
if (address) writeCache(code, address);
|
|
262
|
+
return address;
|
|
263
|
+
}
|
|
87
264
|
var MANAGED = "data-fayz-seo";
|
|
88
265
|
function seoOrigin() {
|
|
89
266
|
return typeof window !== "undefined" ? window.location.origin : "";
|
|
@@ -245,58 +422,560 @@ function downloadCSV(csv, filename) {
|
|
|
245
422
|
function exportCSV(columns, rows, filename) {
|
|
246
423
|
downloadCSV(buildCSV(columns, rows), filename);
|
|
247
424
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
425
|
+
|
|
426
|
+
// src/search/text.ts
|
|
427
|
+
var COMBINING_MARKS = /[\u0300-\u036f]/g;
|
|
428
|
+
function foldText(value) {
|
|
429
|
+
if (typeof value !== "string" || value === "") return "";
|
|
430
|
+
return value.normalize("NFD").replace(COMBINING_MARKS, "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
431
|
+
}
|
|
432
|
+
function foldWithMap(value) {
|
|
433
|
+
const out = [];
|
|
434
|
+
const map = [];
|
|
435
|
+
let pendingSpace = false;
|
|
436
|
+
for (let i = 0; i < value.length; i++) {
|
|
437
|
+
const ch = value[i];
|
|
438
|
+
const folded = ch.normalize("NFD").replace(COMBINING_MARKS, "").toLowerCase();
|
|
439
|
+
for (const f of folded) {
|
|
440
|
+
const alnum = f >= "a" && f <= "z" || f >= "0" && f <= "9";
|
|
441
|
+
if (!alnum) {
|
|
442
|
+
pendingSpace = out.length > 0;
|
|
443
|
+
continue;
|
|
260
444
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
set = /* @__PURE__ */ new Set();
|
|
266
|
-
handlers.set(event, set);
|
|
445
|
+
if (pendingSpace) {
|
|
446
|
+
out.push(" ");
|
|
447
|
+
map.push(i);
|
|
448
|
+
pendingSpace = false;
|
|
267
449
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
},
|
|
271
|
-
once(event, handler) {
|
|
272
|
-
const wrapped = (payload) => {
|
|
273
|
-
bus.off(event, wrapped);
|
|
274
|
-
handler(payload);
|
|
275
|
-
};
|
|
276
|
-
return bus.on(event, wrapped);
|
|
277
|
-
},
|
|
278
|
-
off(event, handler) {
|
|
279
|
-
handlers.get(event)?.delete(handler);
|
|
280
|
-
},
|
|
281
|
-
clear(event) {
|
|
282
|
-
if (event) handlers.delete(event);
|
|
283
|
-
else handlers.clear();
|
|
450
|
+
out.push(f);
|
|
451
|
+
map.push(i);
|
|
284
452
|
}
|
|
453
|
+
}
|
|
454
|
+
return { folded: out.join(""), map };
|
|
455
|
+
}
|
|
456
|
+
function digitsOf(value) {
|
|
457
|
+
return typeof value === "string" ? value.replace(/\D+/g, "") : "";
|
|
458
|
+
}
|
|
459
|
+
var MIN_DIGIT_QUERY = 4;
|
|
460
|
+
function normalizeQuery(raw) {
|
|
461
|
+
const folded = foldText(raw);
|
|
462
|
+
const tokens = folded ? folded.split(" ").filter(Boolean) : [];
|
|
463
|
+
const digits = raw.replace(/\D+/g, "");
|
|
464
|
+
let anchor = "";
|
|
465
|
+
for (const token of tokens) if (token.length > anchor.length) anchor = token;
|
|
466
|
+
return {
|
|
467
|
+
raw,
|
|
468
|
+
folded,
|
|
469
|
+
tokens,
|
|
470
|
+
digits: digits.length >= MIN_DIGIT_QUERY ? digits : "",
|
|
471
|
+
anchor
|
|
285
472
|
};
|
|
286
|
-
return bus;
|
|
287
473
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
);
|
|
474
|
+
function bigrams(value) {
|
|
475
|
+
if (value.length < 2) return value ? [value] : [];
|
|
476
|
+
const out = [];
|
|
477
|
+
for (let i = 0; i < value.length - 1; i++) out.push(value.slice(i, i + 2));
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
480
|
+
function similarity(a, b) {
|
|
481
|
+
if (!a || !b) return 0;
|
|
482
|
+
if (a === b) return 1;
|
|
483
|
+
const left = bigrams(a);
|
|
484
|
+
const right = bigrams(b);
|
|
485
|
+
if (left.length === 0 || right.length === 0) return 0;
|
|
486
|
+
const pool = /* @__PURE__ */ new Map();
|
|
487
|
+
for (const g of left) pool.set(g, (pool.get(g) ?? 0) + 1);
|
|
488
|
+
let shared = 0;
|
|
489
|
+
for (const g of right) {
|
|
490
|
+
const count = pool.get(g);
|
|
491
|
+
if (count) {
|
|
492
|
+
shared++;
|
|
493
|
+
pool.set(g, count - 1);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return 2 * shared / (left.length + right.length);
|
|
497
|
+
}
|
|
498
|
+
function wordSimilarity(text, token) {
|
|
499
|
+
let best = similarity(text, token);
|
|
500
|
+
for (const word of text.split(" ")) {
|
|
501
|
+
if (!word) continue;
|
|
502
|
+
const s = similarity(word, token);
|
|
503
|
+
if (s > best) best = s;
|
|
504
|
+
}
|
|
505
|
+
return best;
|
|
506
|
+
}
|
|
507
|
+
var FUZZY_FLOOR = 0.62;
|
|
508
|
+
function scoreCandidate(query, candidate) {
|
|
509
|
+
const { folded, tokens } = query;
|
|
510
|
+
if (!folded || tokens.length === 0) return 0;
|
|
511
|
+
const title = candidate.title;
|
|
512
|
+
const haystack = candidate.haystack || title;
|
|
513
|
+
let phrase = 0;
|
|
514
|
+
if (title === folded) phrase = 1;
|
|
515
|
+
else if (title.startsWith(folded)) phrase = 0.94;
|
|
516
|
+
else if (title.includes(" " + folded)) phrase = 0.88;
|
|
517
|
+
else if (title.includes(folded)) phrase = 0.76;
|
|
518
|
+
else if (haystack.startsWith(folded) || haystack.includes(" " + folded)) phrase = 0.7;
|
|
519
|
+
else if (haystack.includes(folded)) phrase = 0.62;
|
|
520
|
+
let tokenTotal = 0;
|
|
521
|
+
let fuzzyUsed = false;
|
|
522
|
+
for (const token of tokens) {
|
|
523
|
+
let best = 0;
|
|
524
|
+
if (title.startsWith(token)) best = 0.95;
|
|
525
|
+
else if (title.includes(" " + token)) best = 0.85;
|
|
526
|
+
else if (title.includes(token)) best = 0.7;
|
|
527
|
+
else if (haystack.startsWith(token) || haystack.includes(" " + token)) best = 0.6;
|
|
528
|
+
else if (haystack.includes(token)) best = 0.5;
|
|
529
|
+
else {
|
|
530
|
+
const sim = wordSimilarity(title, token);
|
|
531
|
+
if (sim >= FUZZY_FLOOR) {
|
|
532
|
+
best = 0.45 * sim;
|
|
533
|
+
fuzzyUsed = true;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (best === 0) return phrase > 0 ? phrase : 0;
|
|
537
|
+
tokenTotal += best;
|
|
538
|
+
}
|
|
539
|
+
let tokenScore = tokenTotal / tokens.length;
|
|
540
|
+
if (fuzzyUsed) tokenScore *= 0.8;
|
|
541
|
+
let score = Math.max(phrase, tokenScore);
|
|
542
|
+
if (query.digits && candidate.digits && candidate.digits.includes(query.digits)) {
|
|
543
|
+
score = Math.max(score, 0.72 + Math.min(0.2, query.digits.length / 50));
|
|
544
|
+
}
|
|
545
|
+
if (score === 0) return 0;
|
|
546
|
+
const brevity = 1 / (1 + Math.max(0, title.length - folded.length) / 40);
|
|
547
|
+
return score * 0.97 + brevity * 0.03;
|
|
548
|
+
}
|
|
549
|
+
function highlightRanges(text, query) {
|
|
550
|
+
if (!text || query.tokens.length === 0) return [];
|
|
551
|
+
const { folded, map } = foldWithMap(text);
|
|
552
|
+
if (!folded) return [];
|
|
553
|
+
const spans = [];
|
|
554
|
+
const needles = query.tokens.length > 1 ? [query.folded, ...query.tokens] : [query.folded];
|
|
555
|
+
for (const needle of needles) {
|
|
556
|
+
if (!needle) continue;
|
|
557
|
+
let from = 0;
|
|
558
|
+
for (; ; ) {
|
|
559
|
+
const at = folded.indexOf(needle, from);
|
|
560
|
+
if (at === -1) break;
|
|
561
|
+
const start = map[at];
|
|
562
|
+
const endChar = map[at + needle.length - 1];
|
|
563
|
+
if (start !== void 0 && endChar !== void 0) spans.push([start, endChar + 1]);
|
|
564
|
+
from = at + needle.length;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
if (spans.length === 0) return [];
|
|
568
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
569
|
+
const merged = [spans[0]];
|
|
570
|
+
for (const span of spans.slice(1)) {
|
|
571
|
+
const last = merged[merged.length - 1];
|
|
572
|
+
if (span[0] <= last[1]) last[1] = Math.max(last[1], span[1]);
|
|
573
|
+
else merged.push(span);
|
|
574
|
+
}
|
|
575
|
+
return merged;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/search/engine.ts
|
|
579
|
+
var MIN_QUERY_LENGTH = 2;
|
|
580
|
+
var DEFAULT_LIMIT = 30;
|
|
581
|
+
var DEFAULT_PER_TARGET = 5;
|
|
582
|
+
var SCAN_CONCURRENCY = 16;
|
|
583
|
+
var TARGET_TIMEOUT_MS = 3e3;
|
|
584
|
+
var CACHE_TTL_MS = 3e4;
|
|
585
|
+
var CACHE_MAX_ENTRIES = 40;
|
|
586
|
+
var RPC_NAME = "fayz_global_search";
|
|
587
|
+
var indexAvailable = null;
|
|
588
|
+
function setSearchIndexAvailable(value) {
|
|
589
|
+
indexAvailable = value;
|
|
590
|
+
}
|
|
591
|
+
function isSearchIndexAvailable() {
|
|
592
|
+
return indexAvailable;
|
|
593
|
+
}
|
|
594
|
+
function camelOf(key) {
|
|
595
|
+
return key.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
596
|
+
}
|
|
597
|
+
function snakeOf(key) {
|
|
598
|
+
return key.replace(/[A-Z]/g, (c) => "_" + c.toLowerCase());
|
|
599
|
+
}
|
|
600
|
+
function readField(row, key) {
|
|
601
|
+
if (key in row) return row[key];
|
|
602
|
+
const camel = camelOf(key);
|
|
603
|
+
if (camel in row) return row[camel];
|
|
604
|
+
const snake = snakeOf(key);
|
|
605
|
+
if (snake in row) return row[snake];
|
|
606
|
+
return void 0;
|
|
607
|
+
}
|
|
608
|
+
function asText(value) {
|
|
609
|
+
if (typeof value === "string") return value;
|
|
610
|
+
if (typeof value === "number") return String(value);
|
|
611
|
+
if (Array.isArray(value)) return value.filter((v) => typeof v === "string").join(" ");
|
|
612
|
+
return "";
|
|
613
|
+
}
|
|
614
|
+
function searchableKeys(entity) {
|
|
615
|
+
const declared = entity.data?.searchColumns ?? entity.fields.filter((f) => f.searchable).map((f) => f.key);
|
|
616
|
+
const keys = [...declared];
|
|
617
|
+
const display = entity.displayField ?? "name";
|
|
618
|
+
if (!keys.includes(display)) keys.unshift(display);
|
|
619
|
+
const subtitle = entity.subtitleField;
|
|
620
|
+
if (subtitle && !keys.includes(subtitle)) keys.push(subtitle);
|
|
621
|
+
return keys;
|
|
622
|
+
}
|
|
623
|
+
var DIGIT_FIELD_TYPES = /* @__PURE__ */ new Set(["phone", "number"]);
|
|
624
|
+
function digitKeys(entity) {
|
|
625
|
+
const keys = entity.fields.filter((f) => DIGIT_FIELD_TYPES.has(f.type) || /phone|document|cpf|cnpj|zip|postal|sku|code|reference/i.test(f.key)).map((f) => f.key);
|
|
626
|
+
for (const extra of ["phone", "document_number", "sku", "reference_number", "postal_code"]) {
|
|
627
|
+
if (!keys.includes(extra)) keys.push(extra);
|
|
628
|
+
}
|
|
629
|
+
return keys;
|
|
630
|
+
}
|
|
631
|
+
function titleOf(entity, row) {
|
|
632
|
+
const display = entity.displayField ?? "name";
|
|
633
|
+
const primary = asText(readField(row, display));
|
|
634
|
+
if (primary) return primary;
|
|
635
|
+
for (const field of entity.fields) {
|
|
636
|
+
if (field.type === "text" || field.type === "email") {
|
|
637
|
+
const value = asText(readField(row, field.key));
|
|
638
|
+
if (value) return value;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return asText(row.id) || "\u2014";
|
|
642
|
+
}
|
|
643
|
+
function subtitleOf(entity, row, title) {
|
|
644
|
+
const distinct = (value) => {
|
|
645
|
+
if (!value || foldText(value) === foldText(title)) return void 0;
|
|
646
|
+
return value.length > 90 ? value.slice(0, 89) + "\u2026" : value;
|
|
647
|
+
};
|
|
648
|
+
const declared = entity.subtitleField;
|
|
649
|
+
if (declared) {
|
|
650
|
+
const value = distinct(asText(readField(row, declared)));
|
|
651
|
+
if (value) return value;
|
|
652
|
+
}
|
|
653
|
+
for (const key of ["email", "phone", "sku", "reference_number", "document_number", "description"]) {
|
|
654
|
+
const value = distinct(asText(readField(row, key)));
|
|
655
|
+
if (value) return value;
|
|
656
|
+
}
|
|
657
|
+
return void 0;
|
|
658
|
+
}
|
|
659
|
+
function hitFromRow(target, row, query) {
|
|
660
|
+
const id = asText(row.id);
|
|
661
|
+
if (!id) return null;
|
|
662
|
+
const entity = target.entity;
|
|
663
|
+
const title = titleOf(entity, row);
|
|
664
|
+
const parts = [title];
|
|
665
|
+
for (const key of searchableKeys(entity)) {
|
|
666
|
+
const value = asText(readField(row, key));
|
|
667
|
+
if (value) parts.push(value);
|
|
668
|
+
}
|
|
669
|
+
let digits = "";
|
|
670
|
+
for (const key of digitKeys(entity)) {
|
|
671
|
+
const value = readField(row, key);
|
|
672
|
+
if (typeof value === "string" || typeof value === "number") digits += digitsOf(String(value));
|
|
673
|
+
}
|
|
674
|
+
const score = scoreCandidate(query, {
|
|
675
|
+
title: foldText(title),
|
|
676
|
+
haystack: foldText(parts.join(" ")),
|
|
677
|
+
digits
|
|
678
|
+
});
|
|
679
|
+
if (score === 0) return null;
|
|
680
|
+
return {
|
|
681
|
+
uid: `${target.key}:${id}`,
|
|
682
|
+
id,
|
|
683
|
+
key: target.key,
|
|
684
|
+
group: target.label,
|
|
685
|
+
icon: target.icon ?? entity.icon,
|
|
686
|
+
title,
|
|
687
|
+
subtitle: subtitleOf(entity, row, title),
|
|
688
|
+
score: score * (target.boost ?? 1),
|
|
689
|
+
archetype: entity.data?.archetype,
|
|
690
|
+
archetypeKind: entity.data?.archetypeKind,
|
|
691
|
+
table: entity.data?.table,
|
|
692
|
+
record: row
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
696
|
+
function recordIdentity(hit) {
|
|
697
|
+
if (UUID.test(hit.id)) return hit.id;
|
|
698
|
+
return `${hit.table ?? hit.key}#${hit.id}`;
|
|
699
|
+
}
|
|
700
|
+
function assemble(query, hits, targets, opts) {
|
|
701
|
+
const seen = /* @__PURE__ */ new Set();
|
|
702
|
+
const unique = [];
|
|
703
|
+
for (const hit of hits.slice().sort((a, b) => b.score - a.score || a.title.localeCompare(b.title))) {
|
|
704
|
+
const identity = recordIdentity(hit);
|
|
705
|
+
if (seen.has(identity)) continue;
|
|
706
|
+
seen.add(identity);
|
|
707
|
+
unique.push(hit);
|
|
708
|
+
}
|
|
709
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
710
|
+
const kept = [];
|
|
711
|
+
const capped = /* @__PURE__ */ new Set();
|
|
712
|
+
for (const hit of unique) {
|
|
713
|
+
const bucket = byKey.get(hit.key) ?? [];
|
|
714
|
+
if (bucket.length >= opts.perTarget) {
|
|
715
|
+
capped.add(hit.key);
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
bucket.push(hit);
|
|
719
|
+
byKey.set(hit.key, bucket);
|
|
720
|
+
kept.push(hit);
|
|
721
|
+
}
|
|
722
|
+
const limited = kept.slice(0, opts.limit);
|
|
723
|
+
const order = new Map(targets.map((t, i) => [t.key, i]));
|
|
724
|
+
const groups = [];
|
|
725
|
+
for (const hit of limited) {
|
|
726
|
+
let group = groups.find((g) => g.key === hit.key);
|
|
727
|
+
if (!group) {
|
|
728
|
+
group = { key: hit.key, label: hit.group, icon: hit.icon, hits: [], hasMore: capped.has(hit.key) };
|
|
729
|
+
groups.push(group);
|
|
730
|
+
}
|
|
731
|
+
group.hits.push(hit);
|
|
732
|
+
}
|
|
733
|
+
groups.sort((a, b) => (b.hits[0]?.score ?? 0) - (a.hits[0]?.score ?? 0) || (order.get(a.key) ?? 99) - (order.get(b.key) ?? 99));
|
|
734
|
+
return {
|
|
735
|
+
query,
|
|
736
|
+
hits: limited,
|
|
737
|
+
groups,
|
|
738
|
+
via: opts.via,
|
|
739
|
+
failed: opts.failed,
|
|
740
|
+
elapsedMs: opts.elapsedMs,
|
|
741
|
+
partial: opts.partial ?? false
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
var cache = [];
|
|
745
|
+
function scopeKey(targets) {
|
|
746
|
+
return `${getActiveTenantId() ?? "_"}|${targets.map((t) => t.key).sort().join(",")}`;
|
|
747
|
+
}
|
|
748
|
+
function cacheGet(scope, folded) {
|
|
749
|
+
const now = Date.now();
|
|
750
|
+
return cache.find((e) => e.scope === scope && e.folded === folded && now - e.at < CACHE_TTL_MS);
|
|
751
|
+
}
|
|
752
|
+
function cachePrefix(scope, folded) {
|
|
753
|
+
const now = Date.now();
|
|
754
|
+
let best;
|
|
755
|
+
for (const entry of cache) {
|
|
756
|
+
if (entry.scope !== scope || !entry.complete) continue;
|
|
757
|
+
if (now - entry.at >= CACHE_TTL_MS) continue;
|
|
758
|
+
if (!folded.startsWith(entry.folded)) continue;
|
|
759
|
+
if (!best || entry.folded.length > best.folded.length) best = entry;
|
|
760
|
+
}
|
|
761
|
+
return best;
|
|
762
|
+
}
|
|
763
|
+
function cacheSet(entry) {
|
|
764
|
+
const at = cache.findIndex((e) => e.scope === entry.scope && e.folded === entry.folded);
|
|
765
|
+
if (at >= 0) cache.splice(at, 1);
|
|
766
|
+
cache.unshift(entry);
|
|
767
|
+
if (cache.length > CACHE_MAX_ENTRIES) cache.length = CACHE_MAX_ENTRIES;
|
|
768
|
+
}
|
|
769
|
+
function clearSearchCache() {
|
|
770
|
+
cache.length = 0;
|
|
771
|
+
}
|
|
772
|
+
function isMissingFunction(error) {
|
|
773
|
+
if (!error) return false;
|
|
774
|
+
if (error.code === "PGRST202") return true;
|
|
775
|
+
const message = (error.message ?? "").toLowerCase();
|
|
776
|
+
return message.includes(RPC_NAME) && (message.includes("does not exist") || message.includes("not find"));
|
|
777
|
+
}
|
|
778
|
+
async function searchViaIndex(query, targets, perTarget, limit) {
|
|
779
|
+
const client = getSupabaseClientOptional();
|
|
780
|
+
const tenantId = getActiveTenantId();
|
|
781
|
+
if (!client || !tenantId) return { hits: [], ok: false };
|
|
782
|
+
const byKey = new Map(targets.map((t) => [t.key, t]));
|
|
783
|
+
const { data, error } = await client.rpc(RPC_NAME, {
|
|
784
|
+
p_query: query.raw,
|
|
785
|
+
p_tenant_id: tenantId,
|
|
786
|
+
p_entity_keys: targets.map((t) => t.key),
|
|
787
|
+
p_per_source: perTarget * 3,
|
|
788
|
+
p_limit: limit * 3
|
|
789
|
+
});
|
|
790
|
+
if (error) {
|
|
791
|
+
if (isMissingFunction(error)) {
|
|
792
|
+
indexAvailable = false;
|
|
793
|
+
return { hits: [], ok: false };
|
|
794
|
+
}
|
|
795
|
+
throw error;
|
|
796
|
+
}
|
|
797
|
+
indexAvailable = true;
|
|
798
|
+
const rows = Array.isArray(data) ? data : [];
|
|
799
|
+
const hits = [];
|
|
800
|
+
for (const row of rows) {
|
|
801
|
+
const target = byKey.get(row.entity_key);
|
|
802
|
+
if (!target || !row.record_id) continue;
|
|
803
|
+
const title = row.title ?? "";
|
|
804
|
+
const local = scoreCandidate(query, {
|
|
805
|
+
title: foldText(title),
|
|
806
|
+
haystack: foldText(`${title} ${row.subtitle ?? ""}`),
|
|
807
|
+
digits: digitsOf(row.subtitle ?? "") + digitsOf(title)
|
|
808
|
+
});
|
|
809
|
+
const score = local > 0 ? local : Math.min(0.55, row.score ?? 0.4);
|
|
810
|
+
hits.push({
|
|
811
|
+
uid: `${target.key}:${row.record_id}`,
|
|
812
|
+
id: row.record_id,
|
|
813
|
+
key: target.key,
|
|
814
|
+
group: target.label,
|
|
815
|
+
icon: target.icon ?? target.entity.icon,
|
|
816
|
+
title: title || "\u2014",
|
|
817
|
+
subtitle: row.subtitle ?? void 0,
|
|
818
|
+
score: score * (target.boost ?? 1),
|
|
819
|
+
archetype: target.entity.data?.archetype,
|
|
820
|
+
archetypeKind: target.entity.data?.archetypeKind,
|
|
821
|
+
table: target.entity.data?.table
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
return { hits, ok: true };
|
|
825
|
+
}
|
|
826
|
+
async function withTimeout(promise, ms) {
|
|
827
|
+
let timer;
|
|
828
|
+
try {
|
|
829
|
+
return await Promise.race([
|
|
830
|
+
promise,
|
|
831
|
+
new Promise((_, reject) => {
|
|
832
|
+
timer = setTimeout(() => reject(new Error("search target timed out")), ms);
|
|
833
|
+
})
|
|
834
|
+
]);
|
|
835
|
+
} finally {
|
|
836
|
+
if (timer) clearTimeout(timer);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
async function mapLimited(items, limit, fn) {
|
|
840
|
+
const results = new Array(items.length);
|
|
841
|
+
let cursor = 0;
|
|
842
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
|
|
843
|
+
for (; ; ) {
|
|
844
|
+
const index = cursor++;
|
|
845
|
+
if (index >= items.length) return;
|
|
846
|
+
const item = items[index];
|
|
847
|
+
try {
|
|
848
|
+
results[index] = { ok: true, value: await fn(item) };
|
|
849
|
+
} catch {
|
|
850
|
+
results[index] = { ok: false, item };
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
await Promise.all(workers);
|
|
855
|
+
return results;
|
|
856
|
+
}
|
|
857
|
+
async function searchViaScan(query, targets, perTarget, onProgress) {
|
|
858
|
+
const multiWord = query.tokens.length > 1;
|
|
859
|
+
const phrase = query.folded;
|
|
860
|
+
const token = query.anchor.length >= MIN_QUERY_LENGTH ? query.anchor : phrase;
|
|
861
|
+
const pageSize = Math.min(perTarget * 3, 24);
|
|
862
|
+
const widePageSize = Math.min(perTarget * 6, 40);
|
|
863
|
+
const searchable = targets.filter((t) => searchableKeys(t.entity).length > 0);
|
|
864
|
+
const running = [];
|
|
865
|
+
const round = (search, size) => mapLimited(searchable, SCAN_CONCURRENCY, async (target) => {
|
|
866
|
+
const provider = resolveDataProvider(target.entity, target.mockData);
|
|
867
|
+
const result = await withTimeout(
|
|
868
|
+
provider.list({ search, pageSize: size, countMode: "none" }),
|
|
869
|
+
TARGET_TIMEOUT_MS
|
|
870
|
+
);
|
|
871
|
+
const rows = result.data;
|
|
872
|
+
const hits = rows.map((row) => hitFromRow(target, row, query)).filter((h) => h !== null);
|
|
873
|
+
if (hits.length && onProgress) {
|
|
874
|
+
running.push(...hits);
|
|
875
|
+
onProgress(running.slice());
|
|
876
|
+
}
|
|
877
|
+
return { target, hits };
|
|
878
|
+
});
|
|
879
|
+
const collect = (settled) => {
|
|
880
|
+
const hits = [];
|
|
881
|
+
const failed = [];
|
|
882
|
+
for (const outcome of settled) {
|
|
883
|
+
if (outcome.ok) hits.push(...outcome.value.hits);
|
|
884
|
+
else failed.push(outcome.item.key);
|
|
885
|
+
}
|
|
886
|
+
return { hits, failed };
|
|
887
|
+
};
|
|
888
|
+
const first = collect(await round(phrase, pageSize));
|
|
889
|
+
if (first.hits.length > 0 || !multiWord || token === phrase) return first;
|
|
890
|
+
const second = collect(await round(token, widePageSize));
|
|
891
|
+
return { hits: second.hits, failed: [.../* @__PURE__ */ new Set([...first.failed, ...second.failed])] };
|
|
892
|
+
}
|
|
893
|
+
async function searchEverything(rawQuery, options) {
|
|
894
|
+
const started = Date.now();
|
|
895
|
+
const query = normalizeQuery(rawQuery);
|
|
896
|
+
const targets = options.targets;
|
|
897
|
+
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
898
|
+
const perTarget = options.perTarget ?? DEFAULT_PER_TARGET;
|
|
899
|
+
const empty = (via2) => assemble(rawQuery, [], targets, { via: via2, failed: [], elapsedMs: Date.now() - started, perTarget, limit });
|
|
900
|
+
if (query.folded.length < MIN_QUERY_LENGTH || targets.length === 0) return empty("cache");
|
|
901
|
+
const scope = scopeKey(targets);
|
|
902
|
+
const exact = cacheGet(scope, query.folded);
|
|
903
|
+
if (exact) {
|
|
904
|
+
return assemble(rawQuery, exact.hits, targets, {
|
|
905
|
+
via: "cache",
|
|
906
|
+
failed: [],
|
|
907
|
+
elapsedMs: Date.now() - started,
|
|
908
|
+
perTarget,
|
|
909
|
+
limit
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
if (options.onPartial) {
|
|
913
|
+
const prefix = cachePrefix(scope, query.folded);
|
|
914
|
+
if (prefix) {
|
|
915
|
+
const rescored = prefix.hits.map((hit) => {
|
|
916
|
+
const score = scoreCandidate(query, {
|
|
917
|
+
title: foldText(hit.title),
|
|
918
|
+
haystack: foldText(`${hit.title} ${hit.subtitle ?? ""}`),
|
|
919
|
+
digits: digitsOf(hit.subtitle ?? "")
|
|
920
|
+
});
|
|
921
|
+
return score > 0 ? { ...hit, score } : null;
|
|
922
|
+
}).filter((h) => h !== null);
|
|
923
|
+
options.onPartial(assemble(rawQuery, rescored, targets, {
|
|
924
|
+
via: "cache",
|
|
925
|
+
failed: [],
|
|
926
|
+
elapsedMs: Date.now() - started,
|
|
927
|
+
perTarget,
|
|
928
|
+
limit,
|
|
929
|
+
partial: true
|
|
930
|
+
}));
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
let hits = [];
|
|
934
|
+
let failed = [];
|
|
935
|
+
let via = "scan";
|
|
936
|
+
if (!options.forceScan && indexAvailable !== false) {
|
|
937
|
+
try {
|
|
938
|
+
const indexed = await searchViaIndex(query, targets, perTarget, limit);
|
|
939
|
+
if (indexed.ok) {
|
|
940
|
+
hits = indexed.hits;
|
|
941
|
+
via = "index";
|
|
942
|
+
}
|
|
943
|
+
} catch {
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (via !== "index") {
|
|
947
|
+
const emit = options.onPartial;
|
|
948
|
+
const scanned = await searchViaScan(query, targets, perTarget, emit ? (running) => emit(assemble(rawQuery, running, targets, {
|
|
949
|
+
via: "scan",
|
|
950
|
+
failed: [],
|
|
951
|
+
elapsedMs: Date.now() - started,
|
|
952
|
+
perTarget,
|
|
953
|
+
limit,
|
|
954
|
+
partial: true
|
|
955
|
+
})) : void 0);
|
|
956
|
+
hits = scanned.hits;
|
|
957
|
+
failed = scanned.failed;
|
|
958
|
+
}
|
|
959
|
+
if (options.signal?.aborted) return empty(via);
|
|
960
|
+
const result = assemble(rawQuery, hits, targets, {
|
|
961
|
+
via,
|
|
962
|
+
failed,
|
|
963
|
+
elapsedMs: Date.now() - started,
|
|
964
|
+
perTarget,
|
|
965
|
+
limit
|
|
966
|
+
});
|
|
967
|
+
cacheSet({
|
|
968
|
+
scope,
|
|
969
|
+
folded: query.folded,
|
|
970
|
+
at: Date.now(),
|
|
971
|
+
// Only an uncapped answer is a sound superset for longer queries.
|
|
972
|
+
complete: failed.length === 0 && !result.groups.some((g) => g.hasMore),
|
|
973
|
+
hits
|
|
974
|
+
});
|
|
975
|
+
return result;
|
|
297
976
|
}
|
|
298
977
|
function UnknownBlock({ type }) {
|
|
299
|
-
return
|
|
978
|
+
return React3.createElement(
|
|
300
979
|
"div",
|
|
301
980
|
{
|
|
302
981
|
style: {
|
|
@@ -313,14 +992,14 @@ function UnknownBlock({ type }) {
|
|
|
313
992
|
function BlockRenderer({ node }) {
|
|
314
993
|
const Comp = getBlock(node.type);
|
|
315
994
|
const childEls = node.children?.length ? node.children.map(
|
|
316
|
-
(child, i) =>
|
|
995
|
+
(child, i) => React3.createElement(BlockRenderer, { key: child.id ?? `${child.type}-${i}`, node: child })
|
|
317
996
|
) : null;
|
|
318
|
-
if (!Comp) return
|
|
319
|
-
return
|
|
997
|
+
if (!Comp) return React3.createElement(UnknownBlock, { type: node.type });
|
|
998
|
+
return React3.createElement(Comp, { ...node.props ?? {}, node }, childEls);
|
|
320
999
|
}
|
|
321
1000
|
function renderBlocks(nodes) {
|
|
322
1001
|
return nodes.map(
|
|
323
|
-
(node, i) =>
|
|
1002
|
+
(node, i) => React3.createElement(BlockRenderer, { key: node.id ?? `${node.type}-${i}`, node })
|
|
324
1003
|
);
|
|
325
1004
|
}
|
|
326
1005
|
function BlockChildren({ node }) {
|
|
@@ -329,7 +1008,7 @@ function BlockChildren({ node }) {
|
|
|
329
1008
|
}
|
|
330
1009
|
|
|
331
1010
|
// src/manifest/index.ts
|
|
332
|
-
var CURRENT_MANIFEST_VERSION =
|
|
1011
|
+
var CURRENT_MANIFEST_VERSION = 3;
|
|
333
1012
|
var migrations = /* @__PURE__ */ new Map();
|
|
334
1013
|
var supportedBackendProviders = ["supabase", "fayz-api", "fayz-shop", "mock", "custom"];
|
|
335
1014
|
var supportedPageSections = /* @__PURE__ */ new Set(["main", "secondary", "settings"]);
|
|
@@ -343,7 +1022,10 @@ var allowedManifestKeys = /* @__PURE__ */ new Set([
|
|
|
343
1022
|
"permissions",
|
|
344
1023
|
"billing",
|
|
345
1024
|
"entities",
|
|
346
|
-
"surfaces"
|
|
1025
|
+
"surfaces",
|
|
1026
|
+
"limitDeclarations",
|
|
1027
|
+
"agent",
|
|
1028
|
+
"contractHash"
|
|
347
1029
|
]);
|
|
348
1030
|
var allowedBackendKeys = /* @__PURE__ */ new Set(["provider", "projectRef", "url", "adapterId", "options"]);
|
|
349
1031
|
var allowedSurfaceKeys = /* @__PURE__ */ new Set(["scaffold", "options", "plugins", "pages"]);
|
|
@@ -355,6 +1037,7 @@ var manifestIdPattern = /^[a-z0-9][a-z0-9-]*$/;
|
|
|
355
1037
|
function registerManifestMigration(fromVersion, fn) {
|
|
356
1038
|
migrations.set(fromVersion, fn);
|
|
357
1039
|
}
|
|
1040
|
+
registerManifestMigration(2, (m) => m);
|
|
358
1041
|
function migrateManifest(input) {
|
|
359
1042
|
let m = { ...input };
|
|
360
1043
|
let version = typeof m.manifestVersion === "number" ? m.manifestVersion : 1;
|
|
@@ -392,6 +1075,21 @@ function validateManifest(m) {
|
|
|
392
1075
|
validateLooseObject(manifest.theme, "manifest.theme", problems);
|
|
393
1076
|
validateLooseObject(manifest.permissions, "manifest.permissions", problems);
|
|
394
1077
|
validateLooseObject(manifest.billing, "manifest.billing", problems);
|
|
1078
|
+
validateLooseObject(manifest.agent, "manifest.agent", problems);
|
|
1079
|
+
if (manifest.contractHash !== void 0 && !isNonEmptyString(manifest.contractHash)) {
|
|
1080
|
+
problems.push("manifest.contractHash must be a non-empty string");
|
|
1081
|
+
}
|
|
1082
|
+
if (manifest.limitDeclarations !== void 0) {
|
|
1083
|
+
if (!Array.isArray(manifest.limitDeclarations)) {
|
|
1084
|
+
problems.push("manifest.limitDeclarations must be an array");
|
|
1085
|
+
} else {
|
|
1086
|
+
manifest.limitDeclarations.forEach((decl, index) => {
|
|
1087
|
+
if (!isRecord(decl) || !isNonEmptyString(decl.key) || !isNonEmptyString(decl.table)) {
|
|
1088
|
+
problems.push(`manifest.limitDeclarations[${index}] must declare key and table`);
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
395
1093
|
if (manifest.entities !== void 0) {
|
|
396
1094
|
if (!Array.isArray(manifest.entities)) {
|
|
397
1095
|
problems.push("manifest.entities must be an array");
|
|
@@ -552,7 +1250,7 @@ function validateBlockManifest(block, path, problems) {
|
|
|
552
1250
|
function addUnsupportedKeys(value, allowedKeys, path, problems) {
|
|
553
1251
|
for (const key of Object.keys(value)) {
|
|
554
1252
|
if (!allowedKeys.has(key)) {
|
|
555
|
-
problems.push(`${path}.${key} is not part of AppManifest
|
|
1253
|
+
problems.push(`${path}.${key} is not part of AppManifest v${CURRENT_MANIFEST_VERSION}`);
|
|
556
1254
|
}
|
|
557
1255
|
}
|
|
558
1256
|
}
|
|
@@ -573,113 +1271,717 @@ function isNonEmptyString(value) {
|
|
|
573
1271
|
return typeof value === "string" && value.trim().length > 0;
|
|
574
1272
|
}
|
|
575
1273
|
|
|
1274
|
+
// src/manifest/serialize.ts
|
|
1275
|
+
function sortValue(value) {
|
|
1276
|
+
if (Array.isArray(value)) return value.map(sortValue);
|
|
1277
|
+
if (value !== null && typeof value === "object") {
|
|
1278
|
+
const record = value;
|
|
1279
|
+
const out = {};
|
|
1280
|
+
for (const key of Object.keys(record).sort()) {
|
|
1281
|
+
const v = record[key];
|
|
1282
|
+
if (v !== void 0) out[key] = sortValue(v);
|
|
1283
|
+
}
|
|
1284
|
+
return out;
|
|
1285
|
+
}
|
|
1286
|
+
return value;
|
|
1287
|
+
}
|
|
1288
|
+
function serializeManifest(manifest) {
|
|
1289
|
+
const { contractHash: _omit, ...rest } = manifest;
|
|
1290
|
+
return `${JSON.stringify(sortValue(rest), null, 2)}
|
|
1291
|
+
`;
|
|
1292
|
+
}
|
|
1293
|
+
async function computeContractHash(manifest) {
|
|
1294
|
+
const bytes = new TextEncoder().encode(serializeManifest(manifest));
|
|
1295
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
1296
|
+
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1297
|
+
}
|
|
1298
|
+
async function serializeManifestWithHash(manifest) {
|
|
1299
|
+
const contractHash = await computeContractHash(manifest);
|
|
1300
|
+
const { contractHash: _omit, ...rest } = manifest;
|
|
1301
|
+
return `${JSON.stringify(sortValue({ ...rest, contractHash }), null, 2)}
|
|
1302
|
+
`;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
576
1305
|
// src/manifest/app-manifest.schema.json
|
|
577
1306
|
var app_manifest_schema_default = {
|
|
578
1307
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
579
|
-
$id: "https://fayz.ai/schemas/app-manifest/
|
|
1308
|
+
$id: "https://fayz.ai/schemas/app-manifest/v3.json",
|
|
580
1309
|
title: "Fayz AppManifest",
|
|
581
1310
|
description: "The canonical, JSON-serializable definition of a Fayz app. Code is referenced by registry id; never inlined.",
|
|
582
1311
|
type: "object",
|
|
583
|
-
required: [
|
|
1312
|
+
required: [
|
|
1313
|
+
"manifestVersion",
|
|
1314
|
+
"id",
|
|
1315
|
+
"name",
|
|
1316
|
+
"surfaces"
|
|
1317
|
+
],
|
|
584
1318
|
additionalProperties: false,
|
|
585
1319
|
properties: {
|
|
586
1320
|
manifestVersion: {
|
|
587
1321
|
type: "integer",
|
|
588
|
-
const:
|
|
1322
|
+
const: 3,
|
|
589
1323
|
description: "Current AppManifest schema version. Older or newer versions require an explicit SDK migration before validation."
|
|
590
1324
|
},
|
|
591
|
-
id: {
|
|
592
|
-
|
|
1325
|
+
id: {
|
|
1326
|
+
type: "string",
|
|
1327
|
+
minLength: 1,
|
|
1328
|
+
pattern: "^[a-z0-9][a-z0-9-]*$"
|
|
1329
|
+
},
|
|
1330
|
+
name: {
|
|
1331
|
+
type: "string",
|
|
1332
|
+
minLength: 1
|
|
1333
|
+
},
|
|
593
1334
|
backend: {
|
|
594
1335
|
type: "object",
|
|
595
1336
|
additionalProperties: false,
|
|
596
|
-
required: [
|
|
1337
|
+
required: [
|
|
1338
|
+
"provider"
|
|
1339
|
+
],
|
|
597
1340
|
properties: {
|
|
598
|
-
provider: {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
1341
|
+
provider: {
|
|
1342
|
+
enum: [
|
|
1343
|
+
"supabase",
|
|
1344
|
+
"fayz-api",
|
|
1345
|
+
"fayz-shop",
|
|
1346
|
+
"mock",
|
|
1347
|
+
"custom"
|
|
1348
|
+
]
|
|
1349
|
+
},
|
|
1350
|
+
projectRef: {
|
|
1351
|
+
type: "string",
|
|
1352
|
+
minLength: 1
|
|
1353
|
+
},
|
|
1354
|
+
url: {
|
|
1355
|
+
type: "string",
|
|
1356
|
+
minLength: 1
|
|
1357
|
+
},
|
|
1358
|
+
adapterId: {
|
|
1359
|
+
type: "string",
|
|
1360
|
+
minLength: 1
|
|
1361
|
+
},
|
|
1362
|
+
options: {
|
|
1363
|
+
type: "object"
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
},
|
|
1367
|
+
locale: {
|
|
1368
|
+
type: "object"
|
|
1369
|
+
},
|
|
1370
|
+
theme: {
|
|
1371
|
+
type: "object"
|
|
1372
|
+
},
|
|
1373
|
+
permissions: {
|
|
1374
|
+
type: "object"
|
|
1375
|
+
},
|
|
1376
|
+
billing: {
|
|
1377
|
+
type: "object"
|
|
1378
|
+
},
|
|
1379
|
+
entities: {
|
|
1380
|
+
type: "array",
|
|
1381
|
+
items: {
|
|
1382
|
+
type: "object"
|
|
603
1383
|
}
|
|
604
1384
|
},
|
|
605
|
-
locale: { type: "object" },
|
|
606
|
-
theme: { type: "object" },
|
|
607
|
-
permissions: { type: "object" },
|
|
608
|
-
billing: { type: "object" },
|
|
609
|
-
entities: { type: "array", items: { type: "object" } },
|
|
610
1385
|
surfaces: {
|
|
611
1386
|
type: "object",
|
|
612
1387
|
minProperties: 1,
|
|
613
|
-
additionalProperties: {
|
|
1388
|
+
additionalProperties: {
|
|
1389
|
+
$ref: "#/$defs/surface"
|
|
1390
|
+
}
|
|
1391
|
+
},
|
|
1392
|
+
limitDeclarations: {
|
|
1393
|
+
type: "array",
|
|
1394
|
+
description: "Resolved 4-layer limit-declaration merge (core < entity-derived < plugin < app) so server runtimes count exactly what the client counts.",
|
|
1395
|
+
items: {
|
|
1396
|
+
type: "object",
|
|
1397
|
+
required: [
|
|
1398
|
+
"key",
|
|
1399
|
+
"table"
|
|
1400
|
+
],
|
|
1401
|
+
additionalProperties: false,
|
|
1402
|
+
properties: {
|
|
1403
|
+
key: {
|
|
1404
|
+
type: "string",
|
|
1405
|
+
minLength: 1
|
|
1406
|
+
},
|
|
1407
|
+
label: {
|
|
1408
|
+
type: "string"
|
|
1409
|
+
},
|
|
1410
|
+
table: {
|
|
1411
|
+
type: "string",
|
|
1412
|
+
minLength: 1
|
|
1413
|
+
},
|
|
1414
|
+
kindFilter: {
|
|
1415
|
+
type: "string"
|
|
1416
|
+
},
|
|
1417
|
+
period: {
|
|
1418
|
+
enum: [
|
|
1419
|
+
"month",
|
|
1420
|
+
"total"
|
|
1421
|
+
]
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
},
|
|
1426
|
+
agent: {
|
|
1427
|
+
$ref: "#/$defs/agentContract"
|
|
1428
|
+
},
|
|
1429
|
+
contractHash: {
|
|
1430
|
+
type: "string",
|
|
1431
|
+
minLength: 1,
|
|
1432
|
+
description: "sha256 of the canonical serialization (excluding this field)."
|
|
614
1433
|
}
|
|
615
1434
|
},
|
|
616
1435
|
$defs: {
|
|
617
1436
|
surface: {
|
|
618
1437
|
type: "object",
|
|
619
|
-
required: [
|
|
1438
|
+
required: [
|
|
1439
|
+
"scaffold"
|
|
1440
|
+
],
|
|
620
1441
|
additionalProperties: false,
|
|
621
1442
|
properties: {
|
|
622
|
-
scaffold: {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
1443
|
+
scaffold: {
|
|
1444
|
+
type: "string",
|
|
1445
|
+
minLength: 1
|
|
1446
|
+
},
|
|
1447
|
+
options: {
|
|
1448
|
+
type: "object"
|
|
1449
|
+
},
|
|
1450
|
+
plugins: {
|
|
1451
|
+
type: "array",
|
|
1452
|
+
items: {
|
|
1453
|
+
$ref: "#/$defs/pluginRef"
|
|
1454
|
+
}
|
|
1455
|
+
},
|
|
1456
|
+
pages: {
|
|
1457
|
+
type: "array",
|
|
1458
|
+
items: {
|
|
1459
|
+
$ref: "#/$defs/page"
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
626
1462
|
}
|
|
627
1463
|
},
|
|
628
1464
|
pluginRef: {
|
|
629
1465
|
type: "object",
|
|
630
|
-
required: [
|
|
1466
|
+
required: [
|
|
1467
|
+
"id"
|
|
1468
|
+
],
|
|
631
1469
|
additionalProperties: false,
|
|
632
1470
|
properties: {
|
|
633
|
-
id: {
|
|
634
|
-
|
|
635
|
-
|
|
1471
|
+
id: {
|
|
1472
|
+
type: "string",
|
|
1473
|
+
minLength: 1
|
|
1474
|
+
},
|
|
1475
|
+
config: {
|
|
1476
|
+
type: "object"
|
|
1477
|
+
},
|
|
1478
|
+
enabled: {
|
|
1479
|
+
type: "boolean"
|
|
1480
|
+
}
|
|
636
1481
|
}
|
|
637
1482
|
},
|
|
638
1483
|
page: {
|
|
639
1484
|
type: "object",
|
|
640
|
-
required: [
|
|
1485
|
+
required: [
|
|
1486
|
+
"path"
|
|
1487
|
+
],
|
|
641
1488
|
additionalProperties: false,
|
|
642
1489
|
description: "Exactly one of blocks/entity/component must be set.",
|
|
643
1490
|
properties: {
|
|
644
|
-
path: {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
1491
|
+
path: {
|
|
1492
|
+
type: "string",
|
|
1493
|
+
minLength: 1
|
|
1494
|
+
},
|
|
1495
|
+
label: {
|
|
1496
|
+
type: "string"
|
|
1497
|
+
},
|
|
1498
|
+
icon: {
|
|
1499
|
+
type: "string"
|
|
1500
|
+
},
|
|
1501
|
+
section: {
|
|
1502
|
+
enum: [
|
|
1503
|
+
"main",
|
|
1504
|
+
"secondary",
|
|
1505
|
+
"settings"
|
|
1506
|
+
]
|
|
1507
|
+
},
|
|
1508
|
+
blocks: {
|
|
1509
|
+
type: "array",
|
|
1510
|
+
items: {
|
|
1511
|
+
$ref: "#/$defs/block"
|
|
1512
|
+
}
|
|
1513
|
+
},
|
|
1514
|
+
entity: {
|
|
1515
|
+
type: "string",
|
|
1516
|
+
minLength: 1
|
|
1517
|
+
},
|
|
1518
|
+
component: {
|
|
1519
|
+
type: "string",
|
|
1520
|
+
minLength: 1
|
|
1521
|
+
},
|
|
651
1522
|
permission: {
|
|
652
1523
|
type: "object",
|
|
653
|
-
required: [
|
|
1524
|
+
required: [
|
|
1525
|
+
"feature",
|
|
1526
|
+
"action"
|
|
1527
|
+
],
|
|
654
1528
|
additionalProperties: false,
|
|
655
1529
|
properties: {
|
|
656
|
-
feature: {
|
|
657
|
-
|
|
1530
|
+
feature: {
|
|
1531
|
+
type: "string",
|
|
1532
|
+
minLength: 1
|
|
1533
|
+
},
|
|
1534
|
+
action: {
|
|
1535
|
+
type: "string",
|
|
1536
|
+
minLength: 1
|
|
1537
|
+
}
|
|
658
1538
|
}
|
|
659
1539
|
}
|
|
660
1540
|
},
|
|
661
1541
|
oneOf: [
|
|
662
|
-
{
|
|
663
|
-
|
|
664
|
-
|
|
1542
|
+
{
|
|
1543
|
+
required: [
|
|
1544
|
+
"blocks"
|
|
1545
|
+
]
|
|
1546
|
+
},
|
|
1547
|
+
{
|
|
1548
|
+
required: [
|
|
1549
|
+
"entity"
|
|
1550
|
+
]
|
|
1551
|
+
},
|
|
1552
|
+
{
|
|
1553
|
+
required: [
|
|
1554
|
+
"component"
|
|
1555
|
+
]
|
|
1556
|
+
}
|
|
665
1557
|
]
|
|
666
1558
|
},
|
|
667
1559
|
block: {
|
|
668
1560
|
type: "object",
|
|
669
|
-
required: [
|
|
1561
|
+
required: [
|
|
1562
|
+
"type"
|
|
1563
|
+
],
|
|
1564
|
+
additionalProperties: false,
|
|
1565
|
+
properties: {
|
|
1566
|
+
type: {
|
|
1567
|
+
type: "string",
|
|
1568
|
+
minLength: 1
|
|
1569
|
+
},
|
|
1570
|
+
id: {
|
|
1571
|
+
type: "string"
|
|
1572
|
+
},
|
|
1573
|
+
props: {
|
|
1574
|
+
type: "object"
|
|
1575
|
+
},
|
|
1576
|
+
children: {
|
|
1577
|
+
type: "array",
|
|
1578
|
+
items: {
|
|
1579
|
+
$ref: "#/$defs/block"
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
},
|
|
1584
|
+
agentContract: {
|
|
1585
|
+
type: "object",
|
|
670
1586
|
additionalProperties: false,
|
|
1587
|
+
description: "Everything a server-side agent runtime needs to operate the app without a browser. Derived from code at publish; synced to Fayz as the versioned AgentContract.",
|
|
671
1588
|
properties: {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
1589
|
+
persona: {
|
|
1590
|
+
type: "object",
|
|
1591
|
+
additionalProperties: false,
|
|
1592
|
+
properties: {
|
|
1593
|
+
name: {
|
|
1594
|
+
type: "string"
|
|
1595
|
+
},
|
|
1596
|
+
systemPrompt: {
|
|
1597
|
+
type: "string"
|
|
1598
|
+
},
|
|
1599
|
+
icon: {
|
|
1600
|
+
type: "string"
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
},
|
|
1604
|
+
executionPlane: {
|
|
1605
|
+
enum: [
|
|
1606
|
+
"client",
|
|
1607
|
+
"server"
|
|
1608
|
+
]
|
|
1609
|
+
},
|
|
1610
|
+
entities: {
|
|
1611
|
+
type: "array",
|
|
1612
|
+
items: {
|
|
1613
|
+
$ref: "#/$defs/entityContract"
|
|
1614
|
+
}
|
|
1615
|
+
},
|
|
1616
|
+
tools: {
|
|
1617
|
+
type: "array",
|
|
1618
|
+
items: {
|
|
1619
|
+
$ref: "#/$defs/aiToolContract"
|
|
1620
|
+
}
|
|
1621
|
+
},
|
|
1622
|
+
registries: {
|
|
1623
|
+
type: "array",
|
|
1624
|
+
items: {
|
|
1625
|
+
type: "object",
|
|
1626
|
+
required: [
|
|
1627
|
+
"pluginId",
|
|
1628
|
+
"id"
|
|
1629
|
+
],
|
|
1630
|
+
additionalProperties: false,
|
|
1631
|
+
properties: {
|
|
1632
|
+
pluginId: {
|
|
1633
|
+
type: "string",
|
|
1634
|
+
minLength: 1
|
|
1635
|
+
},
|
|
1636
|
+
id: {
|
|
1637
|
+
type: "string",
|
|
1638
|
+
minLength: 1
|
|
1639
|
+
},
|
|
1640
|
+
entityKey: {
|
|
1641
|
+
type: "string"
|
|
1642
|
+
},
|
|
1643
|
+
readOnly: {
|
|
1644
|
+
type: "boolean"
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
},
|
|
1649
|
+
declaredFeatures: {
|
|
1650
|
+
type: "array",
|
|
1651
|
+
items: {
|
|
1652
|
+
type: "object"
|
|
1653
|
+
}
|
|
1654
|
+
},
|
|
1655
|
+
rpcs: {
|
|
1656
|
+
type: "array",
|
|
1657
|
+
items: {
|
|
1658
|
+
$ref: "#/$defs/rpcContract"
|
|
1659
|
+
}
|
|
1660
|
+
},
|
|
1661
|
+
domainKnowledge: {
|
|
1662
|
+
type: "object",
|
|
1663
|
+
additionalProperties: false,
|
|
1664
|
+
properties: {
|
|
1665
|
+
statuses: {
|
|
1666
|
+
type: "object"
|
|
1667
|
+
},
|
|
1668
|
+
scheduleDefaults: {
|
|
1669
|
+
type: "object"
|
|
1670
|
+
},
|
|
1671
|
+
businessRules: {
|
|
1672
|
+
type: "array",
|
|
1673
|
+
items: {
|
|
1674
|
+
type: "object",
|
|
1675
|
+
required: [
|
|
1676
|
+
"id",
|
|
1677
|
+
"description"
|
|
1678
|
+
],
|
|
1679
|
+
additionalProperties: false,
|
|
1680
|
+
properties: {
|
|
1681
|
+
id: {
|
|
1682
|
+
type: "string",
|
|
1683
|
+
minLength: 1
|
|
1684
|
+
},
|
|
1685
|
+
description: {
|
|
1686
|
+
type: "string",
|
|
1687
|
+
minLength: 1
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
},
|
|
1696
|
+
entityContract: {
|
|
1697
|
+
type: "object",
|
|
1698
|
+
required: [
|
|
1699
|
+
"key",
|
|
1700
|
+
"label",
|
|
1701
|
+
"data",
|
|
1702
|
+
"fields"
|
|
1703
|
+
],
|
|
1704
|
+
additionalProperties: false,
|
|
1705
|
+
properties: {
|
|
1706
|
+
key: {
|
|
1707
|
+
type: "string",
|
|
1708
|
+
minLength: 1
|
|
1709
|
+
},
|
|
1710
|
+
label: {
|
|
1711
|
+
type: "string",
|
|
1712
|
+
minLength: 1
|
|
1713
|
+
},
|
|
1714
|
+
labelPlural: {
|
|
1715
|
+
type: "string"
|
|
1716
|
+
},
|
|
1717
|
+
limitKey: {
|
|
1718
|
+
type: "string"
|
|
1719
|
+
},
|
|
1720
|
+
data: {
|
|
1721
|
+
type: "object",
|
|
1722
|
+
required: [
|
|
1723
|
+
"table"
|
|
1724
|
+
],
|
|
1725
|
+
additionalProperties: false,
|
|
1726
|
+
properties: {
|
|
1727
|
+
table: {
|
|
1728
|
+
type: "string",
|
|
1729
|
+
minLength: 1
|
|
1730
|
+
},
|
|
1731
|
+
schema: {
|
|
1732
|
+
type: "string"
|
|
1733
|
+
},
|
|
1734
|
+
tenantScoped: {
|
|
1735
|
+
type: "boolean"
|
|
1736
|
+
},
|
|
1737
|
+
tenantIdColumn: {
|
|
1738
|
+
type: "string"
|
|
1739
|
+
},
|
|
1740
|
+
searchColumns: {
|
|
1741
|
+
type: "array",
|
|
1742
|
+
items: {
|
|
1743
|
+
type: "string"
|
|
1744
|
+
}
|
|
1745
|
+
},
|
|
1746
|
+
selectColumns: {
|
|
1747
|
+
type: "string"
|
|
1748
|
+
},
|
|
1749
|
+
columnMap: {
|
|
1750
|
+
type: "object"
|
|
1751
|
+
},
|
|
1752
|
+
archetype: {
|
|
1753
|
+
type: "string"
|
|
1754
|
+
},
|
|
1755
|
+
archetypeKind: {
|
|
1756
|
+
type: "string"
|
|
1757
|
+
},
|
|
1758
|
+
filters: {
|
|
1759
|
+
type: "object"
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
},
|
|
1763
|
+
fields: {
|
|
1764
|
+
type: "array",
|
|
1765
|
+
items: {
|
|
1766
|
+
type: "object",
|
|
1767
|
+
required: [
|
|
1768
|
+
"key",
|
|
1769
|
+
"label",
|
|
1770
|
+
"type"
|
|
1771
|
+
],
|
|
1772
|
+
additionalProperties: false,
|
|
1773
|
+
properties: {
|
|
1774
|
+
key: {
|
|
1775
|
+
type: "string",
|
|
1776
|
+
minLength: 1
|
|
1777
|
+
},
|
|
1778
|
+
label: {
|
|
1779
|
+
type: "string"
|
|
1780
|
+
},
|
|
1781
|
+
type: {
|
|
1782
|
+
type: "string",
|
|
1783
|
+
minLength: 1
|
|
1784
|
+
},
|
|
1785
|
+
required: {
|
|
1786
|
+
type: "boolean"
|
|
1787
|
+
},
|
|
1788
|
+
searchable: {
|
|
1789
|
+
type: "boolean"
|
|
1790
|
+
},
|
|
1791
|
+
hint: {
|
|
1792
|
+
type: "string"
|
|
1793
|
+
},
|
|
1794
|
+
options: {
|
|
1795
|
+
type: "array",
|
|
1796
|
+
items: {
|
|
1797
|
+
type: "string"
|
|
1798
|
+
}
|
|
1799
|
+
},
|
|
1800
|
+
relationTable: {
|
|
1801
|
+
type: "string"
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
},
|
|
1806
|
+
permission: {
|
|
1807
|
+
type: "object",
|
|
1808
|
+
required: [
|
|
1809
|
+
"feature",
|
|
1810
|
+
"action"
|
|
1811
|
+
],
|
|
1812
|
+
additionalProperties: false,
|
|
1813
|
+
properties: {
|
|
1814
|
+
feature: {
|
|
1815
|
+
type: "string",
|
|
1816
|
+
minLength: 1
|
|
1817
|
+
},
|
|
1818
|
+
action: {
|
|
1819
|
+
type: "string",
|
|
1820
|
+
minLength: 1
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
},
|
|
1826
|
+
aiToolContract: {
|
|
1827
|
+
type: "object",
|
|
1828
|
+
required: [
|
|
1829
|
+
"id",
|
|
1830
|
+
"name",
|
|
1831
|
+
"description",
|
|
1832
|
+
"mode"
|
|
1833
|
+
],
|
|
1834
|
+
additionalProperties: false,
|
|
1835
|
+
properties: {
|
|
1836
|
+
id: {
|
|
1837
|
+
type: "string",
|
|
1838
|
+
minLength: 1
|
|
1839
|
+
},
|
|
1840
|
+
name: {
|
|
1841
|
+
type: "string",
|
|
1842
|
+
minLength: 1,
|
|
1843
|
+
pattern: "^[a-zA-Z0-9_-]{1,64}$"
|
|
1844
|
+
},
|
|
1845
|
+
description: {
|
|
1846
|
+
type: "string",
|
|
1847
|
+
minLength: 1
|
|
1848
|
+
},
|
|
1849
|
+
mode: {
|
|
1850
|
+
enum: [
|
|
1851
|
+
"read",
|
|
1852
|
+
"persist"
|
|
1853
|
+
]
|
|
1854
|
+
},
|
|
1855
|
+
parameters: {
|
|
1856
|
+
type: "object"
|
|
1857
|
+
},
|
|
1858
|
+
permission: {
|
|
1859
|
+
type: "object",
|
|
1860
|
+
required: [
|
|
1861
|
+
"feature",
|
|
1862
|
+
"action"
|
|
1863
|
+
],
|
|
1864
|
+
additionalProperties: false,
|
|
1865
|
+
properties: {
|
|
1866
|
+
feature: {
|
|
1867
|
+
type: "string",
|
|
1868
|
+
minLength: 1
|
|
1869
|
+
},
|
|
1870
|
+
action: {
|
|
1871
|
+
type: "string",
|
|
1872
|
+
minLength: 1
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
},
|
|
1876
|
+
limitKey: {
|
|
1877
|
+
type: "string"
|
|
1878
|
+
},
|
|
1879
|
+
execution: {
|
|
1880
|
+
oneOf: [
|
|
1881
|
+
{
|
|
1882
|
+
type: "object",
|
|
1883
|
+
required: [
|
|
1884
|
+
"plane",
|
|
1885
|
+
"kind",
|
|
1886
|
+
"rpc"
|
|
1887
|
+
],
|
|
1888
|
+
additionalProperties: false,
|
|
1889
|
+
properties: {
|
|
1890
|
+
plane: {
|
|
1891
|
+
const: "server"
|
|
1892
|
+
},
|
|
1893
|
+
kind: {
|
|
1894
|
+
const: "rpc"
|
|
1895
|
+
},
|
|
1896
|
+
rpc: {
|
|
1897
|
+
type: "string",
|
|
1898
|
+
minLength: 1
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
},
|
|
1902
|
+
{
|
|
1903
|
+
type: "object",
|
|
1904
|
+
required: [
|
|
1905
|
+
"plane",
|
|
1906
|
+
"kind",
|
|
1907
|
+
"entity"
|
|
1908
|
+
],
|
|
1909
|
+
additionalProperties: false,
|
|
1910
|
+
properties: {
|
|
1911
|
+
plane: {
|
|
1912
|
+
const: "server"
|
|
1913
|
+
},
|
|
1914
|
+
kind: {
|
|
1915
|
+
const: "entity_read"
|
|
1916
|
+
},
|
|
1917
|
+
entity: {
|
|
1918
|
+
type: "string",
|
|
1919
|
+
minLength: 1
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
},
|
|
1923
|
+
{
|
|
1924
|
+
type: "object",
|
|
1925
|
+
required: [
|
|
1926
|
+
"plane"
|
|
1927
|
+
],
|
|
1928
|
+
additionalProperties: false,
|
|
1929
|
+
properties: {
|
|
1930
|
+
plane: {
|
|
1931
|
+
const: "client"
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
]
|
|
1936
|
+
},
|
|
1937
|
+
requiresConfirmation: {
|
|
1938
|
+
type: "boolean"
|
|
1939
|
+
},
|
|
1940
|
+
pluginId: {
|
|
1941
|
+
type: "string"
|
|
1942
|
+
},
|
|
1943
|
+
category: {
|
|
1944
|
+
type: "string"
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
},
|
|
1948
|
+
rpcContract: {
|
|
1949
|
+
type: "object",
|
|
1950
|
+
required: [
|
|
1951
|
+
"name",
|
|
1952
|
+
"kind",
|
|
1953
|
+
"description"
|
|
1954
|
+
],
|
|
1955
|
+
additionalProperties: false,
|
|
1956
|
+
properties: {
|
|
1957
|
+
name: {
|
|
1958
|
+
type: "string",
|
|
1959
|
+
minLength: 1,
|
|
1960
|
+
pattern: "^agent_[a-z0-9_]+$"
|
|
1961
|
+
},
|
|
1962
|
+
kind: {
|
|
1963
|
+
enum: [
|
|
1964
|
+
"read",
|
|
1965
|
+
"write"
|
|
1966
|
+
]
|
|
1967
|
+
},
|
|
1968
|
+
description: {
|
|
1969
|
+
type: "string",
|
|
1970
|
+
minLength: 1
|
|
1971
|
+
},
|
|
1972
|
+
payloadSchema: {
|
|
1973
|
+
type: "object"
|
|
1974
|
+
},
|
|
1975
|
+
audits: {
|
|
1976
|
+
type: "boolean"
|
|
1977
|
+
}
|
|
676
1978
|
}
|
|
677
1979
|
}
|
|
678
1980
|
}
|
|
679
1981
|
};
|
|
680
|
-
var ManifestContext =
|
|
1982
|
+
var ManifestContext = React3.createContext(null);
|
|
681
1983
|
function useManifest() {
|
|
682
|
-
return
|
|
1984
|
+
return React3.useContext(ManifestContext);
|
|
683
1985
|
}
|
|
684
1986
|
function defineApp(manifest) {
|
|
685
1987
|
const full = {
|
|
@@ -711,10 +2013,10 @@ function renderApp(input, opts = {}) {
|
|
|
711
2013
|
`Scaffold "${surface.scaffold}" is not registered. Import the package that provides it (e.g. @fayz-ai/saas or @fayz-ai/shop) before calling renderApp.`
|
|
712
2014
|
);
|
|
713
2015
|
}
|
|
714
|
-
return
|
|
2016
|
+
return React3.createElement(
|
|
715
2017
|
ManifestContext.Provider,
|
|
716
2018
|
{ value: manifest },
|
|
717
|
-
|
|
2019
|
+
React3.createElement(Scaffold, { manifest, surface: surfaceName })
|
|
718
2020
|
);
|
|
719
2021
|
}
|
|
720
2022
|
var MAX_ENTRIES = 20;
|
|
@@ -757,6 +2059,6 @@ function routeModule(path) {
|
|
|
757
2059
|
return path.replace(/^\/+/, "").split("/")[0] ?? "";
|
|
758
2060
|
}
|
|
759
2061
|
|
|
760
|
-
export { BlockChildren, BlockRenderer, COUNTRIES, CURRENT_MANIFEST_VERSION, DEFAULT_COUNTRY, WidgetZone, absoluteUrl, app_manifest_schema_default as appManifestSchema, applySeo, breadcrumbJsonLd, buildCSV, createEventBus, createSafeDataProvider, defineApp, downloadCSV, eventBus, exportCSV, formatCurrency, formatDate, formatDateTime, getActiveLocale, getCountry, getDefaultCurrency, maskDigitCount, maskPhone, migrateManifest, navHistoryBack, organizationJsonLd, recordNavigation, registerManifestMigration, renderApp, renderBlocks, routeModule, seoOrigin, setDefaultCurrency, unmaskPhone, useManifest, useNavHistoryStore, useNavReferrer, useOnEvent, useSeo, validateManifest };
|
|
2062
|
+
export { BlockChildren, BlockRenderer, COUNTRIES, CURRENT_MANIFEST_VERSION, DATA_CHANGED_EVENT, DEFAULT_COUNTRY, MIN_QUERY_LENGTH, WidgetZone, absoluteUrl, app_manifest_schema_default as appManifestSchema, applySeo, breadcrumbJsonLd, buildCSV, clearPostalCache, clearSearchCache, computeContractHash, createEventBus, createSafeDataProvider, createViaCepProvider, defineApp, digitsOf, downloadCSV, emitDataChanged, eventBus, exportCSV, foldText, foldWithMap, formatCurrency, formatDate, formatDateTime, formatPostalCode, getActiveLocale, getCountry, getDefaultCurrency, getPostalLookupProvider, highlightRanges, isSearchIndexAvailable, isValidPostalCode, lookupPostalCode, maskDigitCount, maskPhone, matchesDataChange, migrateManifest, navHistoryBack, normalizePostalCode, normalizeQuery, organizationJsonLd, recordNavigation, registerManifestMigration, renderApp, renderBlocks, routeModule, scoreCandidate, searchEverything, seoOrigin, serializeManifest, serializeManifestWithHash, setDefaultCurrency, setPostalLookupProvider, setSearchIndexAvailable, similarity, unmaskPhone, useDataChanged, useManifest, useNavHistoryStore, useNavReferrer, useOnEvent, useSeo, validateManifest };
|
|
761
2063
|
//# sourceMappingURL=index.js.map
|
|
762
2064
|
//# sourceMappingURL=index.js.map
|