@fayz-ai/core 0.1.6

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 (172) hide show
  1. package/dist/app/render.d.ts +16 -0
  2. package/dist/app/render.d.ts.map +1 -0
  3. package/dist/blocks/index.d.ts +25 -0
  4. package/dist/blocks/index.d.ts.map +1 -0
  5. package/dist/chunk-2NTHNWEH.cjs +32 -0
  6. package/dist/chunk-2NTHNWEH.cjs.map +1 -0
  7. package/dist/chunk-5EAZOMVP.js +1609 -0
  8. package/dist/chunk-5EAZOMVP.js.map +1 -0
  9. package/dist/chunk-5TKIZVVJ.cjs +16 -0
  10. package/dist/chunk-5TKIZVVJ.cjs.map +1 -0
  11. package/dist/chunk-7RTUHSKY.js +3 -0
  12. package/dist/chunk-7RTUHSKY.js.map +1 -0
  13. package/dist/chunk-AXTWKF5X.cjs +38 -0
  14. package/dist/chunk-AXTWKF5X.cjs.map +1 -0
  15. package/dist/chunk-EGWWQ7AT.cjs +355 -0
  16. package/dist/chunk-EGWWQ7AT.cjs.map +1 -0
  17. package/dist/chunk-KAYQFSR6.js +298 -0
  18. package/dist/chunk-KAYQFSR6.js.map +1 -0
  19. package/dist/chunk-LSVBDYFQ.cjs +1639 -0
  20. package/dist/chunk-LSVBDYFQ.cjs.map +1 -0
  21. package/dist/chunk-SNQW6TRF.cjs +658 -0
  22. package/dist/chunk-SNQW6TRF.cjs.map +1 -0
  23. package/dist/chunk-WKZVSKEU.js +26 -0
  24. package/dist/chunk-WKZVSKEU.js.map +1 -0
  25. package/dist/chunk-Y2C2FBQP.js +35 -0
  26. package/dist/chunk-Y2C2FBQP.js.map +1 -0
  27. package/dist/chunk-YSBXGHAM.js +643 -0
  28. package/dist/chunk-YSBXGHAM.js.map +1 -0
  29. package/dist/data/archetype.d.ts +13 -0
  30. package/dist/data/archetype.d.ts.map +1 -0
  31. package/dist/data/cached.d.ts +19 -0
  32. package/dist/data/cached.d.ts.map +1 -0
  33. package/dist/data/fayz-api.d.ts +19 -0
  34. package/dist/data/fayz-api.d.ts.map +1 -0
  35. package/dist/data/index.cjs +32 -0
  36. package/dist/data/index.cjs.map +1 -0
  37. package/dist/data/index.d.ts +9 -0
  38. package/dist/data/index.d.ts.map +1 -0
  39. package/dist/data/index.js +3 -0
  40. package/dist/data/index.js.map +1 -0
  41. package/dist/data/mock.d.ts +7 -0
  42. package/dist/data/mock.d.ts.map +1 -0
  43. package/dist/data/resolve.d.ts +22 -0
  44. package/dist/data/resolve.d.ts.map +1 -0
  45. package/dist/data/supabase.d.ts +18 -0
  46. package/dist/data/supabase.d.ts.map +1 -0
  47. package/dist/data/types.d.ts +23 -0
  48. package/dist/data/types.d.ts.map +1 -0
  49. package/dist/entity/index.cjs +28 -0
  50. package/dist/entity/index.cjs.map +1 -0
  51. package/dist/entity/index.d.ts +3 -0
  52. package/dist/entity/index.d.ts.map +1 -0
  53. package/dist/entity/index.js +3 -0
  54. package/dist/entity/index.js.map +1 -0
  55. package/dist/entity/registry.d.ts +20 -0
  56. package/dist/entity/registry.d.ts.map +1 -0
  57. package/dist/events/index.d.ts +16 -0
  58. package/dist/events/index.d.ts.map +1 -0
  59. package/dist/i18n/index.cjs +44 -0
  60. package/dist/i18n/index.cjs.map +1 -0
  61. package/dist/i18n/index.d.ts +21 -0
  62. package/dist/i18n/index.d.ts.map +1 -0
  63. package/dist/i18n/index.js +3 -0
  64. package/dist/i18n/index.js.map +1 -0
  65. package/dist/i18n/shell-translations.d.ts +2 -0
  66. package/dist/i18n/shell-translations.d.ts.map +1 -0
  67. package/dist/index.cjs +911 -0
  68. package/dist/index.cjs.map +1 -0
  69. package/dist/index.d.ts +46 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +596 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/lib/cache.d.ts +21 -0
  74. package/dist/lib/cache.d.ts.map +1 -0
  75. package/dist/lib/csv.d.ts +14 -0
  76. package/dist/lib/csv.d.ts.map +1 -0
  77. package/dist/lib/format.d.ts +7 -0
  78. package/dist/lib/format.d.ts.map +1 -0
  79. package/dist/manifest/index.d.ts +80 -0
  80. package/dist/manifest/index.d.ts.map +1 -0
  81. package/dist/plugin/createSafeDataProvider.d.ts +14 -0
  82. package/dist/plugin/createSafeDataProvider.d.ts.map +1 -0
  83. package/dist/plugin/index.cjs +32 -0
  84. package/dist/plugin/index.cjs.map +1 -0
  85. package/dist/plugin/index.d.ts +2 -0
  86. package/dist/plugin/index.d.ts.map +1 -0
  87. package/dist/plugin/index.js +3 -0
  88. package/dist/plugin/index.js.map +1 -0
  89. package/dist/plugin/runtime.d.ts +31 -0
  90. package/dist/plugin/runtime.d.ts.map +1 -0
  91. package/dist/registry/index.d.ts +88 -0
  92. package/dist/registry/index.d.ts.map +1 -0
  93. package/dist/router/index.cjs +16 -0
  94. package/dist/router/index.cjs.map +1 -0
  95. package/dist/router/index.d.ts +8 -0
  96. package/dist/router/index.d.ts.map +1 -0
  97. package/dist/router/index.js +3 -0
  98. package/dist/router/index.js.map +1 -0
  99. package/dist/runtime/index.cjs +16 -0
  100. package/dist/runtime/index.cjs.map +1 -0
  101. package/dist/runtime/index.d.ts +3 -0
  102. package/dist/runtime/index.d.ts.map +1 -0
  103. package/dist/runtime/index.js +3 -0
  104. package/dist/runtime/index.js.map +1 -0
  105. package/dist/runtime/oauth.d.ts +96 -0
  106. package/dist/runtime/oauth.d.ts.map +1 -0
  107. package/dist/tenant/index.d.ts +3 -0
  108. package/dist/tenant/index.d.ts.map +1 -0
  109. package/dist/types/auth.d.ts +27 -0
  110. package/dist/types/auth.d.ts.map +1 -0
  111. package/dist/types/billing.d.ts +17 -0
  112. package/dist/types/billing.d.ts.map +1 -0
  113. package/dist/types/crud.d.ts +107 -0
  114. package/dist/types/crud.d.ts.map +1 -0
  115. package/dist/types/entities.d.ts +134 -0
  116. package/dist/types/entities.d.ts.map +1 -0
  117. package/dist/types/i18n.d.ts +8 -0
  118. package/dist/types/i18n.d.ts.map +1 -0
  119. package/dist/types/index.cjs +4 -0
  120. package/dist/types/index.cjs.map +1 -0
  121. package/dist/types/index.d.ts +10 -0
  122. package/dist/types/index.d.ts.map +1 -0
  123. package/dist/types/index.js +3 -0
  124. package/dist/types/index.js.map +1 -0
  125. package/dist/types/org.d.ts +96 -0
  126. package/dist/types/org.d.ts.map +1 -0
  127. package/dist/types/permissions.d.ts +25 -0
  128. package/dist/types/permissions.d.ts.map +1 -0
  129. package/dist/types/plugins.d.ts +314 -0
  130. package/dist/types/plugins.d.ts.map +1 -0
  131. package/dist/types/theme.d.ts +16 -0
  132. package/dist/types/theme.d.ts.map +1 -0
  133. package/package.json +98 -0
  134. package/src/app/render.tsx +69 -0
  135. package/src/blocks/index.tsx +68 -0
  136. package/src/data/archetype.ts +210 -0
  137. package/src/data/cached.ts +55 -0
  138. package/src/data/fayz-api.ts +200 -0
  139. package/src/data/index.ts +8 -0
  140. package/src/data/mock.ts +84 -0
  141. package/src/data/resolve.ts +108 -0
  142. package/src/data/supabase.ts +169 -0
  143. package/src/data/types.ts +20 -0
  144. package/src/entity/index.ts +2 -0
  145. package/src/entity/registry.ts +42 -0
  146. package/src/events/index.tsx +82 -0
  147. package/src/i18n/index.ts +250 -0
  148. package/src/i18n/shell-translations.ts +1391 -0
  149. package/src/index.ts +132 -0
  150. package/src/lib/cache.ts +118 -0
  151. package/src/lib/csv.ts +54 -0
  152. package/src/lib/format.ts +58 -0
  153. package/src/manifest/app-manifest.schema.json +103 -0
  154. package/src/manifest/index.ts +394 -0
  155. package/src/plugin/createSafeDataProvider.ts +30 -0
  156. package/src/plugin/index.ts +1 -0
  157. package/src/plugin/runtime.ts +313 -0
  158. package/src/registry/index.ts +163 -0
  159. package/src/router/index.ts +29 -0
  160. package/src/runtime/index.ts +15 -0
  161. package/src/runtime/oauth.ts +219 -0
  162. package/src/tenant/index.ts +18 -0
  163. package/src/types/auth.ts +26 -0
  164. package/src/types/billing.ts +17 -0
  165. package/src/types/crud.ts +108 -0
  166. package/src/types/entities.ts +142 -0
  167. package/src/types/i18n.ts +7 -0
  168. package/src/types/index.ts +10 -0
  169. package/src/types/org.ts +94 -0
  170. package/src/types/permissions.ts +32 -0
  171. package/src/types/plugins.ts +363 -0
  172. package/src/types/theme.ts +17 -0
package/dist/index.js ADDED
@@ -0,0 +1,596 @@
1
+ import { getSupabaseClientOptional } from './chunk-YSBXGHAM.js';
2
+ export { clearGlobalCache, createArchetypeProvider, createCacheStore, createFayzApiProvider, createMockProvider, createSupabaseProvider, getActiveTenantId, getSupabaseClientOptional, globalCache, resolveDataProvider, setActiveTenantId, setGlobalSupabaseClient, stableKey, withCache } from './chunk-YSBXGHAM.js';
3
+ export { clearEntityRegistry, deriveEntityKey, getAllEntities, getEntityByKey, registerEntity } from './chunk-WKZVSKEU.js';
4
+ import { getBlock, getScaffold } from './chunk-KAYQFSR6.js';
5
+ 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, usePluginRuntime, usePluginRuntimeOptional } from './chunk-KAYQFSR6.js';
6
+ import { getCurrentLocale } from './chunk-5EAZOMVP.js';
7
+ export { I18nProvider, coreTranslations, getCurrentLocale, mergeTranslations, registerTranslations, setCurrentLocale, setLocaleStore, useI18nConfig, useTranslation } from './chunk-5EAZOMVP.js';
8
+ export { hashRouterAdapter, windowRouterAdapter } from './chunk-Y2C2FBQP.js';
9
+ export { FayzRuntimeError, createFayzRuntimeClient } from './chunk-7RTUHSKY.js';
10
+ import * as React2 from 'react';
11
+
12
+ // src/types/plugins.ts
13
+ var WidgetZone = {
14
+ SIDEBAR_BEFORE_NAV: "shell.sidebar.before-nav",
15
+ SIDEBAR_FOOTER: "shell.sidebar.footer",
16
+ TOPBAR_START: "shell.topbar.start",
17
+ TOPBAR_END: "shell.topbar.end",
18
+ PAGE_BEFORE: "page.before",
19
+ PAGE_AFTER: "page.after",
20
+ SETTINGS_BEFORE: "settings.before",
21
+ SETTINGS_AFTER: "settings.after",
22
+ FLOATING: "shell.floating"
23
+ };
24
+
25
+ // src/plugin/createSafeDataProvider.ts
26
+ function createSafeDataProvider(makeSupabase, makeMock) {
27
+ let resolved = null;
28
+ const get = () => {
29
+ if (!resolved) {
30
+ resolved = getSupabaseClientOptional() ? makeSupabase() : makeMock();
31
+ }
32
+ return resolved;
33
+ };
34
+ return new Proxy({}, {
35
+ get: (_target, prop) => (...args) => get()[prop](...args)
36
+ });
37
+ }
38
+
39
+ // src/lib/format.ts
40
+ var CURRENCY_BY_LOCALE = {
41
+ "pt-BR": "BRL",
42
+ "pt-PT": "EUR",
43
+ en: "USD",
44
+ "en-US": "USD",
45
+ "en-GB": "GBP",
46
+ es: "EUR"
47
+ };
48
+ var _defaultCurrency = null;
49
+ function setDefaultCurrency(code) {
50
+ _defaultCurrency = code ?? null;
51
+ }
52
+ function getActiveLocale() {
53
+ try {
54
+ return getCurrentLocale() || "en";
55
+ } catch {
56
+ return "en";
57
+ }
58
+ }
59
+ function getDefaultCurrency() {
60
+ if (_defaultCurrency) return _defaultCurrency;
61
+ return CURRENCY_BY_LOCALE[getActiveLocale()] ?? "USD";
62
+ }
63
+ function formatCurrency(value, currency) {
64
+ const num = typeof value === "number" ? value : parseFloat(String(value));
65
+ if (isNaN(num)) return String(value ?? "");
66
+ return new Intl.NumberFormat(getActiveLocale(), {
67
+ style: "currency",
68
+ currency: currency ?? getDefaultCurrency()
69
+ }).format(num);
70
+ }
71
+ function formatDate(value) {
72
+ if (!value) return "";
73
+ try {
74
+ return new Intl.DateTimeFormat(getActiveLocale(), { dateStyle: "medium" }).format(new Date(value));
75
+ } catch {
76
+ return String(value);
77
+ }
78
+ }
79
+ function formatDateTime(value) {
80
+ if (!value) return "";
81
+ try {
82
+ return new Intl.DateTimeFormat(getActiveLocale(), { dateStyle: "medium", timeStyle: "short" }).format(new Date(value));
83
+ } catch {
84
+ return String(value);
85
+ }
86
+ }
87
+
88
+ // src/lib/csv.ts
89
+ function escapeCSVField(value) {
90
+ if (value == null) return "";
91
+ const str = String(value);
92
+ if (str.includes(",") || str.includes('"') || str.includes("\n")) {
93
+ return `"${str.replace(/"/g, '""')}"`;
94
+ }
95
+ return str;
96
+ }
97
+ function buildCSV(columns, rows) {
98
+ const header = columns.map((c) => escapeCSVField(c.label)).join(",");
99
+ const body = rows.map(
100
+ (row) => columns.map((col) => {
101
+ const raw = row[col.key];
102
+ const formatted = col.format ? col.format(raw, row) : raw;
103
+ return escapeCSVField(formatted);
104
+ }).join(",")
105
+ );
106
+ return [header, ...body].join("\n");
107
+ }
108
+ function downloadCSV(csv, filename) {
109
+ const bom = "\uFEFF";
110
+ const blob = new Blob([bom + csv], { type: "text/csv;charset=utf-8;" });
111
+ const url = URL.createObjectURL(blob);
112
+ const a = document.createElement("a");
113
+ a.href = url;
114
+ a.download = filename.endsWith(".csv") ? filename : `${filename}.csv`;
115
+ a.click();
116
+ URL.revokeObjectURL(url);
117
+ }
118
+ function exportCSV(columns, rows, filename) {
119
+ downloadCSV(buildCSV(columns, rows), filename);
120
+ }
121
+ function createEventBus() {
122
+ const handlers = /* @__PURE__ */ new Map();
123
+ const bus = {
124
+ emit(event, payload) {
125
+ const set = handlers.get(event);
126
+ if (!set) return;
127
+ for (const handler of Array.from(set)) {
128
+ try {
129
+ handler(payload);
130
+ } catch (err) {
131
+ console.error(`[fayz] event handler for "${event}" threw:`, err);
132
+ }
133
+ }
134
+ },
135
+ on(event, handler) {
136
+ let set = handlers.get(event);
137
+ if (!set) {
138
+ set = /* @__PURE__ */ new Set();
139
+ handlers.set(event, set);
140
+ }
141
+ set.add(handler);
142
+ return () => bus.off(event, handler);
143
+ },
144
+ once(event, handler) {
145
+ const wrapped = (payload) => {
146
+ bus.off(event, wrapped);
147
+ handler(payload);
148
+ };
149
+ return bus.on(event, wrapped);
150
+ },
151
+ off(event, handler) {
152
+ handlers.get(event)?.delete(handler);
153
+ },
154
+ clear(event) {
155
+ if (event) handlers.delete(event);
156
+ else handlers.clear();
157
+ }
158
+ };
159
+ return bus;
160
+ }
161
+ var eventBus = createEventBus();
162
+ function useOnEvent(event, handler, deps = []) {
163
+ const ref = React2.useRef(handler);
164
+ ref.current = handler;
165
+ React2.useEffect(
166
+ () => eventBus.on(event, (payload) => ref.current(payload)),
167
+ // eslint-disable-next-line react-hooks/exhaustive-deps
168
+ [event, ...deps]
169
+ );
170
+ }
171
+ function UnknownBlock({ type }) {
172
+ return React2.createElement(
173
+ "div",
174
+ {
175
+ style: {
176
+ border: "1px dashed #c026d3",
177
+ color: "#a21caf",
178
+ padding: "8px 12px",
179
+ font: "12px ui-monospace, monospace",
180
+ borderRadius: 6
181
+ }
182
+ },
183
+ `Unknown block: "${type}"`
184
+ );
185
+ }
186
+ function BlockRenderer({ node }) {
187
+ const Comp = getBlock(node.type);
188
+ const childEls = node.children?.length ? node.children.map(
189
+ (child, i) => React2.createElement(BlockRenderer, { key: child.id ?? `${child.type}-${i}`, node: child })
190
+ ) : null;
191
+ if (!Comp) return React2.createElement(UnknownBlock, { type: node.type });
192
+ return React2.createElement(Comp, { ...node.props ?? {}, node }, childEls);
193
+ }
194
+ function renderBlocks(nodes) {
195
+ return nodes.map(
196
+ (node, i) => React2.createElement(BlockRenderer, { key: node.id ?? `${node.type}-${i}`, node })
197
+ );
198
+ }
199
+ function BlockChildren({ node }) {
200
+ if (!node?.children?.length) return null;
201
+ return renderBlocks(node.children);
202
+ }
203
+
204
+ // src/manifest/index.ts
205
+ var CURRENT_MANIFEST_VERSION = 2;
206
+ var migrations = /* @__PURE__ */ new Map();
207
+ var supportedBackendProviders = ["supabase", "fayz-api", "fayz-shop", "mock", "custom"];
208
+ var supportedPageSections = /* @__PURE__ */ new Set(["main", "secondary", "settings"]);
209
+ var allowedManifestKeys = /* @__PURE__ */ new Set([
210
+ "manifestVersion",
211
+ "id",
212
+ "name",
213
+ "backend",
214
+ "locale",
215
+ "theme",
216
+ "permissions",
217
+ "billing",
218
+ "entities",
219
+ "surfaces"
220
+ ]);
221
+ var allowedBackendKeys = /* @__PURE__ */ new Set(["provider", "projectRef", "url", "adapterId", "options"]);
222
+ var allowedSurfaceKeys = /* @__PURE__ */ new Set(["scaffold", "options", "plugins", "pages"]);
223
+ var allowedPluginRefKeys = /* @__PURE__ */ new Set(["id", "config", "enabled"]);
224
+ var allowedPageKeys = /* @__PURE__ */ new Set(["path", "label", "icon", "section", "blocks", "entity", "component", "permission"]);
225
+ var allowedPermissionKeys = /* @__PURE__ */ new Set(["feature", "action"]);
226
+ var allowedBlockKeys = /* @__PURE__ */ new Set(["type", "id", "props", "children"]);
227
+ var manifestIdPattern = /^[a-z0-9][a-z0-9-]*$/;
228
+ function registerManifestMigration(fromVersion, fn) {
229
+ migrations.set(fromVersion, fn);
230
+ }
231
+ function migrateManifest(input) {
232
+ let m = { ...input };
233
+ let version = typeof m.manifestVersion === "number" ? m.manifestVersion : 1;
234
+ if (version > CURRENT_MANIFEST_VERSION) {
235
+ throw new Error(
236
+ `Manifest version ${version} is newer than this SDK supports (${CURRENT_MANIFEST_VERSION}). Upgrade @fayz-ai/app-runtime.`
237
+ );
238
+ }
239
+ while (version < CURRENT_MANIFEST_VERSION) {
240
+ const migrate = migrations.get(version);
241
+ if (!migrate) {
242
+ throw new Error(`No manifest migration registered from version ${version}.`);
243
+ }
244
+ m = migrate(m);
245
+ version += 1;
246
+ m.manifestVersion = version;
247
+ }
248
+ return m;
249
+ }
250
+ function validateManifest(m) {
251
+ const problems = [];
252
+ const manifest = m;
253
+ addUnsupportedKeys(manifest, allowedManifestKeys, "manifest", problems);
254
+ if (!Number.isInteger(manifest.manifestVersion)) {
255
+ problems.push("manifest.manifestVersion must be an integer");
256
+ } else if (manifest.manifestVersion !== CURRENT_MANIFEST_VERSION) {
257
+ problems.push(`manifest.manifestVersion must be ${CURRENT_MANIFEST_VERSION}`);
258
+ }
259
+ if (typeof manifest.id !== "string" || !manifest.id.trim()) problems.push("manifest.id is required");
260
+ else if (!manifestIdPattern.test(manifest.id.trim())) {
261
+ problems.push("manifest.id must match /^[a-z0-9][a-z0-9-]*$/");
262
+ }
263
+ if (typeof manifest.name !== "string" || !manifest.name.trim()) problems.push("manifest.name is required");
264
+ validateLooseObject(manifest.locale, "manifest.locale", problems);
265
+ validateLooseObject(manifest.theme, "manifest.theme", problems);
266
+ validateLooseObject(manifest.permissions, "manifest.permissions", problems);
267
+ validateLooseObject(manifest.billing, "manifest.billing", problems);
268
+ if (manifest.entities !== void 0) {
269
+ if (!Array.isArray(manifest.entities)) {
270
+ problems.push("manifest.entities must be an array");
271
+ } else {
272
+ manifest.entities.forEach((entity, index) => {
273
+ if (!isRecord(entity)) problems.push(`manifest.entities[${index}] must be an object`);
274
+ });
275
+ }
276
+ }
277
+ if (manifest.backend !== void 0) {
278
+ if (!isRecord(manifest.backend)) {
279
+ problems.push("manifest.backend must be an object");
280
+ } else {
281
+ addUnsupportedKeys(manifest.backend, allowedBackendKeys, "manifest.backend", problems);
282
+ const provider = manifest.backend.provider;
283
+ if (typeof provider !== "string" || !supportedBackendProviders.includes(provider)) {
284
+ problems.push(`manifest.backend.provider "${String(provider)}" is not supported`);
285
+ }
286
+ validateOptionalNonEmptyString(manifest.backend.projectRef, "manifest.backend.projectRef", problems);
287
+ validateOptionalNonEmptyString(manifest.backend.url, "manifest.backend.url", problems);
288
+ if (provider === "custom" && !isNonEmptyString(manifest.backend.adapterId)) {
289
+ problems.push('manifest.backend.adapterId is required when provider is "custom"');
290
+ } else if (provider !== "custom") {
291
+ validateOptionalNonEmptyString(manifest.backend.adapterId, "manifest.backend.adapterId", problems);
292
+ }
293
+ validateLooseObject(manifest.backend.options, "manifest.backend.options", problems);
294
+ }
295
+ }
296
+ if (!isRecord(manifest.surfaces) || Object.keys(manifest.surfaces).length === 0) {
297
+ problems.push("manifest.surfaces must declare at least one surface");
298
+ } else {
299
+ for (const [name, surface] of Object.entries(manifest.surfaces)) {
300
+ validateSurfaceManifest(name, surface, problems);
301
+ }
302
+ }
303
+ return problems;
304
+ }
305
+ function validateSurfaceManifest(name, surface, problems) {
306
+ if (!isRecord(surface)) {
307
+ problems.push(`surface "${name}" must be an object`);
308
+ return;
309
+ }
310
+ addUnsupportedKeys(surface, allowedSurfaceKeys, `surface "${name}"`, problems);
311
+ if (!isNonEmptyString(surface.scaffold)) problems.push(`surface "${name}" is missing a scaffold id`);
312
+ validateLooseObject(surface.options, `surface "${name}".options`, problems);
313
+ if (surface.pages !== void 0) {
314
+ if (!Array.isArray(surface.pages)) {
315
+ problems.push(`surface "${name}".pages must be an array`);
316
+ } else {
317
+ const pagePaths = /* @__PURE__ */ new Set();
318
+ for (const [index, page] of surface.pages.entries()) {
319
+ validatePageManifest(name, index, page, pagePaths, problems);
320
+ }
321
+ }
322
+ }
323
+ if (surface.plugins !== void 0) {
324
+ if (!Array.isArray(surface.plugins)) {
325
+ problems.push(`surface "${name}".plugins must be an array`);
326
+ } else {
327
+ const pluginIds = /* @__PURE__ */ new Set();
328
+ for (const [index, plugin] of surface.plugins.entries()) {
329
+ validatePluginRef(name, index, plugin, pluginIds, problems);
330
+ }
331
+ }
332
+ }
333
+ }
334
+ function validatePageManifest(surfaceName, index, page, pagePaths, problems) {
335
+ const pageLabel = `surface "${surfaceName}" page #${index + 1}`;
336
+ if (!isRecord(page)) {
337
+ problems.push(`${pageLabel} must be an object`);
338
+ return;
339
+ }
340
+ addUnsupportedKeys(page, allowedPageKeys, pageLabel, problems);
341
+ if (!isNonEmptyString(page.path)) {
342
+ problems.push(`${pageLabel} has an empty path`);
343
+ } else {
344
+ const normalizedPath = page.path.trim();
345
+ if (pagePaths.has(normalizedPath)) {
346
+ problems.push(`surface "${surfaceName}" declares duplicate page path "${normalizedPath}"`);
347
+ } else {
348
+ pagePaths.add(normalizedPath);
349
+ }
350
+ }
351
+ if (page.label !== void 0 && typeof page.label !== "string") problems.push(`${pageLabel}.label must be a string`);
352
+ if (page.icon !== void 0 && typeof page.icon !== "string") problems.push(`${pageLabel}.icon must be a string`);
353
+ if (page.section !== void 0 && (typeof page.section !== "string" || !supportedPageSections.has(page.section))) {
354
+ problems.push(`${pageLabel}.section must be one of main, secondary, settings`);
355
+ }
356
+ if (page.entity !== void 0 && !isNonEmptyString(page.entity)) problems.push(`${pageLabel}.entity must be a non-empty string`);
357
+ if (page.component !== void 0 && !isNonEmptyString(page.component)) {
358
+ problems.push(`${pageLabel}.component must be a non-empty string`);
359
+ }
360
+ const kinds = [page.blocks, page.entity, page.component].filter((x) => x != null);
361
+ if (kinds.length !== 1) {
362
+ problems.push(`${pageLabel} must set exactly one of blocks/entity/component`);
363
+ }
364
+ if (page.permission !== void 0) {
365
+ validatePermission(page.permission, `${pageLabel}.permission`, problems);
366
+ }
367
+ if (page.blocks !== void 0) {
368
+ if (!Array.isArray(page.blocks)) {
369
+ problems.push(`${pageLabel}.blocks must be an array`);
370
+ } else {
371
+ page.blocks.forEach((block, blockIndex) => {
372
+ validateBlockManifest(block, `${pageLabel}.blocks[${blockIndex}]`, problems);
373
+ });
374
+ }
375
+ }
376
+ }
377
+ function validatePluginRef(surfaceName, index, plugin, pluginIds, problems) {
378
+ const pluginLabel = `surface "${surfaceName}" plugin #${index + 1}`;
379
+ if (!isRecord(plugin)) {
380
+ problems.push(`${pluginLabel} must be an object`);
381
+ return;
382
+ }
383
+ addUnsupportedKeys(plugin, allowedPluginRefKeys, pluginLabel, problems);
384
+ if (!isNonEmptyString(plugin.id)) {
385
+ problems.push(`${pluginLabel} has an empty id`);
386
+ } else {
387
+ const normalizedId = plugin.id.trim();
388
+ if (pluginIds.has(normalizedId)) {
389
+ problems.push(`surface "${surfaceName}" declares duplicate plugin id "${normalizedId}"`);
390
+ } else {
391
+ pluginIds.add(normalizedId);
392
+ }
393
+ }
394
+ validateLooseObject(plugin.config, `${pluginLabel}.config`, problems);
395
+ if (plugin.enabled !== void 0 && typeof plugin.enabled !== "boolean") {
396
+ problems.push(`${pluginLabel}.enabled must be a boolean`);
397
+ }
398
+ }
399
+ function validatePermission(permission, path, problems) {
400
+ if (!isRecord(permission)) {
401
+ problems.push(`${path} must be an object`);
402
+ return;
403
+ }
404
+ addUnsupportedKeys(permission, allowedPermissionKeys, path, problems);
405
+ if (!isNonEmptyString(permission.feature)) problems.push(`${path}.feature is required`);
406
+ if (!isNonEmptyString(permission.action)) problems.push(`${path}.action is required`);
407
+ }
408
+ function validateBlockManifest(block, path, problems) {
409
+ if (!isRecord(block)) {
410
+ problems.push(`${path} must be an object`);
411
+ return;
412
+ }
413
+ addUnsupportedKeys(block, allowedBlockKeys, path, problems);
414
+ if (!isNonEmptyString(block.type)) problems.push(`${path}.type is required`);
415
+ validateLooseObject(block.props, `${path}.props`, problems);
416
+ if (block.id !== void 0 && typeof block.id !== "string") problems.push(`${path}.id must be a string`);
417
+ if (block.children !== void 0) {
418
+ if (!Array.isArray(block.children)) {
419
+ problems.push(`${path}.children must be an array`);
420
+ } else {
421
+ block.children.forEach((child, index) => validateBlockManifest(child, `${path}.children[${index}]`, problems));
422
+ }
423
+ }
424
+ }
425
+ function addUnsupportedKeys(value, allowedKeys, path, problems) {
426
+ for (const key of Object.keys(value)) {
427
+ if (!allowedKeys.has(key)) {
428
+ problems.push(`${path}.${key} is not part of AppManifest v2`);
429
+ }
430
+ }
431
+ }
432
+ function validateLooseObject(value, path, problems) {
433
+ if (value !== void 0 && !isRecord(value)) {
434
+ problems.push(`${path} must be an object`);
435
+ }
436
+ }
437
+ function validateOptionalNonEmptyString(value, path, problems) {
438
+ if (value !== void 0 && !isNonEmptyString(value)) {
439
+ problems.push(`${path} must be a non-empty string`);
440
+ }
441
+ }
442
+ function isRecord(value) {
443
+ return typeof value === "object" && value !== null && !Array.isArray(value);
444
+ }
445
+ function isNonEmptyString(value) {
446
+ return typeof value === "string" && value.trim().length > 0;
447
+ }
448
+
449
+ // src/manifest/app-manifest.schema.json
450
+ var app_manifest_schema_default = {
451
+ $schema: "https://json-schema.org/draft/2020-12/schema",
452
+ $id: "https://fayz.ai/schemas/app-manifest/v2.json",
453
+ title: "Fayz AppManifest",
454
+ description: "The canonical, JSON-serializable definition of a Fayz app. Code is referenced by registry id; never inlined.",
455
+ type: "object",
456
+ required: ["manifestVersion", "id", "name", "surfaces"],
457
+ additionalProperties: false,
458
+ properties: {
459
+ manifestVersion: {
460
+ type: "integer",
461
+ const: 2,
462
+ description: "Current AppManifest schema version. Older or newer versions require an explicit SDK migration before validation."
463
+ },
464
+ id: { type: "string", minLength: 1, pattern: "^[a-z0-9][a-z0-9-]*$" },
465
+ name: { type: "string", minLength: 1 },
466
+ backend: {
467
+ type: "object",
468
+ additionalProperties: false,
469
+ required: ["provider"],
470
+ properties: {
471
+ provider: { enum: ["supabase", "fayz-api", "fayz-shop", "mock", "custom"] },
472
+ projectRef: { type: "string", minLength: 1 },
473
+ url: { type: "string", minLength: 1 },
474
+ adapterId: { type: "string", minLength: 1 },
475
+ options: { type: "object" }
476
+ }
477
+ },
478
+ locale: { type: "object" },
479
+ theme: { type: "object" },
480
+ permissions: { type: "object" },
481
+ billing: { type: "object" },
482
+ entities: { type: "array", items: { type: "object" } },
483
+ surfaces: {
484
+ type: "object",
485
+ minProperties: 1,
486
+ additionalProperties: { $ref: "#/$defs/surface" }
487
+ }
488
+ },
489
+ $defs: {
490
+ surface: {
491
+ type: "object",
492
+ required: ["scaffold"],
493
+ additionalProperties: false,
494
+ properties: {
495
+ scaffold: { type: "string", minLength: 1 },
496
+ options: { type: "object" },
497
+ plugins: { type: "array", items: { $ref: "#/$defs/pluginRef" } },
498
+ pages: { type: "array", items: { $ref: "#/$defs/page" } }
499
+ }
500
+ },
501
+ pluginRef: {
502
+ type: "object",
503
+ required: ["id"],
504
+ additionalProperties: false,
505
+ properties: {
506
+ id: { type: "string", minLength: 1 },
507
+ config: { type: "object" },
508
+ enabled: { type: "boolean" }
509
+ }
510
+ },
511
+ page: {
512
+ type: "object",
513
+ required: ["path"],
514
+ additionalProperties: false,
515
+ description: "Exactly one of blocks/entity/component must be set.",
516
+ properties: {
517
+ path: { type: "string", minLength: 1 },
518
+ label: { type: "string" },
519
+ icon: { type: "string" },
520
+ section: { enum: ["main", "secondary", "settings"] },
521
+ blocks: { type: "array", items: { $ref: "#/$defs/block" } },
522
+ entity: { type: "string", minLength: 1 },
523
+ component: { type: "string", minLength: 1 },
524
+ permission: {
525
+ type: "object",
526
+ required: ["feature", "action"],
527
+ additionalProperties: false,
528
+ properties: {
529
+ feature: { type: "string", minLength: 1 },
530
+ action: { type: "string", minLength: 1 }
531
+ }
532
+ }
533
+ },
534
+ oneOf: [
535
+ { required: ["blocks"] },
536
+ { required: ["entity"] },
537
+ { required: ["component"] }
538
+ ]
539
+ },
540
+ block: {
541
+ type: "object",
542
+ required: ["type"],
543
+ additionalProperties: false,
544
+ properties: {
545
+ type: { type: "string", minLength: 1 },
546
+ id: { type: "string" },
547
+ props: { type: "object" },
548
+ children: { type: "array", items: { $ref: "#/$defs/block" } }
549
+ }
550
+ }
551
+ }
552
+ };
553
+ var ManifestContext = React2.createContext(null);
554
+ function useManifest() {
555
+ return React2.useContext(ManifestContext);
556
+ }
557
+ function defineApp(manifest) {
558
+ const full = {
559
+ ...manifest,
560
+ manifestVersion: manifest.manifestVersion ?? CURRENT_MANIFEST_VERSION
561
+ };
562
+ const problems = validateManifest(full);
563
+ if (problems.length) {
564
+ throw new Error(`Invalid AppManifest:
565
+ - ${problems.join("\n - ")}`);
566
+ }
567
+ return full;
568
+ }
569
+ function renderApp(input, opts = {}) {
570
+ const manifest = migrateManifest(input);
571
+ const problems = validateManifest(manifest);
572
+ if (problems.length) {
573
+ throw new Error(`Invalid AppManifest:
574
+ - ${problems.join("\n - ")}`);
575
+ }
576
+ const surfaceName = opts.surface ?? Object.keys(manifest.surfaces)[0];
577
+ const surface = surfaceName ? manifest.surfaces[surfaceName] : void 0;
578
+ if (!surface) {
579
+ throw new Error(`Surface "${surfaceName}" not found in manifest.`);
580
+ }
581
+ const Scaffold = getScaffold(surface.scaffold);
582
+ if (!Scaffold) {
583
+ throw new Error(
584
+ `Scaffold "${surface.scaffold}" is not registered. Import the package that provides it (e.g. @fayz-ai/saas or @fayz-ai/shop) before calling renderApp.`
585
+ );
586
+ }
587
+ return React2.createElement(
588
+ ManifestContext.Provider,
589
+ { value: manifest },
590
+ React2.createElement(Scaffold, { manifest, surface: surfaceName })
591
+ );
592
+ }
593
+
594
+ export { BlockChildren, BlockRenderer, CURRENT_MANIFEST_VERSION, WidgetZone, app_manifest_schema_default as appManifestSchema, buildCSV, createEventBus, createSafeDataProvider, defineApp, downloadCSV, eventBus, exportCSV, formatCurrency, formatDate, formatDateTime, getActiveLocale, getDefaultCurrency, migrateManifest, registerManifestMigration, renderApp, renderBlocks, setDefaultCurrency, useManifest, useOnEvent, validateManifest };
595
+ //# sourceMappingURL=index.js.map
596
+ //# sourceMappingURL=index.js.map