@agimon-ai/po-build 0.0.0-alpha.151 → 0.0.0-alpha.153
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.
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
require("../../constants/layout.cjs");const e=require("../identity/index.cjs");let t=require("node:path");const n=[`host`,`workspace`,`session`];function r(t,n){if(!n)return`api`;let r=e.toKebab(t).split(`-`).filter(e=>e.length>0).map((e,t)=>t===0?e:e[0]?.toUpperCase()+e.slice(1)).join(``);return/^[A-Za-z_]/u.test(r)?r:`api${r}`}function i(e){let t=e.route[0];if(t!==void 0&&`literal`in t)return t.literal}function a(e){let t=/* @__PURE__ */ new Map;for(let n of e.entries){if(n.side!==`backend`||n.surface!==`api`||n.role!==`contribution`)continue;let e=i(n);if(e===void 0)continue;let r=t.get(e)??/* @__PURE__ */ new Set;r.add(n.scope),t.set(e,r)}let a=[...t.keys()].sort();return a.map(e=>{let i=t.get(e),o=n.findIndex(e=>i.has(e));return{basePath:e,scopes:n.slice(o),identifier:r(e,a.length>1)}})}function o(e){let n=t.posix.relative(e,`src/types/apiRoutes`);return n.startsWith(`.`)?n:`./${n}`}const s={imports:[`import { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';`,`import { browserTransport } from '@agimon-ai/po-web-security/browser';`],transport:`(input, init) => browserTransport.fetch(input, init)`},c={imports:[`import { createApiClient, nativeApiTransport, sessionApiAddress } from '@agimon-ai/po-core/native';`],transport:`nativeApiTransport`};function l(e,t){let n=e.scopes.map(e=>`'${e}'`).join(`, `),r=e.identifier===`api`?``:` for the ${e.basePath} mount`;return[`/** Scopes the tree mounts '${e.basePath}' at${r}. */`,`export const ${e.identifier}Scopes = [${n}] as const;`,``,`export const ${e.identifier} = createApiClient(routes, {`,` scopes: ${e.identifier}Scopes,`,` basePath: '${e.basePath}',`,` transport: ${t.transport},`,` sessionAddress: sessionApiAddress,`,`});`,``]}function u(e,t,n){let r=a(e);if(r.length!==0)return`${[`// Generated by @agimon-ai/po-build. Do not edit by hand.`,...n.imports,``,`import routes from '${o(t.entryDir??`generated`)}';`,``,...r.flatMap(e=>l(e,n))].join(`
|
|
1
|
+
require("../../constants/layout.cjs");const e=require("../identity/index.cjs");let t=require("node:path");const n=[`host`,`workspace`,`session`];function r(t,n){if(!n)return`api`;let r=e.toKebab(t).split(`-`).filter(e=>e.length>0).map((e,t)=>t===0?e:e[0]?.toUpperCase()+e.slice(1)).join(``);return/^[A-Za-z_]/u.test(r)?r:`api${r}`}function i(e){let t=e.route[0];if(t!==void 0&&`literal`in t)return t.literal}function a(e){let t=/* @__PURE__ */ new Map;for(let n of e.entries){if(n.side!==`backend`||n.surface!==`api`||n.role!==`contribution`)continue;let e=i(n);if(e===void 0)continue;let r=t.get(e)??/* @__PURE__ */ new Set;r.add(n.scope),t.set(e,r)}let a=[...t.keys()].sort();return a.map(e=>{let i=t.get(e),o=n.findIndex(e=>i.has(e));return{basePath:e,scopes:n.slice(o),identifier:r(e,a.length>1)}})}function o(e){let n=t.posix.relative(e,`src/types/apiRoutes`);return n.startsWith(`.`)?n:`./${n}`}const s={imports:[`import { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';`,`import { browserTransport } from '@agimon-ai/po-web-security/browser';`],transport:`(input, init) => browserTransport.fetch(input, init)`},c={imports:[`import { createApiClient, nativeApiTransport, sessionApiAddress } from '@agimon-ai/po-core/native';`],transport:`nativeApiTransport`};function l(e,t){let n=e.scopes.map(e=>`'${e}'`).join(`, `),r=e.identifier===`api`?``:` for the ${e.basePath} mount`;return[`/** Scopes the tree mounts '${e.basePath}' at${r}. */`,`export const ${e.identifier}Scopes = [${n}] as const;`,``,`export const ${e.identifier} = createApiClient(routes, {`,` scopes: ${e.identifier}Scopes,`,` basePath: '${e.basePath}',`,` transport: ${t.transport},`,` sessionAddress: sessionApiAddress,`,`});`,``]}function u(e,t,n){let r=a(e);if(r.length!==0)return`${[`// Generated by @agimon-ai/po-build. Do not edit by hand.`,...n.imports,``,`import { routes } from '${o(t.entryDir??`generated`)}';`,``,...r.flatMap(e=>l(e,n))].join(`
|
|
2
2
|
`).replace(/\n+$/u,``)}\n`}function d(e,t){return u(e,t,s)}function f(e,t){return u(e,t,c)}exports.renderApiClient=d,exports.renderNativeApiClient=f;
|
|
3
3
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["toKebab","posix","require_index","node_path","SCOPE_ORDER","identifierFor","multiple","camel","basePath","word","index","baseSegmentOf","first","entry","mountsOf","scopesByBase","graph","held","bases","declared","shallowest","scope","routesSpecifier","relative","entryDir","BROWSER_CLIENT","NATIVE_CLIENT","renderMount","scopes","mount","suffix","flavor","renderClient","mounts","options","renderApiClient","renderNativeApiClient"],"sources":["../../../src/services/renderClient/index.ts"],"sourcesContent":["import { posix } from 'node:path';\n\nimport { GENERATED_DIR } from '../../constants/layout';\nimport type { ExtensionEntry, ExtensionGraph, ExtensionScope } from '../../types/extensionGraph';\nimport { toKebab } from '../identity';\nimport type { RenderOptions } from '../renderEntry/type';\n\n/**\n * Renders the typed client a cockpit plugin calls its own API through.\n *\n * The two facts this injects are the two a package kept restating and getting\n * wrong: which scopes its API mounts at, and under which base path. Both are\n * read off the folder tree, so `api/file-edits/route.server.ts` at the session\n * scope is the whole declaration. A package that writes them out again is a\n * package that can disagree with itself, and several did.\n *\n * The route list is not read here. It is authored as plain data in\n * `src/types/apiRoutes.ts`, because this build evaluates no TypeScript and must\n * not learn how: it has no dependencies at all, and every specifier below is\n * emitted as text.\n */\n\nconst HEADER = '// Generated by @agimon-ai/po-build. Do not edit by hand.';\n\n/** Server scopes cascade outward-in, the way the generated server entry mounts them. */\nconst SCOPE_ORDER: readonly ExtensionScope[] = ['host', 'workspace', 'session'];\n\n/** One mount the tree declares: a base path, and the scopes it is reachable at. */\ninterface ClientMount {\n readonly basePath: string;\n readonly scopes: readonly ExtensionScope[];\n /** A valid identifier for the exported factory, when a package has several mounts. */\n readonly identifier: string;\n}\n\nfunction identifierFor(basePath: string, multiple: boolean): string {\n if (!multiple) return 'api';\n const camel = toKebab(basePath)\n .split('-')\n .filter((word) => word.length > 0)\n .map((word, index) => (index === 0 ? word : word[0]?.toUpperCase() + word.slice(1)))\n .join('');\n return /^[A-Za-z_]/u.test(camel) ? camel : `api${camel}`;\n}\n\n/**\n * The base path an api entry mounts at: the first literal segment below `api/`.\n *\n * An entry with no segment is the flat `api/route.server.ts` shape that predates\n * this convention. Its base path lives in a hand-written `PoApi` the build\n * cannot read, so it contributes no client and keeps its hand-built URLs until\n * the folder is renamed.\n */\nfunction baseSegmentOf(entry: ExtensionEntry): string | undefined {\n const first = entry.route[0];\n if (first === undefined || !('literal' in first)) return undefined;\n return first.literal;\n}\n\nfunction mountsOf(graph: ExtensionGraph): ClientMount[] {\n const scopesByBase = new Map<string, Set<ExtensionScope>>();\n for (const entry of graph.entries) {\n if (entry.side !== 'backend' || entry.surface !== 'api' || entry.role !== 'contribution') continue;\n const basePath = baseSegmentOf(entry);\n if (basePath === undefined) continue;\n const held = scopesByBase.get(basePath) ?? new Set<ExtensionScope>();\n held.add(entry.scope);\n scopesByBase.set(basePath, held);\n }\n\n const bases = [...scopesByBase.keys()].sort();\n return bases.map((basePath) => {\n const declared = scopesByBase.get(basePath) as Set<ExtensionScope>;\n // A server plugin offers a contribution at its own scope and every scope below it,\n // so a host API answers workspace and session requests too.\n const shallowest = SCOPE_ORDER.findIndex((scope) => declared.has(scope));\n return {\n basePath,\n scopes: SCOPE_ORDER.slice(shallowest),\n identifier: identifierFor(basePath, bases.length > 1),\n };\n });\n}\n\n/** The extensionless relative specifier from the generated directory to the route table. */\nfunction routesSpecifier(entryDir: string): string {\n const relative = posix.relative(entryDir, 'src/types/apiRoutes');\n return relative.startsWith('.') ? relative : `./${relative}`;\n}\n\n/** What differs between the browser and native clients: where the runtime comes from, and how a call leaves. */\ninterface ClientFlavor {\n readonly imports: readonly string[];\n readonly transport: string;\n}\n\nconst BROWSER_CLIENT: ClientFlavor = {\n imports: [\n \"import { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';\",\n \"import { browserTransport } from '@agimon-ai/po-web-security/browser';\",\n ],\n transport: '(input, init) => browserTransport.fetch(input, init)',\n};\n\n/**\n * Only `@agimon-ai/po-core/native`, the one Po module on the native host list.\n * The phone's sealed channel and step-up live in the app shell, which binds\n * them behind `nativeApiTransport`.\n */\nconst NATIVE_CLIENT: ClientFlavor = {\n imports: [\"import { createApiClient, nativeApiTransport, sessionApiAddress } from '@agimon-ai/po-core/native';\"],\n transport: 'nativeApiTransport',\n};\n\nfunction renderMount(mount: ClientMount, flavor: ClientFlavor): string[] {\n const scopes = mount.scopes.map((scope) => `'${scope}'`).join(', ');\n const suffix = mount.identifier === 'api' ? '' : ` for the ${mount.basePath} mount`;\n return [\n `/** Scopes the tree mounts '${mount.basePath}' at${suffix}. */`,\n `export const ${mount.identifier}Scopes = [${scopes}] as const;`,\n '',\n `export const ${mount.identifier} = createApiClient(routes, {`,\n ` scopes: ${mount.identifier}Scopes,`,\n ` basePath: '${mount.basePath}',`,\n ` transport: ${flavor.transport},`,\n ' sessionAddress: sessionApiAddress,',\n '});',\n '',\n ];\n}\n\nfunction renderClient(graph: ExtensionGraph, options: RenderOptions, flavor: ClientFlavor): string | undefined {\n const mounts = mountsOf(graph);\n if (mounts.length === 0) return undefined;\n\n const lines = [\n HEADER,\n ...flavor.imports,\n '',\n `import routes from '${routesSpecifier(options.entryDir ?? GENERATED_DIR)}';`,\n '',\n ...mounts.flatMap((mount) => renderMount(mount, flavor)),\n ];\n return `${lines.join('\\n').replace(/\\n+$/u, '')}\\n`;\n}\n\n/**\n * The client source, or undefined when the tree declares no `api/<base-path>/`.\n *\n * `options.entryDir` is where the file will be written, so the relative import\n * of the route table is computed against it rather than assumed.\n */\nexport function renderApiClient(graph: ExtensionGraph, options: RenderOptions): string | undefined {\n return renderClient(graph, options, BROWSER_CLIENT);\n}\n\n/**\n * The same mounts and the same route table for a `.native` file.\n *\n * The route table is imported, never restated, so it must itself import only\n * what the native host provides: a table that imports `@agimon-ai/po-core/web`\n * is refused by the native composition build, which is the intended failure.\n */\nexport function renderNativeApiClient(graph: ExtensionGraph, options: RenderOptions): string | undefined {\n return renderClient(graph, options, NATIVE_CLIENT);\n}\n"],"mappings":"A,0GAsBA,MAGMI,EAAyC,CAAC,OAAQ,YAAa,SAAS,EAU9E,SAASC,EAAc,EAAkB,EAA2B,CAClE,GAAI,CAACC,EAAU,MAAO,MACtB,IAAMC,EAAQP,EAAAA,QAAQQ,CAAQ,CAAC,CAC5B,MAAM,GAAG,CAAC,CACV,OAAQ,GAASC,EAAK,OAAS,CAAC,CAAC,CACjC,KAAK,EAAM,IAAWC,IAAU,EAAID,EAAOA,EAAK,EAAE,EAAE,YAAY,EAAIA,EAAK,MAAM,CAAC,CAAE,CAAC,CACnF,KAAK,EAAE,EACV,MAAO,cAAc,KAAKF,CAAK,EAAIA,EAAQ,MAAMA,GACnD,CAUA,SAASI,EAAc,EAA2C,CAChE,IAAMC,EAAQC,EAAM,MAAM,GAC1B,GAAID,IAAU,IAAA,IAAe,YAAaA,EAC1C,OAAOA,EAAM,OACf,CAEA,SAASE,EAAS,EAAsC,CACtD,IAAMC,kBAAe,IAAI,IACzB,IAAK,IAAMF,KAASG,EAAM,QAAS,CACjC,GAAIH,EAAM,OAAS,WAAaA,EAAM,UAAY,OAASA,EAAM,OAAS,eAAgB,SAC1F,IAAML,EAAWG,EAAcE,CAAK,EACpC,GAAIL,IAAa,IAAA,GAAW,SAC5B,IAAMS,EAAOF,EAAa,IAAIP,CAAQ,mBAAK,IAAI,IAC/CS,EAAK,IAAIJ,EAAM,KAAK,EACpBE,EAAa,IAAIP,EAAUS,CAAI,CACjC,CAEA,IAAMC,EAAQ,CAAC,GAAGH,EAAa,KAAK,CAAC,CAAC,CAAC,KAAK,EAC5C,OAAOG,EAAM,IAAK,GAAa,CAC7B,IAAMC,EAAWJ,EAAa,IAAIP,CAAQ,EAGpCY,EAAahB,EAAY,UAAW,GAAUe,EAAS,IAAIE,CAAK,CAAC,EACvE,MAAO,CACL,WACA,OAAQjB,EAAY,MAAMgB,CAAU,EACpC,WAAYf,EAAcG,EAAUU,EAAM,OAAS,CAAC,CACtD,CACF,CAAC,CACH,CAGA,SAASI,EAAgB,EAA0B,CACjD,IAAMC,EAAWtB,EAAAA,MAAM,SAASuB,EAAU,qBAAqB,EAC/D,OAAOD,EAAS,WAAW,GAAG,EAAIA,EAAW,KAAKA,GACpD,CAQA,MAAME,EAA+B,CACnC,QAAS,CACP,+EACA,wEACF,EACA,UAAW,sDACb,EAOMC,EAA8B,CAClC,QAAS,CAAC,qGAAqG,EAC/G,UAAW,oBACb,EAEA,SAASC,EAAY,EAAoB,EAAgC,CACvE,IAAMC,EAASC,EAAM,OAAO,IAAK,GAAU,IAAIR,EAAM,EAAE,CAAC,CAAC,KAAK,IAAI,EAC5DS,EAASD,EAAM,aAAe,MAAQ,GAAK,YAAYA,EAAM,SAAS,QAC5E,MAAO,CACL,+BAA+BA,EAAM,SAAS,MAAMC,EAAO,MAC3D,gBAAgBD,EAAM,WAAW,YAAYD,EAAO,aACpD,GACA,gBAAgBC,EAAM,WAAW,8BACjC,aAAaA,EAAM,WAAW,SAC9B,gBAAgBA,EAAM,SAAS,IAC/B,gBAAgBE,EAAO,UAAU,GACjC,uCACA,MACA,EACF,CACF,CAEA,SAASC,EAAa,EAAuB,EAAwB,EAA0C,CAC7G,IAAMC,EAASnB,EAASE,CAAK,EAC7B,GAAIiB,EAAO,SAAW,EAUtB,MAAO,GAAG,CAPR,4DACA,GAAGF,EAAO,QACV,GACA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["toKebab","posix","require_index","node_path","SCOPE_ORDER","identifierFor","multiple","camel","basePath","word","index","baseSegmentOf","first","entry","mountsOf","scopesByBase","graph","held","bases","declared","shallowest","scope","routesSpecifier","relative","entryDir","BROWSER_CLIENT","NATIVE_CLIENT","renderMount","scopes","mount","suffix","flavor","renderClient","mounts","options","renderApiClient","renderNativeApiClient"],"sources":["../../../src/services/renderClient/index.ts"],"sourcesContent":["import { posix } from 'node:path';\n\nimport { GENERATED_DIR } from '../../constants/layout';\nimport type { ExtensionEntry, ExtensionGraph, ExtensionScope } from '../../types/extensionGraph';\nimport { toKebab } from '../identity';\nimport type { RenderOptions } from '../renderEntry/type';\n\n/**\n * Renders the typed client a cockpit plugin calls its own API through.\n *\n * The two facts this injects are the two a package kept restating and getting\n * wrong: which scopes its API mounts at, and under which base path. Both are\n * read off the folder tree, so `api/file-edits/route.server.ts` at the session\n * scope is the whole declaration. A package that writes them out again is a\n * package that can disagree with itself, and several did.\n *\n * The route list is not read here. It is authored as plain data in\n * `src/types/apiRoutes.ts`, because this build evaluates no TypeScript and must\n * not learn how: it has no dependencies at all, and every specifier below is\n * emitted as text.\n */\n\nconst HEADER = '// Generated by @agimon-ai/po-build. Do not edit by hand.';\n\n/** Server scopes cascade outward-in, the way the generated server entry mounts them. */\nconst SCOPE_ORDER: readonly ExtensionScope[] = ['host', 'workspace', 'session'];\n\n/** One mount the tree declares: a base path, and the scopes it is reachable at. */\ninterface ClientMount {\n readonly basePath: string;\n readonly scopes: readonly ExtensionScope[];\n /** A valid identifier for the exported factory, when a package has several mounts. */\n readonly identifier: string;\n}\n\nfunction identifierFor(basePath: string, multiple: boolean): string {\n if (!multiple) return 'api';\n const camel = toKebab(basePath)\n .split('-')\n .filter((word) => word.length > 0)\n .map((word, index) => (index === 0 ? word : word[0]?.toUpperCase() + word.slice(1)))\n .join('');\n return /^[A-Za-z_]/u.test(camel) ? camel : `api${camel}`;\n}\n\n/**\n * The base path an api entry mounts at: the first literal segment below `api/`.\n *\n * An entry with no segment is the flat `api/route.server.ts` shape that predates\n * this convention. Its base path lives in a hand-written `PoApi` the build\n * cannot read, so it contributes no client and keeps its hand-built URLs until\n * the folder is renamed.\n */\nfunction baseSegmentOf(entry: ExtensionEntry): string | undefined {\n const first = entry.route[0];\n if (first === undefined || !('literal' in first)) return undefined;\n return first.literal;\n}\n\nfunction mountsOf(graph: ExtensionGraph): ClientMount[] {\n const scopesByBase = new Map<string, Set<ExtensionScope>>();\n for (const entry of graph.entries) {\n if (entry.side !== 'backend' || entry.surface !== 'api' || entry.role !== 'contribution') continue;\n const basePath = baseSegmentOf(entry);\n if (basePath === undefined) continue;\n const held = scopesByBase.get(basePath) ?? new Set<ExtensionScope>();\n held.add(entry.scope);\n scopesByBase.set(basePath, held);\n }\n\n const bases = [...scopesByBase.keys()].sort();\n return bases.map((basePath) => {\n const declared = scopesByBase.get(basePath) as Set<ExtensionScope>;\n // A server plugin offers a contribution at its own scope and every scope below it,\n // so a host API answers workspace and session requests too.\n const shallowest = SCOPE_ORDER.findIndex((scope) => declared.has(scope));\n return {\n basePath,\n scopes: SCOPE_ORDER.slice(shallowest),\n identifier: identifierFor(basePath, bases.length > 1),\n };\n });\n}\n\n/** The extensionless relative specifier from the generated directory to the route table. */\nfunction routesSpecifier(entryDir: string): string {\n const relative = posix.relative(entryDir, 'src/types/apiRoutes');\n return relative.startsWith('.') ? relative : `./${relative}`;\n}\n\n/** What differs between the browser and native clients: where the runtime comes from, and how a call leaves. */\ninterface ClientFlavor {\n readonly imports: readonly string[];\n readonly transport: string;\n}\n\nconst BROWSER_CLIENT: ClientFlavor = {\n imports: [\n \"import { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';\",\n \"import { browserTransport } from '@agimon-ai/po-web-security/browser';\",\n ],\n transport: '(input, init) => browserTransport.fetch(input, init)',\n};\n\n/**\n * Only `@agimon-ai/po-core/native`, the one Po module on the native host list.\n * The phone's sealed channel and step-up live in the app shell, which binds\n * them behind `nativeApiTransport`.\n */\nconst NATIVE_CLIENT: ClientFlavor = {\n imports: [\"import { createApiClient, nativeApiTransport, sessionApiAddress } from '@agimon-ai/po-core/native';\"],\n transport: 'nativeApiTransport',\n};\n\nfunction renderMount(mount: ClientMount, flavor: ClientFlavor): string[] {\n const scopes = mount.scopes.map((scope) => `'${scope}'`).join(', ');\n const suffix = mount.identifier === 'api' ? '' : ` for the ${mount.basePath} mount`;\n return [\n `/** Scopes the tree mounts '${mount.basePath}' at${suffix}. */`,\n `export const ${mount.identifier}Scopes = [${scopes}] as const;`,\n '',\n `export const ${mount.identifier} = createApiClient(routes, {`,\n ` scopes: ${mount.identifier}Scopes,`,\n ` basePath: '${mount.basePath}',`,\n ` transport: ${flavor.transport},`,\n ' sessionAddress: sessionApiAddress,',\n '});',\n '',\n ];\n}\n\nfunction renderClient(graph: ExtensionGraph, options: RenderOptions, flavor: ClientFlavor): string | undefined {\n const mounts = mountsOf(graph);\n if (mounts.length === 0) return undefined;\n\n const lines = [\n HEADER,\n ...flavor.imports,\n '',\n `import { routes } from '${routesSpecifier(options.entryDir ?? GENERATED_DIR)}';`,\n '',\n ...mounts.flatMap((mount) => renderMount(mount, flavor)),\n ];\n return `${lines.join('\\n').replace(/\\n+$/u, '')}\\n`;\n}\n\n/**\n * The client source, or undefined when the tree declares no `api/<base-path>/`.\n *\n * `options.entryDir` is where the file will be written, so the relative import\n * of the route table is computed against it rather than assumed.\n */\nexport function renderApiClient(graph: ExtensionGraph, options: RenderOptions): string | undefined {\n return renderClient(graph, options, BROWSER_CLIENT);\n}\n\n/**\n * The same mounts and the same route table for a `.native` file.\n *\n * The route table is imported, never restated, so it must itself import only\n * what the native host provides: a table that imports `@agimon-ai/po-core/web`\n * is refused by the native composition build, which is the intended failure.\n */\nexport function renderNativeApiClient(graph: ExtensionGraph, options: RenderOptions): string | undefined {\n return renderClient(graph, options, NATIVE_CLIENT);\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import"../../constants/layout.mjs";import{toKebab as e}from"../identity/index.mjs";import{posix as t}from"node:path";const n=[`host`,`workspace`,`session`];function r(t,n){if(!n)return`api`;let r=e(t).split(`-`).filter(e=>e.length>0).map((e,t)=>t===0?e:e[0]?.toUpperCase()+e.slice(1)).join(``);return/^[A-Za-z_]/u.test(r)?r:`api${r}`}function i(e){let t=e.route[0];if(t!==void 0&&`literal`in t)return t.literal}function a(e){let t=/* @__PURE__ */ new Map;for(let n of e.entries){if(n.side!==`backend`||n.surface!==`api`||n.role!==`contribution`)continue;let e=i(n);if(e===void 0)continue;let r=t.get(e)??/* @__PURE__ */ new Set;r.add(n.scope),t.set(e,r)}let a=[...t.keys()].sort();return a.map(e=>{let i=t.get(e),o=n.findIndex(e=>i.has(e));return{basePath:e,scopes:n.slice(o),identifier:r(e,a.length>1)}})}function o(e){let n=t.relative(e,`src/types/apiRoutes`);return n.startsWith(`.`)?n:`./${n}`}const s={imports:[`import { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';`,`import { browserTransport } from '@agimon-ai/po-web-security/browser';`],transport:`(input, init) => browserTransport.fetch(input, init)`},c={imports:[`import { createApiClient, nativeApiTransport, sessionApiAddress } from '@agimon-ai/po-core/native';`],transport:`nativeApiTransport`};function l(e,t){let n=e.scopes.map(e=>`'${e}'`).join(`, `),r=e.identifier===`api`?``:` for the ${e.basePath} mount`;return[`/** Scopes the tree mounts '${e.basePath}' at${r}. */`,`export const ${e.identifier}Scopes = [${n}] as const;`,``,`export const ${e.identifier} = createApiClient(routes, {`,` scopes: ${e.identifier}Scopes,`,` basePath: '${e.basePath}',`,` transport: ${t.transport},`,` sessionAddress: sessionApiAddress,`,`});`,``]}function u(e,t,n){let r=a(e);if(r.length!==0)return`${[`// Generated by @agimon-ai/po-build. Do not edit by hand.`,...n.imports,``,`import routes from '${o(t.entryDir??`generated`)}';`,``,...r.flatMap(e=>l(e,n))].join(`
|
|
1
|
+
import"../../constants/layout.mjs";import{toKebab as e}from"../identity/index.mjs";import{posix as t}from"node:path";const n=[`host`,`workspace`,`session`];function r(t,n){if(!n)return`api`;let r=e(t).split(`-`).filter(e=>e.length>0).map((e,t)=>t===0?e:e[0]?.toUpperCase()+e.slice(1)).join(``);return/^[A-Za-z_]/u.test(r)?r:`api${r}`}function i(e){let t=e.route[0];if(t!==void 0&&`literal`in t)return t.literal}function a(e){let t=/* @__PURE__ */ new Map;for(let n of e.entries){if(n.side!==`backend`||n.surface!==`api`||n.role!==`contribution`)continue;let e=i(n);if(e===void 0)continue;let r=t.get(e)??/* @__PURE__ */ new Set;r.add(n.scope),t.set(e,r)}let a=[...t.keys()].sort();return a.map(e=>{let i=t.get(e),o=n.findIndex(e=>i.has(e));return{basePath:e,scopes:n.slice(o),identifier:r(e,a.length>1)}})}function o(e){let n=t.relative(e,`src/types/apiRoutes`);return n.startsWith(`.`)?n:`./${n}`}const s={imports:[`import { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';`,`import { browserTransport } from '@agimon-ai/po-web-security/browser';`],transport:`(input, init) => browserTransport.fetch(input, init)`},c={imports:[`import { createApiClient, nativeApiTransport, sessionApiAddress } from '@agimon-ai/po-core/native';`],transport:`nativeApiTransport`};function l(e,t){let n=e.scopes.map(e=>`'${e}'`).join(`, `),r=e.identifier===`api`?``:` for the ${e.basePath} mount`;return[`/** Scopes the tree mounts '${e.basePath}' at${r}. */`,`export const ${e.identifier}Scopes = [${n}] as const;`,``,`export const ${e.identifier} = createApiClient(routes, {`,` scopes: ${e.identifier}Scopes,`,` basePath: '${e.basePath}',`,` transport: ${t.transport},`,` sessionAddress: sessionApiAddress,`,`});`,``]}function u(e,t,n){let r=a(e);if(r.length!==0)return`${[`// Generated by @agimon-ai/po-build. Do not edit by hand.`,...n.imports,``,`import { routes } from '${o(t.entryDir??`generated`)}';`,``,...r.flatMap(e=>l(e,n))].join(`
|
|
2
2
|
`).replace(/\n+$/u,``)}\n`}function d(e,t){return u(e,t,s)}function f(e,t){return u(e,t,c)}export{d as renderApiClient,f as renderNativeApiClient};
|
|
3
3
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["SCOPE_ORDER","identifierFor","multiple","camel","toKebab","basePath","word","index","baseSegmentOf","first","entry","mountsOf","scopesByBase","graph","held","bases","declared","shallowest","scope","routesSpecifier","relative","posix","entryDir","BROWSER_CLIENT","NATIVE_CLIENT","renderMount","scopes","mount","suffix","flavor","renderClient","mounts","options","renderApiClient","renderNativeApiClient"],"sources":["../../../src/services/renderClient/index.ts"],"sourcesContent":["import { posix } from 'node:path';\n\nimport { GENERATED_DIR } from '../../constants/layout';\nimport type { ExtensionEntry, ExtensionGraph, ExtensionScope } from '../../types/extensionGraph';\nimport { toKebab } from '../identity';\nimport type { RenderOptions } from '../renderEntry/type';\n\n/**\n * Renders the typed client a cockpit plugin calls its own API through.\n *\n * The two facts this injects are the two a package kept restating and getting\n * wrong: which scopes its API mounts at, and under which base path. Both are\n * read off the folder tree, so `api/file-edits/route.server.ts` at the session\n * scope is the whole declaration. A package that writes them out again is a\n * package that can disagree with itself, and several did.\n *\n * The route list is not read here. It is authored as plain data in\n * `src/types/apiRoutes.ts`, because this build evaluates no TypeScript and must\n * not learn how: it has no dependencies at all, and every specifier below is\n * emitted as text.\n */\n\nconst HEADER = '// Generated by @agimon-ai/po-build. Do not edit by hand.';\n\n/** Server scopes cascade outward-in, the way the generated server entry mounts them. */\nconst SCOPE_ORDER: readonly ExtensionScope[] = ['host', 'workspace', 'session'];\n\n/** One mount the tree declares: a base path, and the scopes it is reachable at. */\ninterface ClientMount {\n readonly basePath: string;\n readonly scopes: readonly ExtensionScope[];\n /** A valid identifier for the exported factory, when a package has several mounts. */\n readonly identifier: string;\n}\n\nfunction identifierFor(basePath: string, multiple: boolean): string {\n if (!multiple) return 'api';\n const camel = toKebab(basePath)\n .split('-')\n .filter((word) => word.length > 0)\n .map((word, index) => (index === 0 ? word : word[0]?.toUpperCase() + word.slice(1)))\n .join('');\n return /^[A-Za-z_]/u.test(camel) ? camel : `api${camel}`;\n}\n\n/**\n * The base path an api entry mounts at: the first literal segment below `api/`.\n *\n * An entry with no segment is the flat `api/route.server.ts` shape that predates\n * this convention. Its base path lives in a hand-written `PoApi` the build\n * cannot read, so it contributes no client and keeps its hand-built URLs until\n * the folder is renamed.\n */\nfunction baseSegmentOf(entry: ExtensionEntry): string | undefined {\n const first = entry.route[0];\n if (first === undefined || !('literal' in first)) return undefined;\n return first.literal;\n}\n\nfunction mountsOf(graph: ExtensionGraph): ClientMount[] {\n const scopesByBase = new Map<string, Set<ExtensionScope>>();\n for (const entry of graph.entries) {\n if (entry.side !== 'backend' || entry.surface !== 'api' || entry.role !== 'contribution') continue;\n const basePath = baseSegmentOf(entry);\n if (basePath === undefined) continue;\n const held = scopesByBase.get(basePath) ?? new Set<ExtensionScope>();\n held.add(entry.scope);\n scopesByBase.set(basePath, held);\n }\n\n const bases = [...scopesByBase.keys()].sort();\n return bases.map((basePath) => {\n const declared = scopesByBase.get(basePath) as Set<ExtensionScope>;\n // A server plugin offers a contribution at its own scope and every scope below it,\n // so a host API answers workspace and session requests too.\n const shallowest = SCOPE_ORDER.findIndex((scope) => declared.has(scope));\n return {\n basePath,\n scopes: SCOPE_ORDER.slice(shallowest),\n identifier: identifierFor(basePath, bases.length > 1),\n };\n });\n}\n\n/** The extensionless relative specifier from the generated directory to the route table. */\nfunction routesSpecifier(entryDir: string): string {\n const relative = posix.relative(entryDir, 'src/types/apiRoutes');\n return relative.startsWith('.') ? relative : `./${relative}`;\n}\n\n/** What differs between the browser and native clients: where the runtime comes from, and how a call leaves. */\ninterface ClientFlavor {\n readonly imports: readonly string[];\n readonly transport: string;\n}\n\nconst BROWSER_CLIENT: ClientFlavor = {\n imports: [\n \"import { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';\",\n \"import { browserTransport } from '@agimon-ai/po-web-security/browser';\",\n ],\n transport: '(input, init) => browserTransport.fetch(input, init)',\n};\n\n/**\n * Only `@agimon-ai/po-core/native`, the one Po module on the native host list.\n * The phone's sealed channel and step-up live in the app shell, which binds\n * them behind `nativeApiTransport`.\n */\nconst NATIVE_CLIENT: ClientFlavor = {\n imports: [\"import { createApiClient, nativeApiTransport, sessionApiAddress } from '@agimon-ai/po-core/native';\"],\n transport: 'nativeApiTransport',\n};\n\nfunction renderMount(mount: ClientMount, flavor: ClientFlavor): string[] {\n const scopes = mount.scopes.map((scope) => `'${scope}'`).join(', ');\n const suffix = mount.identifier === 'api' ? '' : ` for the ${mount.basePath} mount`;\n return [\n `/** Scopes the tree mounts '${mount.basePath}' at${suffix}. */`,\n `export const ${mount.identifier}Scopes = [${scopes}] as const;`,\n '',\n `export const ${mount.identifier} = createApiClient(routes, {`,\n ` scopes: ${mount.identifier}Scopes,`,\n ` basePath: '${mount.basePath}',`,\n ` transport: ${flavor.transport},`,\n ' sessionAddress: sessionApiAddress,',\n '});',\n '',\n ];\n}\n\nfunction renderClient(graph: ExtensionGraph, options: RenderOptions, flavor: ClientFlavor): string | undefined {\n const mounts = mountsOf(graph);\n if (mounts.length === 0) return undefined;\n\n const lines = [\n HEADER,\n ...flavor.imports,\n '',\n `import routes from '${routesSpecifier(options.entryDir ?? GENERATED_DIR)}';`,\n '',\n ...mounts.flatMap((mount) => renderMount(mount, flavor)),\n ];\n return `${lines.join('\\n').replace(/\\n+$/u, '')}\\n`;\n}\n\n/**\n * The client source, or undefined when the tree declares no `api/<base-path>/`.\n *\n * `options.entryDir` is where the file will be written, so the relative import\n * of the route table is computed against it rather than assumed.\n */\nexport function renderApiClient(graph: ExtensionGraph, options: RenderOptions): string | undefined {\n return renderClient(graph, options, BROWSER_CLIENT);\n}\n\n/**\n * The same mounts and the same route table for a `.native` file.\n *\n * The route table is imported, never restated, so it must itself import only\n * what the native host provides: a table that imports `@agimon-ai/po-core/web`\n * is refused by the native composition build, which is the intended failure.\n */\nexport function renderNativeApiClient(graph: ExtensionGraph, options: RenderOptions): string | undefined {\n return renderClient(graph, options, NATIVE_CLIENT);\n}\n"],"mappings":"A,qHAsBA,MAGMA,EAAyC,CAAC,OAAQ,YAAa,SAAS,EAU9E,SAASC,EAAc,EAAkB,EAA2B,CAClE,GAAI,CAACC,EAAU,MAAO,MACtB,IAAMC,EAAQC,EAAQC,CAAQ,CAAC,CAC5B,MAAM,GAAG,CAAC,CACV,OAAQ,GAASC,EAAK,OAAS,CAAC,CAAC,CACjC,KAAK,EAAM,IAAWC,IAAU,EAAID,EAAOA,EAAK,EAAE,EAAE,YAAY,EAAIA,EAAK,MAAM,CAAC,CAAE,CAAC,CACnF,KAAK,EAAE,EACV,MAAO,cAAc,KAAKH,CAAK,EAAIA,EAAQ,MAAMA,GACnD,CAUA,SAASK,EAAc,EAA2C,CAChE,IAAMC,EAAQC,EAAM,MAAM,GAC1B,GAAID,IAAU,IAAA,IAAe,YAAaA,EAC1C,OAAOA,EAAM,OACf,CAEA,SAASE,EAAS,EAAsC,CACtD,IAAMC,kBAAe,IAAI,IACzB,IAAK,IAAMF,KAASG,EAAM,QAAS,CACjC,GAAIH,EAAM,OAAS,WAAaA,EAAM,UAAY,OAASA,EAAM,OAAS,eAAgB,SAC1F,IAAML,EAAWG,EAAcE,CAAK,EACpC,GAAIL,IAAa,IAAA,GAAW,SAC5B,IAAMS,EAAOF,EAAa,IAAIP,CAAQ,mBAAK,IAAI,IAC/CS,EAAK,IAAIJ,EAAM,KAAK,EACpBE,EAAa,IAAIP,EAAUS,CAAI,CACjC,CAEA,IAAMC,EAAQ,CAAC,GAAGH,EAAa,KAAK,CAAC,CAAC,CAAC,KAAK,EAC5C,OAAOG,EAAM,IAAK,GAAa,CAC7B,IAAMC,EAAWJ,EAAa,IAAIP,CAAQ,EAGpCY,EAAajB,EAAY,UAAW,GAAUgB,EAAS,IAAIE,CAAK,CAAC,EACvE,MAAO,CACL,WACA,OAAQlB,EAAY,MAAMiB,CAAU,EACpC,WAAYhB,EAAcI,EAAUU,EAAM,OAAS,CAAC,CACtD,CACF,CAAC,CACH,CAGA,SAASI,EAAgB,EAA0B,CACjD,IAAMC,EAAWC,EAAM,SAASC,EAAU,qBAAqB,EAC/D,OAAOF,EAAS,WAAW,GAAG,EAAIA,EAAW,KAAKA,GACpD,CAQA,MAAMG,EAA+B,CACnC,QAAS,CACP,+EACA,wEACF,EACA,UAAW,sDACb,EAOMC,EAA8B,CAClC,QAAS,CAAC,qGAAqG,EAC/G,UAAW,oBACb,EAEA,SAASC,EAAY,EAAoB,EAAgC,CACvE,IAAMC,EAASC,EAAM,OAAO,IAAK,GAAU,IAAIT,EAAM,EAAE,CAAC,CAAC,KAAK,IAAI,EAC5DU,EAASD,EAAM,aAAe,MAAQ,GAAK,YAAYA,EAAM,SAAS,QAC5E,MAAO,CACL,+BAA+BA,EAAM,SAAS,MAAMC,EAAO,MAC3D,gBAAgBD,EAAM,WAAW,YAAYD,EAAO,aACpD,GACA,gBAAgBC,EAAM,WAAW,8BACjC,aAAaA,EAAM,WAAW,SAC9B,gBAAgBA,EAAM,SAAS,IAC/B,gBAAgBE,EAAO,UAAU,GACjC,uCACA,MACA,EACF,CACF,CAEA,SAASC,EAAa,EAAuB,EAAwB,EAA0C,CAC7G,IAAMC,EAASpB,EAASE,CAAK,EAC7B,GAAIkB,EAAO,SAAW,EAUtB,MAAO,GAAG,CAPR,4DACA,GAAGF,EAAO,QACV,GACA,
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["SCOPE_ORDER","identifierFor","multiple","camel","toKebab","basePath","word","index","baseSegmentOf","first","entry","mountsOf","scopesByBase","graph","held","bases","declared","shallowest","scope","routesSpecifier","relative","posix","entryDir","BROWSER_CLIENT","NATIVE_CLIENT","renderMount","scopes","mount","suffix","flavor","renderClient","mounts","options","renderApiClient","renderNativeApiClient"],"sources":["../../../src/services/renderClient/index.ts"],"sourcesContent":["import { posix } from 'node:path';\n\nimport { GENERATED_DIR } from '../../constants/layout';\nimport type { ExtensionEntry, ExtensionGraph, ExtensionScope } from '../../types/extensionGraph';\nimport { toKebab } from '../identity';\nimport type { RenderOptions } from '../renderEntry/type';\n\n/**\n * Renders the typed client a cockpit plugin calls its own API through.\n *\n * The two facts this injects are the two a package kept restating and getting\n * wrong: which scopes its API mounts at, and under which base path. Both are\n * read off the folder tree, so `api/file-edits/route.server.ts` at the session\n * scope is the whole declaration. A package that writes them out again is a\n * package that can disagree with itself, and several did.\n *\n * The route list is not read here. It is authored as plain data in\n * `src/types/apiRoutes.ts`, because this build evaluates no TypeScript and must\n * not learn how: it has no dependencies at all, and every specifier below is\n * emitted as text.\n */\n\nconst HEADER = '// Generated by @agimon-ai/po-build. Do not edit by hand.';\n\n/** Server scopes cascade outward-in, the way the generated server entry mounts them. */\nconst SCOPE_ORDER: readonly ExtensionScope[] = ['host', 'workspace', 'session'];\n\n/** One mount the tree declares: a base path, and the scopes it is reachable at. */\ninterface ClientMount {\n readonly basePath: string;\n readonly scopes: readonly ExtensionScope[];\n /** A valid identifier for the exported factory, when a package has several mounts. */\n readonly identifier: string;\n}\n\nfunction identifierFor(basePath: string, multiple: boolean): string {\n if (!multiple) return 'api';\n const camel = toKebab(basePath)\n .split('-')\n .filter((word) => word.length > 0)\n .map((word, index) => (index === 0 ? word : word[0]?.toUpperCase() + word.slice(1)))\n .join('');\n return /^[A-Za-z_]/u.test(camel) ? camel : `api${camel}`;\n}\n\n/**\n * The base path an api entry mounts at: the first literal segment below `api/`.\n *\n * An entry with no segment is the flat `api/route.server.ts` shape that predates\n * this convention. Its base path lives in a hand-written `PoApi` the build\n * cannot read, so it contributes no client and keeps its hand-built URLs until\n * the folder is renamed.\n */\nfunction baseSegmentOf(entry: ExtensionEntry): string | undefined {\n const first = entry.route[0];\n if (first === undefined || !('literal' in first)) return undefined;\n return first.literal;\n}\n\nfunction mountsOf(graph: ExtensionGraph): ClientMount[] {\n const scopesByBase = new Map<string, Set<ExtensionScope>>();\n for (const entry of graph.entries) {\n if (entry.side !== 'backend' || entry.surface !== 'api' || entry.role !== 'contribution') continue;\n const basePath = baseSegmentOf(entry);\n if (basePath === undefined) continue;\n const held = scopesByBase.get(basePath) ?? new Set<ExtensionScope>();\n held.add(entry.scope);\n scopesByBase.set(basePath, held);\n }\n\n const bases = [...scopesByBase.keys()].sort();\n return bases.map((basePath) => {\n const declared = scopesByBase.get(basePath) as Set<ExtensionScope>;\n // A server plugin offers a contribution at its own scope and every scope below it,\n // so a host API answers workspace and session requests too.\n const shallowest = SCOPE_ORDER.findIndex((scope) => declared.has(scope));\n return {\n basePath,\n scopes: SCOPE_ORDER.slice(shallowest),\n identifier: identifierFor(basePath, bases.length > 1),\n };\n });\n}\n\n/** The extensionless relative specifier from the generated directory to the route table. */\nfunction routesSpecifier(entryDir: string): string {\n const relative = posix.relative(entryDir, 'src/types/apiRoutes');\n return relative.startsWith('.') ? relative : `./${relative}`;\n}\n\n/** What differs between the browser and native clients: where the runtime comes from, and how a call leaves. */\ninterface ClientFlavor {\n readonly imports: readonly string[];\n readonly transport: string;\n}\n\nconst BROWSER_CLIENT: ClientFlavor = {\n imports: [\n \"import { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';\",\n \"import { browserTransport } from '@agimon-ai/po-web-security/browser';\",\n ],\n transport: '(input, init) => browserTransport.fetch(input, init)',\n};\n\n/**\n * Only `@agimon-ai/po-core/native`, the one Po module on the native host list.\n * The phone's sealed channel and step-up live in the app shell, which binds\n * them behind `nativeApiTransport`.\n */\nconst NATIVE_CLIENT: ClientFlavor = {\n imports: [\"import { createApiClient, nativeApiTransport, sessionApiAddress } from '@agimon-ai/po-core/native';\"],\n transport: 'nativeApiTransport',\n};\n\nfunction renderMount(mount: ClientMount, flavor: ClientFlavor): string[] {\n const scopes = mount.scopes.map((scope) => `'${scope}'`).join(', ');\n const suffix = mount.identifier === 'api' ? '' : ` for the ${mount.basePath} mount`;\n return [\n `/** Scopes the tree mounts '${mount.basePath}' at${suffix}. */`,\n `export const ${mount.identifier}Scopes = [${scopes}] as const;`,\n '',\n `export const ${mount.identifier} = createApiClient(routes, {`,\n ` scopes: ${mount.identifier}Scopes,`,\n ` basePath: '${mount.basePath}',`,\n ` transport: ${flavor.transport},`,\n ' sessionAddress: sessionApiAddress,',\n '});',\n '',\n ];\n}\n\nfunction renderClient(graph: ExtensionGraph, options: RenderOptions, flavor: ClientFlavor): string | undefined {\n const mounts = mountsOf(graph);\n if (mounts.length === 0) return undefined;\n\n const lines = [\n HEADER,\n ...flavor.imports,\n '',\n `import { routes } from '${routesSpecifier(options.entryDir ?? GENERATED_DIR)}';`,\n '',\n ...mounts.flatMap((mount) => renderMount(mount, flavor)),\n ];\n return `${lines.join('\\n').replace(/\\n+$/u, '')}\\n`;\n}\n\n/**\n * The client source, or undefined when the tree declares no `api/<base-path>/`.\n *\n * `options.entryDir` is where the file will be written, so the relative import\n * of the route table is computed against it rather than assumed.\n */\nexport function renderApiClient(graph: ExtensionGraph, options: RenderOptions): string | undefined {\n return renderClient(graph, options, BROWSER_CLIENT);\n}\n\n/**\n * The same mounts and the same route table for a `.native` file.\n *\n * The route table is imported, never restated, so it must itself import only\n * what the native host provides: a table that imports `@agimon-ai/po-core/web`\n * is refused by the native composition build, which is the intended failure.\n */\nexport function renderNativeApiClient(graph: ExtensionGraph, options: RenderOptions): string | undefined {\n return renderClient(graph, options, NATIVE_CLIENT);\n}\n"],"mappings":"A,qHAsBA,MAGMA,EAAyC,CAAC,OAAQ,YAAa,SAAS,EAU9E,SAASC,EAAc,EAAkB,EAA2B,CAClE,GAAI,CAACC,EAAU,MAAO,MACtB,IAAMC,EAAQC,EAAQC,CAAQ,CAAC,CAC5B,MAAM,GAAG,CAAC,CACV,OAAQ,GAASC,EAAK,OAAS,CAAC,CAAC,CACjC,KAAK,EAAM,IAAWC,IAAU,EAAID,EAAOA,EAAK,EAAE,EAAE,YAAY,EAAIA,EAAK,MAAM,CAAC,CAAE,CAAC,CACnF,KAAK,EAAE,EACV,MAAO,cAAc,KAAKH,CAAK,EAAIA,EAAQ,MAAMA,GACnD,CAUA,SAASK,EAAc,EAA2C,CAChE,IAAMC,EAAQC,EAAM,MAAM,GAC1B,GAAID,IAAU,IAAA,IAAe,YAAaA,EAC1C,OAAOA,EAAM,OACf,CAEA,SAASE,EAAS,EAAsC,CACtD,IAAMC,kBAAe,IAAI,IACzB,IAAK,IAAMF,KAASG,EAAM,QAAS,CACjC,GAAIH,EAAM,OAAS,WAAaA,EAAM,UAAY,OAASA,EAAM,OAAS,eAAgB,SAC1F,IAAML,EAAWG,EAAcE,CAAK,EACpC,GAAIL,IAAa,IAAA,GAAW,SAC5B,IAAMS,EAAOF,EAAa,IAAIP,CAAQ,mBAAK,IAAI,IAC/CS,EAAK,IAAIJ,EAAM,KAAK,EACpBE,EAAa,IAAIP,EAAUS,CAAI,CACjC,CAEA,IAAMC,EAAQ,CAAC,GAAGH,EAAa,KAAK,CAAC,CAAC,CAAC,KAAK,EAC5C,OAAOG,EAAM,IAAK,GAAa,CAC7B,IAAMC,EAAWJ,EAAa,IAAIP,CAAQ,EAGpCY,EAAajB,EAAY,UAAW,GAAUgB,EAAS,IAAIE,CAAK,CAAC,EACvE,MAAO,CACL,WACA,OAAQlB,EAAY,MAAMiB,CAAU,EACpC,WAAYhB,EAAcI,EAAUU,EAAM,OAAS,CAAC,CACtD,CACF,CAAC,CACH,CAGA,SAASI,EAAgB,EAA0B,CACjD,IAAMC,EAAWC,EAAM,SAASC,EAAU,qBAAqB,EAC/D,OAAOF,EAAS,WAAW,GAAG,EAAIA,EAAW,KAAKA,GACpD,CAQA,MAAMG,EAA+B,CACnC,QAAS,CACP,+EACA,wEACF,EACA,UAAW,sDACb,EAOMC,EAA8B,CAClC,QAAS,CAAC,qGAAqG,EAC/G,UAAW,oBACb,EAEA,SAASC,EAAY,EAAoB,EAAgC,CACvE,IAAMC,EAASC,EAAM,OAAO,IAAK,GAAU,IAAIT,EAAM,EAAE,CAAC,CAAC,KAAK,IAAI,EAC5DU,EAASD,EAAM,aAAe,MAAQ,GAAK,YAAYA,EAAM,SAAS,QAC5E,MAAO,CACL,+BAA+BA,EAAM,SAAS,MAAMC,EAAO,MAC3D,gBAAgBD,EAAM,WAAW,YAAYD,EAAO,aACpD,GACA,gBAAgBC,EAAM,WAAW,8BACjC,aAAaA,EAAM,WAAW,SAC9B,gBAAgBA,EAAM,SAAS,IAC/B,gBAAgBE,EAAO,UAAU,GACjC,uCACA,MACA,EACF,CACF,CAEA,SAASC,EAAa,EAAuB,EAAwB,EAA0C,CAC7G,IAAMC,EAASpB,EAASE,CAAK,EAC7B,GAAIkB,EAAO,SAAW,EAUtB,MAAO,GAAG,CAPR,4DACA,GAAGF,EAAO,QACV,GACA,2BAA2BV,EAAgBa,EAAQ,UAAA,WAAyB,EAAE,IAC9E,GACA,GAAGD,EAAO,QAAS,GAAUN,EAAYE,EAAOE,CAAM,CAAC,CAE3C,CAAC,CAAC,KAAK;CAAI,CAAC,CAAC,QAAQ,QAAS,EAAE,EAAE,GAClD,CAQA,SAAgBI,EAAgB,EAAuB,EAA4C,CACjG,OAAOH,EAAajB,EAAOmB,EAAST,CAAc,CACpD,CASA,SAAgBW,EAAsB,EAAuB,EAA4C,CACvG,OAAOJ,EAAajB,EAAOmB,EAASR,CAAa,CACnD,C"}
|