@fluid-app/fluid-cli-portal 0.1.58 → 0.1.59
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/{backend-dev-plugin-CA3i3_ge.mjs → backend-dev-plugin-DfP44z0B.mjs} +2 -2
- package/dist/{backend-dev-plugin-CA3i3_ge.mjs.map → backend-dev-plugin-DfP44z0B.mjs.map} +1 -1
- package/dist/index.d.mts +18 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +126 -10
- package/dist/index.mjs.map +1 -1
- package/dist/{portal-dev-plugin-DfEBjr96.mjs → portal-dev-plugin-BbXPp3ts.mjs} +2 -2
- package/dist/{portal-dev-plugin-DfEBjr96.mjs.map → portal-dev-plugin-BbXPp3ts.mjs.map} +1 -1
- package/dist/{portal-widget-dev-plugin-DUPIALuT.mjs → portal-widget-dev-plugin-CwD90Z8O.mjs} +7 -5
- package/dist/{portal-widget-dev-plugin-DUPIALuT.mjs.map → portal-widget-dev-plugin-CwD90Z8O.mjs.map} +1 -1
- package/dist/{pull-BZqZAiSF.mjs → pull-C26wQlO_.mjs} +109 -43
- package/dist/pull-C26wQlO_.mjs.map +1 -0
- package/dist/{src-CpaIqsaL.mjs → src-2znVq55R.mjs} +151 -26
- package/dist/src-2znVq55R.mjs.map +1 -0
- package/dist/vite-plugin.mjs +4 -4
- package/package.json +6 -6
- package/templates/base/skills/fluid-portal-authoring/SKILL.md +6 -2
- package/dist/pull-BZqZAiSF.mjs.map +0 -1
- package/dist/src-CpaIqsaL.mjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { F as __exportAll } from "./pull-C26wQlO_.mjs";
|
|
2
2
|
import { buildManifestFromLocalFiles } from "./build-manifest.mjs";
|
|
3
3
|
import { join, resolve, sep } from "node:path";
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -229,4 +229,4 @@ function portalDevPlugin(options) {
|
|
|
229
229
|
//#endregion
|
|
230
230
|
export { portal_dev_plugin_exports as n, portalDevPlugin as t };
|
|
231
231
|
|
|
232
|
-
//# sourceMappingURL=portal-dev-plugin-
|
|
232
|
+
//# sourceMappingURL=portal-dev-plugin-BbXPp3ts.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal-dev-plugin-DfEBjr96.mjs","names":[],"sources":["../src/vite-plugin/portal-dev-plugin.ts"],"sourcesContent":["/**\n * Vite plugin for local portal content development.\n *\n * Intercepts the manifest API request and serves content assembled from the\n * local `portal/` directory. Watches portal files for changes and triggers a\n * full page reload.\n *\n * The manifest is intercepted on BOTH paths the SDK has used across versions\n * (`/api/app/manifest` since portal-sdk 0.1.4xx, `/api/fluid_os/definitions/\n * active` before that) so a newer SDK against an older-generation app — or\n * vice versa — still gets the local draft.\n */\n\nimport { join, resolve, sep } from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { getAuthToken, getFluidApiBase } from \"@fluid-app/fluid-cli\";\nimport { buildManifestFromLocalFiles } from \"./build-manifest.js\";\n\n/** Options for the portal dev plugin. */\nexport interface PortalDevPluginOptions {\n portalDir?: string;\n /**\n * When true (default), dev-server requests are forced same-origin: the\n * plugin blanks `import.meta.env.VITE_API_URL` in serve mode and proxies\n * `/api` to the configured Fluid host instead. Without this, a `.env`\n * pointing VITE_API_URL at a remote API makes the SPA fetch its manifest\n * cross-origin — silently bypassing this plugin and rendering the remote\n * app instead of the local `portal/` draft. Set false to opt out and let\n * the SPA call the remote API directly.\n */\n sameOriginDev?: boolean;\n /**\n * Override the `/api` dev-proxy target. Defaults to VITE_API_URL from the\n * process env / .env files, then the authenticated company's\n * `<subdomain>.portal.fluid.app` tenant BFF, then https://api.fluid.app.\n */\n apiProxyTarget?: string;\n /**\n * Override tenant-target discovery. Primarily useful to keep embedded plugin\n * integrations and tests deterministic.\n */\n resolveTenantApiTarget?: () => Promise<string | undefined>;\n}\n\nconst DEBOUNCE_MS = 100;\n\n/** Manifest endpoints across SDK generations — all serve the local draft. */\nconst MANIFEST_PATHS = [\n \"/api/app/manifest\",\n \"/api/fluid_os/definitions/active\",\n];\n\nconst DEFAULT_API_URL = \"https://api.fluid.app\";\nconst TENANT_LOOKUP_TIMEOUT_MS = 3_000;\nconst tenantTargetPromises = new WeakMap<\n () => Promise<string | undefined>,\n Promise<string | undefined>\n>();\n\ninterface CompanyMeResponse {\n data?: {\n company?: {\n subdomain?: unknown;\n };\n };\n}\n\nexport interface TenantApiTargetDependencies {\n token?: string | null;\n apiBase?: string;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n}\n\n/**\n * Resolve the portal tenant BFF for the CLI's project-aware Fluid profile.\n *\n * Portal member APIs do not live on api.fluid.app or `<tenant>.fluid.app`.\n * Their OpenAPI server is `<tenant>.portal.fluid.app`, and using either of\n * the former hosts produces a wall of unrelated 404s in otherwise-valid\n * local portals.\n */\nexport async function resolvePortalTenantApiTarget(\n dependencies: TenantApiTargetDependencies = {},\n): Promise<string | undefined> {\n const token =\n dependencies.token !== undefined ? dependencies.token : getAuthToken();\n if (!token) return undefined;\n\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(),\n dependencies.timeoutMs ?? TENANT_LOOKUP_TIMEOUT_MS,\n );\n\n try {\n const response = await (dependencies.fetchImpl ?? fetch)(\n `${(dependencies.apiBase ?? getFluidApiBase()).replace(/\\/+$/, \"\")}/api/company/v1/companies/me`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/json\",\n },\n signal: controller.signal,\n },\n );\n if (!response.ok) return undefined;\n\n const body = (await response.json()) as CompanyMeResponse;\n const subdomain = body.data?.company?.subdomain;\n if (\n typeof subdomain !== \"string\" ||\n !/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(subdomain)\n ) {\n return undefined;\n }\n\n return `https://${subdomain}.portal.fluid.app`;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Fallback .env reader for VITE_API_URL, used only when vite's `loadEnv`\n * can't be imported (it's a declared dependency, so this is defensive — but\n * a broken install must degrade to \"wrong host\" rather than \"dev server\n * won't boot\"). Mirrors the basic lookup order: live env > .env.local >\n * .env. Unlike loadEnv it ignores .env.[mode] files and variable expansion.\n */\nfunction readViteApiUrlFallback(root: string): string | undefined {\n if (process.env.VITE_API_URL) return process.env.VITE_API_URL;\n if (process.env.NEXT_PUBLIC_API_BASE_URL) {\n return process.env.NEXT_PUBLIC_API_BASE_URL;\n }\n for (const file of [\".env.local\", \".env\"]) {\n const path = join(root, file);\n if (!existsSync(path)) continue;\n for (const line of readFileSync(path, \"utf8\").split(\"\\n\")) {\n // Quoted values stop at the closing `\"`; unquoted values stop before\n // an inline ` # comment` tail (dotenv semantics — a trailing comment\n // must not leak into the proxy target URL).\n const match =\n /^\\s*VITE_API_URL\\s*=\\s*(?:\"([^\"]*)\"|(.*?))\\s*(?:#.*)?$/.exec(line);\n const value = match?.[1] ?? match?.[2];\n if (value) return value;\n }\n }\n return undefined;\n}\n\nfunction storefrontSubdomainFromTarget(target: string): string | undefined {\n try {\n const hostname = new URL(target).hostname;\n const match = /^([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\\.fluid\\.app$/.exec(\n hostname,\n );\n const subdomain = match?.[1];\n if (\n !subdomain ||\n [\"admin\", \"api\", \"app\", \"docs\", \"portal\", \"upload\"].includes(subdomain)\n ) {\n return undefined;\n }\n return subdomain;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolve VITE_API_URL the way vite itself would — `loadEnv` honors\n * .env.[mode], .env.[mode].local, envDir, and variable expansion, which the\n * fallback parser does not (a mode-specific staging API must not silently\n * fall back to production).\n */\nasync function resolveViteApiUrl(\n root: string,\n mode: string,\n envDir: string | undefined,\n): Promise<string | undefined> {\n try {\n const { loadEnv } = await import(\"vite\");\n const env = loadEnv(mode, envDir ?? root, [\"VITE_\", \"NEXT_PUBLIC_\"]);\n return (\n env.VITE_API_URL ||\n process.env.VITE_API_URL ||\n // Portals migrated from Next.js conventions configure the API host as\n // NEXT_PUBLIC_API_BASE_URL — honored by `fluid portal dev` since it\n // was introduced, so it stays a recognized source here.\n env.NEXT_PUBLIC_API_BASE_URL ||\n process.env.NEXT_PUBLIC_API_BASE_URL ||\n undefined\n );\n } catch {\n return readViteApiUrlFallback(root);\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function portalDevPlugin(options?: PortalDevPluginOptions): any {\n let portalDir: string;\n let cachedManifest: string | null = null;\n let buildInFlight: Promise<string> | null = null;\n /** Bumped on every watched file change. A build carries the value it\n * started under; if it no longer matches when the build resolves, the\n * build read pre-edit state and must not be cached. */\n let manifestGeneration = 0;\n const resolveTenantTarget =\n options?.resolveTenantApiTarget ?? resolvePortalTenantApiTarget;\n\n return {\n name: \"fluid-portal-dev\",\n enforce: \"pre\" as const,\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async config(config: any, env: any) {\n if (env?.command !== \"serve\" || options?.sameOriginDev === false) {\n return {};\n }\n\n const root: string = config.root ?? process.cwd();\n const appDefinesProxy = config.server?.proxy?.[\"/api\"] !== undefined;\n const configuredTarget =\n options?.apiProxyTarget || appDefinesProxy\n ? undefined\n : await resolveViteApiUrl(\n root,\n env?.mode ?? \"development\",\n config.envDir,\n );\n const tenantTarget =\n options?.apiProxyTarget || appDefinesProxy || configuredTarget\n ? undefined\n : await (() => {\n const existing = tenantTargetPromises.get(resolveTenantTarget);\n if (existing) return existing;\n\n const pending = resolveTenantTarget();\n tenantTargetPromises.set(resolveTenantTarget, pending);\n return pending;\n })();\n const target =\n options?.apiProxyTarget ??\n configuredTarget ??\n tenantTarget ??\n DEFAULT_API_URL;\n const storefrontSubdomain = storefrontSubdomainFromTarget(target);\n\n if (tenantTarget) {\n console.log(`[fluid-portal-dev] Tenant API proxy: ${tenantTarget}`);\n console.log(\n \"[fluid-portal-dev] Member data still requires a portal handoff session; localhost requests may return 401.\",\n );\n } else if (\n !options?.apiProxyTarget &&\n !appDefinesProxy &&\n !configuredTarget\n ) {\n console.warn(\n `[fluid-portal-dev] Could not resolve the company tenant API; falling back to ${DEFAULT_API_URL}. ` +\n \"Built-in member screens may not load. Set VITE_API_URL explicitly or sign in with the project's Fluid profile.\",\n );\n } else if (storefrontSubdomain) {\n console.warn(\n `[fluid-portal-dev] ${target} is the storefront host, not the Portal tenant BFF. ` +\n `Built-in member APIs live at https://${storefrontSubdomain}.portal.fluid.app; remove the override to let the CLI resolve it.`,\n );\n }\n\n // Vite merges a plugin's returned config OVER the user config, so an\n // app's own explicit define / /api proxy is only preserved by checking\n // for it and yielding here. BOTH API-host env vars must be blanked:\n // dev.ts's envPrefix exposes NEXT_PUBLIC_API_BASE_URL to the SPA too,\n // so leaving it defined would recreate the exact cross-origin bypass\n // this hook exists to prevent, just for Next.js-migrated portals.\n const defines: Record<string, string> = {};\n for (const key of [\n \"import.meta.env.VITE_API_URL\",\n \"import.meta.env.NEXT_PUBLIC_API_BASE_URL\",\n ]) {\n if (config.define?.[key] === undefined) {\n defines[key] = JSON.stringify(\"\");\n }\n }\n const proxy = !appDefinesProxy\n ? { \"/api\": { target, changeOrigin: true } }\n : {};\n\n return {\n define: defines,\n server: { proxy },\n };\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n configResolved(config: any) {\n const root: string = config.root ?? process.cwd();\n portalDir = options?.portalDir\n ? resolve(root, options.portalDir)\n : join(root, \"portal\");\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n configureServer(server: any) {\n // Idempotency: `fluid portal dev` injects this plugin, and a portal's\n // own vite.config may also wire it. Only the first instance registers\n // the watcher + middleware so we don't double-reload or double-serve.\n if (server.__fluidPortalDevWired) return;\n server.__fluidPortalDevWired = true;\n\n const watchDir = portalDir;\n\n if (existsSync(watchDir)) {\n server.watcher.add(watchDir);\n\n let reloadTimer: ReturnType<typeof setTimeout> | null = null;\n\n const onFileChange = (filePath: string) => {\n if (!filePath.startsWith(watchDir)) return;\n\n cachedManifest = null;\n buildInFlight = null;\n manifestGeneration += 1;\n\n console.log(\n `[fluid-portal-dev] File changed: ${filePath.replace(watchDir + sep, \"\")}`,\n );\n\n if (reloadTimer) clearTimeout(reloadTimer);\n reloadTimer = setTimeout(() => {\n reloadTimer = null;\n server.ws.send({ type: \"full-reload\", path: \"*\" });\n }, DEBOUNCE_MS);\n };\n\n server.watcher.on(\"change\", onFileChange);\n server.watcher.on(\"add\", onFileChange);\n server.watcher.on(\"unlink\", onFileChange);\n }\n\n // ── Middleware to intercept manifest requests ───────────────────\n // Every MANIFEST_PATHS route returns the SAME `{ manifest }` payload:\n // the portal-tenant BFF route the current SDK runtime fetches\n // (useFluidApp → app_manifest_show) and the legacy FluidOS route, so\n // the local portal/ draft feeds the running app regardless of which\n // runtime generation it uses (the SDK adapter reads\n // `response.manifest`, matching buildManifestFromLocalFiles' output).\n server.middlewares.use(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (req: any, res: any, next: () => void) => {\n const url: string | undefined = req.url;\n\n if (!url || !MANIFEST_PATHS.some((path) => url.includes(path))) {\n next();\n return;\n }\n\n (async () => {\n try {\n if (!existsSync(portalDir)) {\n // Fall through to the Vite proxy so requests reach the real\n // API when local portal content is unavailable (e.g. --skip-pull).\n next();\n return;\n }\n\n let manifestJson = cachedManifest;\n if (manifestJson == null) {\n if (!buildInFlight) {\n // Stamp the build with the generation it was started\n // under. onFileChange bumps that counter, so a build\n // whose inputs changed underneath it can be recognized\n // when it finally resolves.\n const generation = manifestGeneration;\n buildInFlight = buildManifestFromLocalFiles(portalDir)\n .then((m) => {\n const json = JSON.stringify(m);\n // Only a build still current may populate the\n // cache. Caching a superseded one pinned the\n // pre-edit snapshot permanently: a non-null cache\n // means no later request ever rebuilds, so the\n // edit stayed invisible until some unrelated file\n // happened to change outside a build window.\n if (generation === manifestGeneration) {\n cachedManifest = json;\n buildInFlight = null;\n }\n return json;\n })\n .catch((err) => {\n // Same guard: a superseded build must not clear a\n // newer one's handle.\n if (generation === manifestGeneration) {\n buildInFlight = null; // allow retry on next request\n }\n throw err;\n });\n }\n // This response serves what was built for it, which is\n // correct even when superseded — the request predates\n // the edit. The point is that the NEXT one rebuilds.\n manifestJson = await buildInFlight;\n }\n\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"X-Portal-Dev-Mode\": \"true\",\n \"Cache-Control\": \"no-store\",\n });\n res.end(manifestJson);\n } catch (err) {\n console.error(\"[fluid-portal-dev] Error building manifest:\", err);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error:\n err instanceof Error\n ? err.message\n : \"Failed to build manifest from local files\",\n }),\n );\n }\n })();\n },\n );\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"portal-dev-plugin-BbXPp3ts.mjs","names":[],"sources":["../src/vite-plugin/portal-dev-plugin.ts"],"sourcesContent":["/**\n * Vite plugin for local portal content development.\n *\n * Intercepts the manifest API request and serves content assembled from the\n * local `portal/` directory. Watches portal files for changes and triggers a\n * full page reload.\n *\n * The manifest is intercepted on BOTH paths the SDK has used across versions\n * (`/api/app/manifest` since portal-sdk 0.1.4xx, `/api/fluid_os/definitions/\n * active` before that) so a newer SDK against an older-generation app — or\n * vice versa — still gets the local draft.\n */\n\nimport { join, resolve, sep } from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { getAuthToken, getFluidApiBase } from \"@fluid-app/fluid-cli\";\nimport { buildManifestFromLocalFiles } from \"./build-manifest.js\";\n\n/** Options for the portal dev plugin. */\nexport interface PortalDevPluginOptions {\n portalDir?: string;\n /**\n * When true (default), dev-server requests are forced same-origin: the\n * plugin blanks `import.meta.env.VITE_API_URL` in serve mode and proxies\n * `/api` to the configured Fluid host instead. Without this, a `.env`\n * pointing VITE_API_URL at a remote API makes the SPA fetch its manifest\n * cross-origin — silently bypassing this plugin and rendering the remote\n * app instead of the local `portal/` draft. Set false to opt out and let\n * the SPA call the remote API directly.\n */\n sameOriginDev?: boolean;\n /**\n * Override the `/api` dev-proxy target. Defaults to VITE_API_URL from the\n * process env / .env files, then the authenticated company's\n * `<subdomain>.portal.fluid.app` tenant BFF, then https://api.fluid.app.\n */\n apiProxyTarget?: string;\n /**\n * Override tenant-target discovery. Primarily useful to keep embedded plugin\n * integrations and tests deterministic.\n */\n resolveTenantApiTarget?: () => Promise<string | undefined>;\n}\n\nconst DEBOUNCE_MS = 100;\n\n/** Manifest endpoints across SDK generations — all serve the local draft. */\nconst MANIFEST_PATHS = [\n \"/api/app/manifest\",\n \"/api/fluid_os/definitions/active\",\n];\n\nconst DEFAULT_API_URL = \"https://api.fluid.app\";\nconst TENANT_LOOKUP_TIMEOUT_MS = 3_000;\nconst tenantTargetPromises = new WeakMap<\n () => Promise<string | undefined>,\n Promise<string | undefined>\n>();\n\ninterface CompanyMeResponse {\n data?: {\n company?: {\n subdomain?: unknown;\n };\n };\n}\n\nexport interface TenantApiTargetDependencies {\n token?: string | null;\n apiBase?: string;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n}\n\n/**\n * Resolve the portal tenant BFF for the CLI's project-aware Fluid profile.\n *\n * Portal member APIs do not live on api.fluid.app or `<tenant>.fluid.app`.\n * Their OpenAPI server is `<tenant>.portal.fluid.app`, and using either of\n * the former hosts produces a wall of unrelated 404s in otherwise-valid\n * local portals.\n */\nexport async function resolvePortalTenantApiTarget(\n dependencies: TenantApiTargetDependencies = {},\n): Promise<string | undefined> {\n const token =\n dependencies.token !== undefined ? dependencies.token : getAuthToken();\n if (!token) return undefined;\n\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(),\n dependencies.timeoutMs ?? TENANT_LOOKUP_TIMEOUT_MS,\n );\n\n try {\n const response = await (dependencies.fetchImpl ?? fetch)(\n `${(dependencies.apiBase ?? getFluidApiBase()).replace(/\\/+$/, \"\")}/api/company/v1/companies/me`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/json\",\n },\n signal: controller.signal,\n },\n );\n if (!response.ok) return undefined;\n\n const body = (await response.json()) as CompanyMeResponse;\n const subdomain = body.data?.company?.subdomain;\n if (\n typeof subdomain !== \"string\" ||\n !/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(subdomain)\n ) {\n return undefined;\n }\n\n return `https://${subdomain}.portal.fluid.app`;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Fallback .env reader for VITE_API_URL, used only when vite's `loadEnv`\n * can't be imported (it's a declared dependency, so this is defensive — but\n * a broken install must degrade to \"wrong host\" rather than \"dev server\n * won't boot\"). Mirrors the basic lookup order: live env > .env.local >\n * .env. Unlike loadEnv it ignores .env.[mode] files and variable expansion.\n */\nfunction readViteApiUrlFallback(root: string): string | undefined {\n if (process.env.VITE_API_URL) return process.env.VITE_API_URL;\n if (process.env.NEXT_PUBLIC_API_BASE_URL) {\n return process.env.NEXT_PUBLIC_API_BASE_URL;\n }\n for (const file of [\".env.local\", \".env\"]) {\n const path = join(root, file);\n if (!existsSync(path)) continue;\n for (const line of readFileSync(path, \"utf8\").split(\"\\n\")) {\n // Quoted values stop at the closing `\"`; unquoted values stop before\n // an inline ` # comment` tail (dotenv semantics — a trailing comment\n // must not leak into the proxy target URL).\n const match =\n /^\\s*VITE_API_URL\\s*=\\s*(?:\"([^\"]*)\"|(.*?))\\s*(?:#.*)?$/.exec(line);\n const value = match?.[1] ?? match?.[2];\n if (value) return value;\n }\n }\n return undefined;\n}\n\nfunction storefrontSubdomainFromTarget(target: string): string | undefined {\n try {\n const hostname = new URL(target).hostname;\n const match = /^([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\\.fluid\\.app$/.exec(\n hostname,\n );\n const subdomain = match?.[1];\n if (\n !subdomain ||\n [\"admin\", \"api\", \"app\", \"docs\", \"portal\", \"upload\"].includes(subdomain)\n ) {\n return undefined;\n }\n return subdomain;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolve VITE_API_URL the way vite itself would — `loadEnv` honors\n * .env.[mode], .env.[mode].local, envDir, and variable expansion, which the\n * fallback parser does not (a mode-specific staging API must not silently\n * fall back to production).\n */\nasync function resolveViteApiUrl(\n root: string,\n mode: string,\n envDir: string | undefined,\n): Promise<string | undefined> {\n try {\n const { loadEnv } = await import(\"vite\");\n const env = loadEnv(mode, envDir ?? root, [\"VITE_\", \"NEXT_PUBLIC_\"]);\n return (\n env.VITE_API_URL ||\n process.env.VITE_API_URL ||\n // Portals migrated from Next.js conventions configure the API host as\n // NEXT_PUBLIC_API_BASE_URL — honored by `fluid portal dev` since it\n // was introduced, so it stays a recognized source here.\n env.NEXT_PUBLIC_API_BASE_URL ||\n process.env.NEXT_PUBLIC_API_BASE_URL ||\n undefined\n );\n } catch {\n return readViteApiUrlFallback(root);\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function portalDevPlugin(options?: PortalDevPluginOptions): any {\n let portalDir: string;\n let cachedManifest: string | null = null;\n let buildInFlight: Promise<string> | null = null;\n /** Bumped on every watched file change. A build carries the value it\n * started under; if it no longer matches when the build resolves, the\n * build read pre-edit state and must not be cached. */\n let manifestGeneration = 0;\n const resolveTenantTarget =\n options?.resolveTenantApiTarget ?? resolvePortalTenantApiTarget;\n\n return {\n name: \"fluid-portal-dev\",\n enforce: \"pre\" as const,\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async config(config: any, env: any) {\n if (env?.command !== \"serve\" || options?.sameOriginDev === false) {\n return {};\n }\n\n const root: string = config.root ?? process.cwd();\n const appDefinesProxy = config.server?.proxy?.[\"/api\"] !== undefined;\n const configuredTarget =\n options?.apiProxyTarget || appDefinesProxy\n ? undefined\n : await resolveViteApiUrl(\n root,\n env?.mode ?? \"development\",\n config.envDir,\n );\n const tenantTarget =\n options?.apiProxyTarget || appDefinesProxy || configuredTarget\n ? undefined\n : await (() => {\n const existing = tenantTargetPromises.get(resolveTenantTarget);\n if (existing) return existing;\n\n const pending = resolveTenantTarget();\n tenantTargetPromises.set(resolveTenantTarget, pending);\n return pending;\n })();\n const target =\n options?.apiProxyTarget ??\n configuredTarget ??\n tenantTarget ??\n DEFAULT_API_URL;\n const storefrontSubdomain = storefrontSubdomainFromTarget(target);\n\n if (tenantTarget) {\n console.log(`[fluid-portal-dev] Tenant API proxy: ${tenantTarget}`);\n console.log(\n \"[fluid-portal-dev] Member data still requires a portal handoff session; localhost requests may return 401.\",\n );\n } else if (\n !options?.apiProxyTarget &&\n !appDefinesProxy &&\n !configuredTarget\n ) {\n console.warn(\n `[fluid-portal-dev] Could not resolve the company tenant API; falling back to ${DEFAULT_API_URL}. ` +\n \"Built-in member screens may not load. Set VITE_API_URL explicitly or sign in with the project's Fluid profile.\",\n );\n } else if (storefrontSubdomain) {\n console.warn(\n `[fluid-portal-dev] ${target} is the storefront host, not the Portal tenant BFF. ` +\n `Built-in member APIs live at https://${storefrontSubdomain}.portal.fluid.app; remove the override to let the CLI resolve it.`,\n );\n }\n\n // Vite merges a plugin's returned config OVER the user config, so an\n // app's own explicit define / /api proxy is only preserved by checking\n // for it and yielding here. BOTH API-host env vars must be blanked:\n // dev.ts's envPrefix exposes NEXT_PUBLIC_API_BASE_URL to the SPA too,\n // so leaving it defined would recreate the exact cross-origin bypass\n // this hook exists to prevent, just for Next.js-migrated portals.\n const defines: Record<string, string> = {};\n for (const key of [\n \"import.meta.env.VITE_API_URL\",\n \"import.meta.env.NEXT_PUBLIC_API_BASE_URL\",\n ]) {\n if (config.define?.[key] === undefined) {\n defines[key] = JSON.stringify(\"\");\n }\n }\n const proxy = !appDefinesProxy\n ? { \"/api\": { target, changeOrigin: true } }\n : {};\n\n return {\n define: defines,\n server: { proxy },\n };\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n configResolved(config: any) {\n const root: string = config.root ?? process.cwd();\n portalDir = options?.portalDir\n ? resolve(root, options.portalDir)\n : join(root, \"portal\");\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n configureServer(server: any) {\n // Idempotency: `fluid portal dev` injects this plugin, and a portal's\n // own vite.config may also wire it. Only the first instance registers\n // the watcher + middleware so we don't double-reload or double-serve.\n if (server.__fluidPortalDevWired) return;\n server.__fluidPortalDevWired = true;\n\n const watchDir = portalDir;\n\n if (existsSync(watchDir)) {\n server.watcher.add(watchDir);\n\n let reloadTimer: ReturnType<typeof setTimeout> | null = null;\n\n const onFileChange = (filePath: string) => {\n if (!filePath.startsWith(watchDir)) return;\n\n cachedManifest = null;\n buildInFlight = null;\n manifestGeneration += 1;\n\n console.log(\n `[fluid-portal-dev] File changed: ${filePath.replace(watchDir + sep, \"\")}`,\n );\n\n if (reloadTimer) clearTimeout(reloadTimer);\n reloadTimer = setTimeout(() => {\n reloadTimer = null;\n server.ws.send({ type: \"full-reload\", path: \"*\" });\n }, DEBOUNCE_MS);\n };\n\n server.watcher.on(\"change\", onFileChange);\n server.watcher.on(\"add\", onFileChange);\n server.watcher.on(\"unlink\", onFileChange);\n }\n\n // ── Middleware to intercept manifest requests ───────────────────\n // Every MANIFEST_PATHS route returns the SAME `{ manifest }` payload:\n // the portal-tenant BFF route the current SDK runtime fetches\n // (useFluidApp → app_manifest_show) and the legacy FluidOS route, so\n // the local portal/ draft feeds the running app regardless of which\n // runtime generation it uses (the SDK adapter reads\n // `response.manifest`, matching buildManifestFromLocalFiles' output).\n server.middlewares.use(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (req: any, res: any, next: () => void) => {\n const url: string | undefined = req.url;\n\n if (!url || !MANIFEST_PATHS.some((path) => url.includes(path))) {\n next();\n return;\n }\n\n (async () => {\n try {\n if (!existsSync(portalDir)) {\n // Fall through to the Vite proxy so requests reach the real\n // API when local portal content is unavailable (e.g. --skip-pull).\n next();\n return;\n }\n\n let manifestJson = cachedManifest;\n if (manifestJson == null) {\n if (!buildInFlight) {\n // Stamp the build with the generation it was started\n // under. onFileChange bumps that counter, so a build\n // whose inputs changed underneath it can be recognized\n // when it finally resolves.\n const generation = manifestGeneration;\n buildInFlight = buildManifestFromLocalFiles(portalDir)\n .then((m) => {\n const json = JSON.stringify(m);\n // Only a build still current may populate the\n // cache. Caching a superseded one pinned the\n // pre-edit snapshot permanently: a non-null cache\n // means no later request ever rebuilds, so the\n // edit stayed invisible until some unrelated file\n // happened to change outside a build window.\n if (generation === manifestGeneration) {\n cachedManifest = json;\n buildInFlight = null;\n }\n return json;\n })\n .catch((err) => {\n // Same guard: a superseded build must not clear a\n // newer one's handle.\n if (generation === manifestGeneration) {\n buildInFlight = null; // allow retry on next request\n }\n throw err;\n });\n }\n // This response serves what was built for it, which is\n // correct even when superseded — the request predates\n // the edit. The point is that the NEXT one rebuilds.\n manifestJson = await buildInFlight;\n }\n\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"X-Portal-Dev-Mode\": \"true\",\n \"Cache-Control\": \"no-store\",\n });\n res.end(manifestJson);\n } catch (err) {\n console.error(\"[fluid-portal-dev] Error building manifest:\", err);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error:\n err instanceof Error\n ? err.message\n : \"Failed to build manifest from local files\",\n }),\n );\n }\n })();\n },\n );\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/{portal-widget-dev-plugin-DUPIALuT.mjs → portal-widget-dev-plugin-CwD90Z8O.mjs}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { c as fluidWidgetDevPlugin, l as handleOptions, u as setCorsHeaders } from "./src-
|
|
1
|
+
import { F as __exportAll } from "./pull-C26wQlO_.mjs";
|
|
2
|
+
import { c as fluidWidgetDevPlugin, l as handleOptions, u as setCorsHeaders } from "./src-2znVq55R.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
//#region src/vite-plugin/portal-widget-dev-plugin.ts
|
|
@@ -122,8 +122,10 @@ function virtualWorkerEntryPlugin() {
|
|
|
122
122
|
},
|
|
123
123
|
load(id) {
|
|
124
124
|
if (id !== VIRTUAL_ENTRY_PATH) return null;
|
|
125
|
-
return `import { startWidgetPackage } from "@fluid-app/portal-sdk/widgets/worker";
|
|
126
|
-
|
|
125
|
+
return `import { prepareRemoteDomWidgetWorker, startWidgetPackage } from "@fluid-app/portal-sdk/widgets/worker";
|
|
126
|
+
|
|
127
|
+
prepareRemoteDomWidgetWorker();
|
|
128
|
+
const source = await import("${WIDGET_CONFIG_MODULE_PATH}");
|
|
127
129
|
|
|
128
130
|
const packages = source.widgetPackages;
|
|
129
131
|
const widgetPackage =
|
|
@@ -141,4 +143,4 @@ if (widgetPackage) {
|
|
|
141
143
|
//#endregion
|
|
142
144
|
export { portal_widget_dev_plugin_exports as n, portalWidgetDevPlugin as t };
|
|
143
145
|
|
|
144
|
-
//# sourceMappingURL=portal-widget-dev-plugin-
|
|
146
|
+
//# sourceMappingURL=portal-widget-dev-plugin-CwD90Z8O.mjs.map
|
package/dist/{portal-widget-dev-plugin-DUPIALuT.mjs.map → portal-widget-dev-plugin-CwD90Z8O.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal-widget-dev-plugin-
|
|
1
|
+
{"version":3,"file":"portal-widget-dev-plugin-CwD90Z8O.mjs","names":[],"sources":["../src/vite-plugin/portal-widget-dev-plugin.ts"],"sourcesContent":["/**\n * Dev routes that let a portal preview render the project's own widgets before\n * they are published.\n *\n * A portal keeps company-owned widget sources in `src/widgets.config.ts` and\n * publishes them with `fluid portal deploy`. Until that deploy runs, the widget\n * exists only on disk — the catalog has never heard of it, so a preview could\n * not show it and the only way to see your work was to publish it. Publishing\n * is not a safe iteration loop: a deploy is live immediately, there is no\n * rollback, and a duplicate `packageId@version` is ignored so every attempt\n * needs a version bump.\n *\n * These routes close that gap by serving the same two endpoints a standalone\n * widget project serves — `/__widget-packages__` (a canonical package\n * descriptor) and `/__runtime-entry__` (the worker bootstrap). A host that can\n * already render published packages needs no new code to render a local one:\n * the descriptor shape is identical, so it merges straight into the catalog\n * array.\n *\n * The route implementations are reused wholesale from\n * `@fluid-app/fluid-cli-widget-tooling`. Its bootstrap script in particular is\n * load-bearing — it exists to fix a message-buffer race that silently strands\n * worker mounts — and must not be reimplemented here.\n *\n * Imported from widget-tooling rather than from the widget CLI deliberately:\n * this package lists widget-tooling in its tsdown `alwaysBundle`, so the routes\n * compile into the portal dist. An import across published CLIs stays external\n * and resolves to un-built TypeScript at runtime.\n *\n * Two things differ from a standalone project, and both are passed as options:\n * the package source lives at `src/widgets.config.ts` rather than\n * `fluid.widget.config.ts`, and it is owned by `company` rather than `droplet`.\n * A portal also has no `startWidgetPackage()` module of its own, so this plugin\n * generates one.\n */\n\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport {\n fluidWidgetDevPlugin,\n handleOptions,\n setCorsHeaders,\n} from \"@fluid-app/fluid-cli-widget-tooling\";\nimport type { Plugin } from \"vite\";\n\n/** Package source, relative to the Vite root. */\nconst WIDGET_CONFIG_MODULE_PATH = \"/src/widgets.config.ts\";\n\n/**\n * Generated worker entry. Requestable (no `\\0` prefix) because the bootstrap\n * script imports it by URL from the dev server rather than through Vite's\n * module graph.\n */\nconst VIRTUAL_ENTRY_PATH = \"/@fluid-portal-widget-entry.js\";\n\nconst WIDGET_PACKAGES_ROUTES = new Set([\n \"/__widget-packages__\",\n \"/__widget-packages__/\",\n]);\nconst RUNTIME_ENTRY_ROUTES = new Set([\n \"/__runtime-entry__\",\n \"/__runtime-entry__/\",\n]);\n\nexport function portalWidgetDevPlugin(): Plugin[] {\n return [\n missingConfigGuardPlugin(),\n virtualWorkerEntryPlugin(),\n fluidWidgetDevPlugin({\n entryModulePath: VIRTUAL_ENTRY_PATH,\n configModulePath: WIDGET_CONFIG_MODULE_PATH,\n owner: \"company\",\n // A portal with no custom widget yet is the normal case, not a fault.\n allowNoPackages: true,\n }),\n ];\n}\n\n/**\n * Short-circuits both routes when the project has no `src/widgets.config.ts`.\n *\n * Most portals never gain one. Without this the delegated routes would try to\n * load a module that does not exist and log a resolution failure on every poll\n * — the preview polls continuously — so the expected case would be the noisiest\n * thing in the log. Registered before the delegated plugin so its middleware\n * runs first.\n */\nfunction missingConfigGuardPlugin(): Plugin {\n let configFilePath = \"\";\n\n return {\n name: \"fluid-portal-widget-dev-missing-config-guard\",\n apply: \"serve\",\n configResolved(config) {\n configFilePath = path.join(\n config.root,\n WIDGET_CONFIG_MODULE_PATH.replace(/^\\//, \"\"),\n );\n },\n configureServer(server) {\n server.middlewares.use((request, response, next): void => {\n const pathname = (request.url ?? \"\").split(\"?\")[0] ?? \"\";\n const isWidgetRoute =\n WIDGET_PACKAGES_ROUTES.has(pathname) ||\n RUNTIME_ENTRY_ROUTES.has(pathname);\n if (!isWidgetRoute) {\n next();\n return;\n }\n // Checked per request, not at startup: `fluid portal widget create`\n // can add the file while the dev server is already running.\n if (existsSync(configFilePath)) {\n next();\n return;\n }\n\n // Answer a preflight exactly as the delegated routes do. Two\n // implementations of the same route disagreeing is bad enough; the\n // guard is the one that runs in the common case, since most portals\n // never gain a widget config, so the divergence would have been the\n // DEFAULT path rather than the edge.\n if (handleOptions(request, response)) return;\n setCorsHeaders(response);\n if (WIDGET_PACKAGES_ROUTES.has(pathname)) {\n response.setHeader(\"Content-Type\", \"application/json\");\n response.statusCode = 200;\n response.end(\"[]\");\n return;\n }\n response.setHeader(\"Content-Type\", \"text/javascript\");\n response.statusCode = 200;\n response.end(\"// No portal widget package in this project.\\n\");\n });\n },\n };\n}\n\n/**\n * Serves the worker entry a portal project does not have.\n *\n * A standalone widget project ships `src/index.ts` calling\n * `startWidgetPackage()`. A portal's `src/widgets.config.ts` only declares the\n * package, so this module supplies the missing call. Export precedence matches\n * the shared descriptor builder's, so the worker starts the same package the\n * descriptor advertises.\n */\nfunction virtualWorkerEntryPlugin(): Plugin {\n return {\n name: \"fluid-portal-widget-dev-entry\",\n apply: \"serve\",\n resolveId(id) {\n return id === VIRTUAL_ENTRY_PATH ? id : null;\n },\n load(id) {\n if (id !== VIRTUAL_ENTRY_PATH) return null;\n return `import { prepareRemoteDomWidgetWorker, startWidgetPackage } from \"@fluid-app/portal-sdk/widgets/worker\";\n\nprepareRemoteDomWidgetWorker();\nconst source = await import(\"${WIDGET_CONFIG_MODULE_PATH}\");\n\nconst packages = source.widgetPackages;\nconst widgetPackage =\n (Array.isArray(packages) && packages.length > 0 ? packages[0] : undefined) ??\n source.widgetPackage ??\n source.default;\n\nif (widgetPackage) {\n startWidgetPackage(widgetPackage);\n}\n`;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,MAAM,4BAA4B;;;;;;AAOlC,MAAM,qBAAqB;AAE3B,MAAM,yBAAyB,IAAI,IAAI,CACrC,wBACA,wBACD,CAAC;AACF,MAAM,uBAAuB,IAAI,IAAI,CACnC,sBACA,sBACD,CAAC;AAEF,SAAgB,wBAAkC;AAChD,QAAO;EACL,0BAA0B;EAC1B,0BAA0B;EAC1B,qBAAqB;GACnB,iBAAiB;GACjB,kBAAkB;GAClB,OAAO;GAEP,iBAAiB;GAClB,CAAC;EACH;;;;;;;;;;;AAYH,SAAS,2BAAmC;CAC1C,IAAI,iBAAiB;AAErB,QAAO;EACL,MAAM;EACN,OAAO;EACP,eAAe,QAAQ;AACrB,oBAAiB,KAAK,KACpB,OAAO,MACP,0BAA0B,QAAQ,OAAO,GAAG,CAC7C;;EAEH,gBAAgB,QAAQ;AACtB,UAAO,YAAY,KAAK,SAAS,UAAU,SAAe;IACxD,MAAM,YAAY,QAAQ,OAAO,IAAI,MAAM,IAAI,CAAC,MAAM;AAItD,QAAI,EAFF,uBAAuB,IAAI,SAAS,IACpC,qBAAqB,IAAI,SAAS,GAChB;AAClB,WAAM;AACN;;AAIF,QAAI,WAAW,eAAe,EAAE;AAC9B,WAAM;AACN;;AAQF,QAAI,cAAc,SAAS,SAAS,CAAE;AACtC,mBAAe,SAAS;AACxB,QAAI,uBAAuB,IAAI,SAAS,EAAE;AACxC,cAAS,UAAU,gBAAgB,mBAAmB;AACtD,cAAS,aAAa;AACtB,cAAS,IAAI,KAAK;AAClB;;AAEF,aAAS,UAAU,gBAAgB,kBAAkB;AACrD,aAAS,aAAa;AACtB,aAAS,IAAI,iDAAiD;KAC9D;;EAEL;;;;;;;;;;;AAYH,SAAS,2BAAmC;AAC1C,QAAO;EACL,MAAM;EACN,OAAO;EACP,UAAU,IAAI;AACZ,UAAO,OAAO,qBAAqB,KAAK;;EAE1C,KAAK,IAAI;AACP,OAAI,OAAO,mBAAoB,QAAO;AACtC,UAAO;;;+BAGkB,0BAA0B;;;;;;;;;;;;;EAatD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as fluidOsV0UpdateFluidOsscreen, E as fluidOsV0UpdateFluidOsprofile, O as fluidOsV0UpdateFluidOstheme, S as fluidOsV0ListFluidOsversions, T as fluidOsV0UpdateFluidOsnavigationItem, _ as fluidOsV0ListFluidOsnavigationItems, a as fluidOsV0CreateFluidOsscreen, b as fluidOsV0ListFluidOsscreens, d as fluidOsV0DeleteFluidOsnavigationItem, f as fluidOsV0DeleteFluidOsprofile, g as fluidOsV0ListFluidOsdefinitions, h as fluidOsV0GetFluidOsscreen, i as fluidOsV0CreateFluidOsprofile, k as fluidOsV0UpdateFluidOsversion, m as fluidOsV0DeleteFluidOstheme, n as fluidOsV0CreateFluidOsnavigation, o as fluidOsV0CreateFluidOstheme, p as fluidOsV0DeleteFluidOsscreen, r as fluidOsV0CreateFluidOsnavigationItem, s as fluidOsV0CreateFluidOsversion, u as fluidOsV0DeleteFluidOsnavigation, v as fluidOsV0ListFluidOsnavigations, w as fluidOsV0UpdateFluidOsnavigation, x as fluidOsV0ListFluidOsthemes, y as fluidOsV0ListFluidOsprofiles } from "./sdk.gen-VRGYiLVJ.mjs";
|
|
1
|
+
import { C as fluidOsV0ListWidgetPackages, D as fluidOsV0UpdateFluidOsscreen, E as fluidOsV0UpdateFluidOsprofile, O as fluidOsV0UpdateFluidOstheme, S as fluidOsV0ListFluidOsversions, T as fluidOsV0UpdateFluidOsnavigationItem, _ as fluidOsV0ListFluidOsnavigationItems, a as fluidOsV0CreateFluidOsscreen, b as fluidOsV0ListFluidOsscreens, d as fluidOsV0DeleteFluidOsnavigationItem, f as fluidOsV0DeleteFluidOsprofile, g as fluidOsV0ListFluidOsdefinitions, h as fluidOsV0GetFluidOsscreen, i as fluidOsV0CreateFluidOsprofile, k as fluidOsV0UpdateFluidOsversion, m as fluidOsV0DeleteFluidOstheme, n as fluidOsV0CreateFluidOsnavigation, o as fluidOsV0CreateFluidOstheme, p as fluidOsV0DeleteFluidOsscreen, r as fluidOsV0CreateFluidOsnavigationItem, s as fluidOsV0CreateFluidOsversion, u as fluidOsV0DeleteFluidOsnavigation, v as fluidOsV0ListFluidOsnavigations, w as fluidOsV0UpdateFluidOsnavigation, x as fluidOsV0ListFluidOsthemes, y as fluidOsV0ListFluidOsprofiles } from "./sdk.gen-VRGYiLVJ.mjs";
|
|
2
2
|
import "node:module";
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import chalk from "chalk";
|
|
@@ -841,6 +841,7 @@ function createFluidOsClient(options) {
|
|
|
841
841
|
});
|
|
842
842
|
}
|
|
843
843
|
const fluidOsApi = {
|
|
844
|
+
listWidgetPackages: (client) => fluidOsV0ListWidgetPackages({ client }),
|
|
844
845
|
listDefinitions: (client, query) => fluidOsV0ListFluidOsdefinitions({
|
|
845
846
|
client,
|
|
846
847
|
query
|
|
@@ -1249,19 +1250,57 @@ async function diffAgainstSnapshot(portalDir, snapshot) {
|
|
|
1249
1250
|
* and records the definition metadata.
|
|
1250
1251
|
*/
|
|
1251
1252
|
async function buildSnapshot(portalDir, definitionName, definitionId) {
|
|
1252
|
-
const
|
|
1253
|
+
const files = await collectFiles(portalDir);
|
|
1254
|
+
const hashEntries = await mapWithLimit(files, MAX_CONCURRENCY, async (filePath) => {
|
|
1253
1255
|
return [filePath, await computeFileHash(join(portalDir, filePath))];
|
|
1254
1256
|
});
|
|
1255
1257
|
hashEntries.sort(([a], [b]) => a.localeCompare(b));
|
|
1256
1258
|
const fileHashes = {};
|
|
1257
1259
|
for (const [path, hash] of hashEntries) fileHashes[path] = hash;
|
|
1260
|
+
const networkWidgetGrants = await collectSnapshotNetworkWidgetGrants(portalDir, files);
|
|
1258
1261
|
return {
|
|
1259
1262
|
definition: definitionName,
|
|
1260
1263
|
definition_id: definitionId,
|
|
1261
1264
|
pulled_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1262
|
-
files: fileHashes
|
|
1265
|
+
files: fileHashes,
|
|
1266
|
+
network_widget_grants: networkWidgetGrants
|
|
1263
1267
|
};
|
|
1264
1268
|
}
|
|
1269
|
+
async function collectSnapshotNetworkWidgetGrants(portalDir, files) {
|
|
1270
|
+
const entries = await mapWithLimit(files.filter((file) => file.startsWith("screens/") && file.endsWith(".json")), MAX_CONCURRENCY, async (file) => {
|
|
1271
|
+
try {
|
|
1272
|
+
return [file, collectNetworkWidgetGrants(JSON.parse(await readFile(join(portalDir, file), "utf8")))];
|
|
1273
|
+
} catch {
|
|
1274
|
+
return [file, []];
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
return Object.fromEntries(entries.filter(([, grants]) => grants.length > 0));
|
|
1278
|
+
}
|
|
1279
|
+
function collectNetworkWidgetGrants(value) {
|
|
1280
|
+
const grants = [];
|
|
1281
|
+
const visit = (candidate) => {
|
|
1282
|
+
if (!candidate || typeof candidate !== "object") return;
|
|
1283
|
+
if (Array.isArray(candidate)) {
|
|
1284
|
+
for (const item of candidate) visit(item);
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
const record = candidate;
|
|
1288
|
+
const networkAccess = asRecord(asRecord(record.capabilityGrants)?.networkAccess);
|
|
1289
|
+
if (typeof record.id === "string" && typeof record.type === "string" && typeof networkAccess?.packageId === "string" && typeof networkAccess.packageVersion === "string" && typeof networkAccess.capabilityVersion === "string") grants.push({
|
|
1290
|
+
widgetId: record.id,
|
|
1291
|
+
widgetType: record.type,
|
|
1292
|
+
packageId: networkAccess.packageId,
|
|
1293
|
+
packageVersion: networkAccess.packageVersion,
|
|
1294
|
+
capabilityVersion: networkAccess.capabilityVersion
|
|
1295
|
+
});
|
|
1296
|
+
for (const child of Object.values(record)) visit(child);
|
|
1297
|
+
};
|
|
1298
|
+
visit(value);
|
|
1299
|
+
return grants;
|
|
1300
|
+
}
|
|
1301
|
+
function asRecord(value) {
|
|
1302
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1303
|
+
}
|
|
1265
1304
|
//#endregion
|
|
1266
1305
|
//#region ../../../node_modules/.pnpm/colorjs.io@0.5.2/node_modules/colorjs.io/dist/color.js
|
|
1267
1306
|
function multiplyMatrices(A, B) {
|
|
@@ -6692,7 +6731,7 @@ function transformProfile(profile, navIdToSlug, themeIdToSlug) {
|
|
|
6692
6731
|
}
|
|
6693
6732
|
};
|
|
6694
6733
|
}
|
|
6695
|
-
const PORTAL_SCHEMA_BASE_URL = `https://assets.fluid.app/docs/schema/portal/1.
|
|
6734
|
+
const PORTAL_SCHEMA_BASE_URL = `https://assets.fluid.app/docs/schema/portal/1.1.0`;
|
|
6696
6735
|
const PORTAL_RESOURCE_KINDS = [
|
|
6697
6736
|
"definition",
|
|
6698
6737
|
"screen",
|
|
@@ -12967,7 +13006,7 @@ var require__2020 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
12967
13006
|
const generatedSchemas = {
|
|
12968
13007
|
"data-source": {
|
|
12969
13008
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
12970
|
-
$id: "https://assets.fluid.app/docs/schema/portal/1.
|
|
13009
|
+
$id: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json",
|
|
12971
13010
|
title: "Fluid Portal Data Source",
|
|
12972
13011
|
type: "object",
|
|
12973
13012
|
properties: {
|
|
@@ -13148,7 +13187,7 @@ const generatedSchemas = {
|
|
|
13148
13187
|
},
|
|
13149
13188
|
"theme-config": {
|
|
13150
13189
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
13151
|
-
$id: "https://assets.fluid.app/docs/schema/portal/1.
|
|
13190
|
+
$id: "https://assets.fluid.app/docs/schema/portal/1.1.0/theme-config.schema.json",
|
|
13152
13191
|
title: "Fluid Portal Structured Theme Configuration",
|
|
13153
13192
|
type: "object",
|
|
13154
13193
|
properties: {
|
|
@@ -13720,7 +13759,7 @@ const generatedSchemas = {
|
|
|
13720
13759
|
},
|
|
13721
13760
|
widget: {
|
|
13722
13761
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
13723
|
-
$id: "https://assets.fluid.app/docs/schema/portal/1.
|
|
13762
|
+
$id: "https://assets.fluid.app/docs/schema/portal/1.1.0/widget.schema.json",
|
|
13724
13763
|
title: "Fluid Portal Widget Node",
|
|
13725
13764
|
$ref: "#/$defs/widgetNode",
|
|
13726
13765
|
$defs: {
|
|
@@ -13901,7 +13940,7 @@ const generatedSchemas = {
|
|
|
13901
13940
|
},
|
|
13902
13941
|
additionalProperties: false
|
|
13903
13942
|
},
|
|
13904
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
13943
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
13905
13944
|
columnIndex: {
|
|
13906
13945
|
type: "integer",
|
|
13907
13946
|
minimum: 0
|
|
@@ -14024,7 +14063,7 @@ const generatedSchemas = {
|
|
|
14024
14063
|
},
|
|
14025
14064
|
additionalProperties: false
|
|
14026
14065
|
},
|
|
14027
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
14066
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
14028
14067
|
columnIndex: {
|
|
14029
14068
|
type: "integer",
|
|
14030
14069
|
minimum: 0
|
|
@@ -14141,7 +14180,7 @@ const generatedSchemas = {
|
|
|
14141
14180
|
},
|
|
14142
14181
|
additionalProperties: false
|
|
14143
14182
|
},
|
|
14144
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
14183
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
14145
14184
|
columnIndex: {
|
|
14146
14185
|
type: "integer",
|
|
14147
14186
|
minimum: 0
|
|
@@ -14279,7 +14318,7 @@ const generatedSchemas = {
|
|
|
14279
14318
|
},
|
|
14280
14319
|
additionalProperties: false
|
|
14281
14320
|
},
|
|
14282
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
14321
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
14283
14322
|
columnIndex: {
|
|
14284
14323
|
type: "integer",
|
|
14285
14324
|
minimum: 0
|
|
@@ -14447,7 +14486,7 @@ const generatedSchemas = {
|
|
|
14447
14486
|
},
|
|
14448
14487
|
additionalProperties: false
|
|
14449
14488
|
},
|
|
14450
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
14489
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
14451
14490
|
columnIndex: {
|
|
14452
14491
|
type: "integer",
|
|
14453
14492
|
minimum: 0
|
|
@@ -14565,7 +14604,7 @@ const generatedSchemas = {
|
|
|
14565
14604
|
},
|
|
14566
14605
|
additionalProperties: false
|
|
14567
14606
|
},
|
|
14568
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
14607
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
14569
14608
|
columnIndex: {
|
|
14570
14609
|
type: "integer",
|
|
14571
14610
|
minimum: 0
|
|
@@ -14694,7 +14733,7 @@ const generatedSchemas = {
|
|
|
14694
14733
|
},
|
|
14695
14734
|
additionalProperties: false
|
|
14696
14735
|
},
|
|
14697
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
14736
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
14698
14737
|
columnIndex: {
|
|
14699
14738
|
type: "integer",
|
|
14700
14739
|
minimum: 0
|
|
@@ -14751,7 +14790,7 @@ const generatedSchemas = {
|
|
|
14751
14790
|
},
|
|
14752
14791
|
additionalProperties: false
|
|
14753
14792
|
},
|
|
14754
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
14793
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
14755
14794
|
columnIndex: {
|
|
14756
14795
|
type: "integer",
|
|
14757
14796
|
minimum: 0
|
|
@@ -14851,7 +14890,7 @@ const generatedSchemas = {
|
|
|
14851
14890
|
},
|
|
14852
14891
|
additionalProperties: false
|
|
14853
14892
|
},
|
|
14854
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
14893
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
14855
14894
|
columnIndex: {
|
|
14856
14895
|
type: "integer",
|
|
14857
14896
|
minimum: 0
|
|
@@ -14925,7 +14964,7 @@ const generatedSchemas = {
|
|
|
14925
14964
|
},
|
|
14926
14965
|
additionalProperties: false
|
|
14927
14966
|
},
|
|
14928
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
14967
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
14929
14968
|
columnIndex: {
|
|
14930
14969
|
type: "integer",
|
|
14931
14970
|
minimum: 0
|
|
@@ -15017,7 +15056,7 @@ const generatedSchemas = {
|
|
|
15017
15056
|
},
|
|
15018
15057
|
additionalProperties: false
|
|
15019
15058
|
},
|
|
15020
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
15059
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
15021
15060
|
columnIndex: {
|
|
15022
15061
|
type: "integer",
|
|
15023
15062
|
minimum: 0
|
|
@@ -15141,7 +15180,7 @@ const generatedSchemas = {
|
|
|
15141
15180
|
},
|
|
15142
15181
|
additionalProperties: false
|
|
15143
15182
|
},
|
|
15144
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
15183
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
15145
15184
|
columnIndex: {
|
|
15146
15185
|
type: "integer",
|
|
15147
15186
|
minimum: 0
|
|
@@ -15393,7 +15432,7 @@ const generatedSchemas = {
|
|
|
15393
15432
|
},
|
|
15394
15433
|
additionalProperties: false
|
|
15395
15434
|
},
|
|
15396
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
15435
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
15397
15436
|
columnIndex: {
|
|
15398
15437
|
type: "integer",
|
|
15399
15438
|
minimum: 0
|
|
@@ -15464,7 +15503,7 @@ const generatedSchemas = {
|
|
|
15464
15503
|
},
|
|
15465
15504
|
additionalProperties: false
|
|
15466
15505
|
},
|
|
15467
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
15506
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
15468
15507
|
columnIndex: {
|
|
15469
15508
|
type: "integer",
|
|
15470
15509
|
minimum: 0
|
|
@@ -15604,7 +15643,7 @@ const generatedSchemas = {
|
|
|
15604
15643
|
},
|
|
15605
15644
|
additionalProperties: false
|
|
15606
15645
|
},
|
|
15607
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
15646
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
15608
15647
|
columnIndex: {
|
|
15609
15648
|
type: "integer",
|
|
15610
15649
|
minimum: 0
|
|
@@ -15730,7 +15769,7 @@ const generatedSchemas = {
|
|
|
15730
15769
|
},
|
|
15731
15770
|
additionalProperties: false
|
|
15732
15771
|
},
|
|
15733
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
15772
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
15734
15773
|
columnIndex: {
|
|
15735
15774
|
type: "integer",
|
|
15736
15775
|
minimum: 0
|
|
@@ -16146,7 +16185,7 @@ const generatedSchemas = {
|
|
|
16146
16185
|
},
|
|
16147
16186
|
additionalProperties: false
|
|
16148
16187
|
},
|
|
16149
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
16188
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
16150
16189
|
columnIndex: {
|
|
16151
16190
|
type: "integer",
|
|
16152
16191
|
minimum: 0
|
|
@@ -16262,7 +16301,7 @@ const generatedSchemas = {
|
|
|
16262
16301
|
},
|
|
16263
16302
|
additionalProperties: false
|
|
16264
16303
|
},
|
|
16265
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
16304
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
16266
16305
|
columnIndex: {
|
|
16267
16306
|
type: "integer",
|
|
16268
16307
|
minimum: 0
|
|
@@ -16384,7 +16423,7 @@ const generatedSchemas = {
|
|
|
16384
16423
|
},
|
|
16385
16424
|
additionalProperties: false
|
|
16386
16425
|
},
|
|
16387
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
16426
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
16388
16427
|
columnIndex: {
|
|
16389
16428
|
type: "integer",
|
|
16390
16429
|
minimum: 0
|
|
@@ -16447,7 +16486,7 @@ const generatedSchemas = {
|
|
|
16447
16486
|
},
|
|
16448
16487
|
additionalProperties: false
|
|
16449
16488
|
},
|
|
16450
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
16489
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
16451
16490
|
columnIndex: {
|
|
16452
16491
|
type: "integer",
|
|
16453
16492
|
minimum: 0
|
|
@@ -16547,7 +16586,7 @@ const generatedSchemas = {
|
|
|
16547
16586
|
},
|
|
16548
16587
|
additionalProperties: false
|
|
16549
16588
|
},
|
|
16550
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
16589
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
16551
16590
|
columnIndex: {
|
|
16552
16591
|
type: "integer",
|
|
16553
16592
|
minimum: 0
|
|
@@ -16576,7 +16615,7 @@ const generatedSchemas = {
|
|
|
16576
16615
|
} },
|
|
16577
16616
|
additionalProperties: false
|
|
16578
16617
|
},
|
|
16579
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
16618
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
16580
16619
|
columnIndex: {
|
|
16581
16620
|
type: "integer",
|
|
16582
16621
|
minimum: 0
|
|
@@ -16712,7 +16751,7 @@ const generatedSchemas = {
|
|
|
16712
16751
|
},
|
|
16713
16752
|
additionalProperties: false
|
|
16714
16753
|
},
|
|
16715
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
16754
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
16716
16755
|
columnIndex: {
|
|
16717
16756
|
type: "integer",
|
|
16718
16757
|
minimum: 0
|
|
@@ -16845,7 +16884,7 @@ const generatedSchemas = {
|
|
|
16845
16884
|
},
|
|
16846
16885
|
additionalProperties: false
|
|
16847
16886
|
},
|
|
16848
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
16887
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
16849
16888
|
columnIndex: {
|
|
16850
16889
|
type: "integer",
|
|
16851
16890
|
minimum: 0
|
|
@@ -16964,7 +17003,7 @@ const generatedSchemas = {
|
|
|
16964
17003
|
},
|
|
16965
17004
|
additionalProperties: false
|
|
16966
17005
|
},
|
|
16967
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
17006
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
16968
17007
|
columnIndex: {
|
|
16969
17008
|
type: "integer",
|
|
16970
17009
|
minimum: 0
|
|
@@ -17056,7 +17095,7 @@ const generatedSchemas = {
|
|
|
17056
17095
|
},
|
|
17057
17096
|
additionalProperties: false
|
|
17058
17097
|
},
|
|
17059
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
17098
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
17060
17099
|
columnIndex: {
|
|
17061
17100
|
type: "integer",
|
|
17062
17101
|
minimum: 0
|
|
@@ -17084,10 +17123,37 @@ const generatedSchemas = {
|
|
|
17084
17123
|
type: "object",
|
|
17085
17124
|
additionalProperties: { $ref: "#/$defs/jsonValue" }
|
|
17086
17125
|
},
|
|
17087
|
-
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
17126
|
+
dataSource: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/data-source.schema.json" },
|
|
17088
17127
|
columnIndex: {
|
|
17089
17128
|
type: "integer",
|
|
17090
17129
|
minimum: 0
|
|
17130
|
+
},
|
|
17131
|
+
capabilityGrants: {
|
|
17132
|
+
type: "object",
|
|
17133
|
+
properties: { networkAccess: {
|
|
17134
|
+
type: "object",
|
|
17135
|
+
properties: {
|
|
17136
|
+
packageId: {
|
|
17137
|
+
type: "string",
|
|
17138
|
+
pattern: "\\S"
|
|
17139
|
+
},
|
|
17140
|
+
packageVersion: {
|
|
17141
|
+
type: "string",
|
|
17142
|
+
pattern: "\\S"
|
|
17143
|
+
},
|
|
17144
|
+
capabilityVersion: {
|
|
17145
|
+
type: "string",
|
|
17146
|
+
pattern: "\\S"
|
|
17147
|
+
}
|
|
17148
|
+
},
|
|
17149
|
+
required: [
|
|
17150
|
+
"packageId",
|
|
17151
|
+
"packageVersion",
|
|
17152
|
+
"capabilityVersion"
|
|
17153
|
+
],
|
|
17154
|
+
additionalProperties: false
|
|
17155
|
+
} },
|
|
17156
|
+
additionalProperties: false
|
|
17091
17157
|
}
|
|
17092
17158
|
},
|
|
17093
17159
|
required: [
|
|
@@ -17102,7 +17168,7 @@ const generatedSchemas = {
|
|
|
17102
17168
|
},
|
|
17103
17169
|
definition: {
|
|
17104
17170
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
17105
|
-
$id: "https://assets.fluid.app/docs/schema/portal/1.
|
|
17171
|
+
$id: "https://assets.fluid.app/docs/schema/portal/1.1.0/definition.schema.json",
|
|
17106
17172
|
title: "Fluid Portal Definition",
|
|
17107
17173
|
type: "object",
|
|
17108
17174
|
properties: {
|
|
@@ -17117,7 +17183,7 @@ const generatedSchemas = {
|
|
|
17117
17183
|
},
|
|
17118
17184
|
screen: {
|
|
17119
17185
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
17120
|
-
$id: "https://assets.fluid.app/docs/schema/portal/1.
|
|
17186
|
+
$id: "https://assets.fluid.app/docs/schema/portal/1.1.0/screen.schema.json",
|
|
17121
17187
|
title: "Fluid Portal Screen",
|
|
17122
17188
|
type: "object",
|
|
17123
17189
|
properties: {
|
|
@@ -17129,7 +17195,7 @@ const generatedSchemas = {
|
|
|
17129
17195
|
component_tree: {
|
|
17130
17196
|
type: "array",
|
|
17131
17197
|
maxItems: 1,
|
|
17132
|
-
items: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
17198
|
+
items: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/widget.schema.json" }
|
|
17133
17199
|
}
|
|
17134
17200
|
},
|
|
17135
17201
|
required: ["name", "component_tree"],
|
|
@@ -17137,7 +17203,7 @@ const generatedSchemas = {
|
|
|
17137
17203
|
},
|
|
17138
17204
|
theme: {
|
|
17139
17205
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
17140
|
-
$id: "https://assets.fluid.app/docs/schema/portal/1.
|
|
17206
|
+
$id: "https://assets.fluid.app/docs/schema/portal/1.1.0/theme.schema.json",
|
|
17141
17207
|
title: "Fluid Portal Theme",
|
|
17142
17208
|
type: "object",
|
|
17143
17209
|
properties: {
|
|
@@ -17146,7 +17212,7 @@ const generatedSchemas = {
|
|
|
17146
17212
|
pattern: "^https://assets\\.fluid\\.app/docs/schema/portal/(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?/theme\\.schema\\.json$"
|
|
17147
17213
|
},
|
|
17148
17214
|
name: { type: "string" },
|
|
17149
|
-
config: { $ref: "https://assets.fluid.app/docs/schema/portal/1.
|
|
17215
|
+
config: { $ref: "https://assets.fluid.app/docs/schema/portal/1.1.0/theme-config.schema.json" },
|
|
17150
17216
|
active: { type: "boolean" }
|
|
17151
17217
|
},
|
|
17152
17218
|
required: [
|
|
@@ -17158,7 +17224,7 @@ const generatedSchemas = {
|
|
|
17158
17224
|
},
|
|
17159
17225
|
navigation: {
|
|
17160
17226
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
17161
|
-
$id: "https://assets.fluid.app/docs/schema/portal/1.
|
|
17227
|
+
$id: "https://assets.fluid.app/docs/schema/portal/1.1.0/navigation.schema.json",
|
|
17162
17228
|
title: "Fluid Portal Navigation",
|
|
17163
17229
|
type: "object",
|
|
17164
17230
|
properties: {
|
|
@@ -17210,7 +17276,7 @@ const generatedSchemas = {
|
|
|
17210
17276
|
},
|
|
17211
17277
|
profile: {
|
|
17212
17278
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
17213
|
-
$id: "https://assets.fluid.app/docs/schema/portal/1.
|
|
17279
|
+
$id: "https://assets.fluid.app/docs/schema/portal/1.1.0/profile.schema.json",
|
|
17214
17280
|
title: "Fluid Portal Profile",
|
|
17215
17281
|
type: "object",
|
|
17216
17282
|
properties: {
|
|
@@ -17644,6 +17710,6 @@ const pullCommand = new Command("pull").description("Pull a Fluid OS definition'
|
|
|
17644
17710
|
console.log();
|
|
17645
17711
|
});
|
|
17646
17712
|
//#endregion
|
|
17647
|
-
export {
|
|
17713
|
+
export { toFluidOsDynamicObject as A, resolveIdToSlug as C, atomicWriteFile as D, writeMappings as E, __exportAll as F, ApiError as M, STANDALONE_PORTAL_MARKER as N, createFluidOsClient as O, assertStandalonePortalRemoteActionAllowed as P, removeMapping as S, updateMapping as T, diffAgainstSnapshot as _, buildNavigationIdToSlugMap as a, deriveSlug as b, transformNavigation as c, transformScreen as d, transformTheme as f, computeFileHash as g, collectSnapshotNetworkWidgetGrants as h, buildIdToSlugMap as i, createFluidOsHeyApiClient as j, fluidOsApi as k, transformNavigationItems as l, collectNetworkWidgetGrants as m, pull_exports as n, buildThemeIdToSlugMap as o, buildSnapshot as p, validatePortalResource as r, deriveScreenSlug as s, pullCommand as t, transformProfile as u, readSnapshot as v, resolveSlugToId as w, readMappings as x, writeSnapshot as y };
|
|
17648
17714
|
|
|
17649
|
-
//# sourceMappingURL=pull-
|
|
17715
|
+
//# sourceMappingURL=pull-C26wQlO_.mjs.map
|