@camstack/addon-cloudflare-turn 0.1.15 → 0.1.16
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.
|
@@ -56,7 +56,7 @@ class CloudflareTurnAddon extends types.BaseAddon {
|
|
|
56
56
|
key: "turnHelp",
|
|
57
57
|
label: "How to get the Account ID + API Token",
|
|
58
58
|
format: "html",
|
|
59
|
-
content: '<ul><li><strong>Account ID</strong> — open <a href="https://dash.cloudflare.com/">dash.cloudflare.com</a>, pick your account; the URL shows <code>dash.cloudflare.com/<accountId>/...</code>. Copy that hex id.</li><li><strong>TURN Token</strong> — go to <a href="https://dash.cloudflare.com/?to=/:account/
|
|
59
|
+
content: '<ul><li><strong>Account ID</strong> — open <a href="https://dash.cloudflare.com/">dash.cloudflare.com</a>, pick your account; the URL shows <code>dash.cloudflare.com/<accountId>/...</code>. Copy that hex id.</li><li><strong>TURN Token</strong> — go to <a href="https://dash.cloudflare.com/?to=/:account/realtime/turn/overview" target="_blank" rel="noopener noreferrer">Realtime → TURN</a>, click <strong>Create TURN App</strong>, copy the generated <em>Token ID</em> and <em>API Token</em>.</li><li>Paste the <em>API Token</em> below (the Token ID is the Account ID variant for TURN apps — Cloudflare uses both naming conventions across their dashboard).</li><li>Permissions auto-set when you create a Realtime app — no manual scope picker needed.</li></ul>',
|
|
60
60
|
variant: "info"
|
|
61
61
|
},
|
|
62
62
|
this.field({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare-turn.addon.js","sources":["../src/cloudflare-turn.ts","../src/cloudflare-turn.addon.ts"],"sourcesContent":["import type { IScopedLogger } from '@camstack/types'\n\nexport interface CloudflareTurnConfig {\n readonly apiToken: string\n readonly accountId: string\n}\n\ntype TurnServer = { urls: string | string[]; username?: string; credential?: string }\n\n/**\n * Cloudflare TURN/STUN provider.\n *\n * Implements the `turn-provider` capability (system collection). Called\n * by the `webrtc` capability implementations when building ICE server\n * lists for a new peer connection. Each call SHOULD fetch fresh\n * short-lived credentials — currently stubbed with static values until\n * the Cloudflare Calls API integration is wired.\n */\nexport class CloudflareTurnService {\n readonly id = 'cloudflare-turn'\n readonly name = 'Cloudflare TURN'\n\n constructor(\n _config: CloudflareTurnConfig,\n private readonly logger: IScopedLogger,\n ) {}\n\n /**\n * Return the current TURN/STUN server list with credentials.\n * Implements `turn-provider` capability.\n */\n getTurnServers(): readonly TurnServer[] {\n this.logger.debug('Fetching TURN servers from Cloudflare')\n // TODO: implement real Cloudflare TURN integration.\n // Real integration requires a Cloudflare Calls API call using this.apiToken/accountId.\n return [\n {\n urls: [\n 'turn:turn.cloudflare.com:3478?transport=udp',\n 'turn:turn.cloudflare.com:3478?transport=tcp',\n ],\n username: 'temp-user',\n credential: 'temp-credential',\n },\n ]\n }\n}\n","import type { ProviderRegistration } from '@camstack/types'\nimport { BaseAddon, turnProviderCapability } from '@camstack/types'\nimport { CloudflareTurnService } from './cloudflare-turn'\nimport type { CloudflareTurnConfig } from './cloudflare-turn'\n\n/**\n * Settings redesign Phase 3: cloudflare-turn is node-level credentials\n * storage. Implements `getGlobalSettings`.\n */\nexport class CloudflareTurnAddon extends BaseAddon<CloudflareTurnConfig> {\n private service: CloudflareTurnService | null = null\n\n constructor() {\n super({ apiToken: '', accountId: '' })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.service = new CloudflareTurnService(this.config, this.ctx.logger)\n this.ctx.logger.info('Cloudflare TURN initialized')\n return [{ capability: turnProviderCapability, provider: this.service }]\n }\n\n protected async onShutdown(): Promise<void> {\n this.service = null\n }\n\n getService(): CloudflareTurnService {\n if (!this.service) throw new Error('Cloudflare TURN not initialized')\n return this.service\n }\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [\n {\n id: 'credentials',\n title: 'Cloudflare Credentials',\n description: 'API credentials for fetching TURN relay tokens from Cloudflare Realtime.',\n immediate: true,\n fields: [\n {\n type: 'info' as const,\n key: 'turnHelp',\n label: 'How to get the Account ID + API Token',\n format: 'html' as const,\n content:\n '<ul>' +\n '<li><strong>Account ID</strong> — open ' +\n '<a href=\"https://dash.cloudflare.com/\">dash.cloudflare.com</a>, ' +\n 'pick your account; the URL shows ' +\n '<code>dash.cloudflare.com/<accountId>/...</code>. ' +\n 'Copy that hex id.</li>' +\n '<li><strong>TURN Token</strong> — go to ' +\n '<a href=\"https://dash.cloudflare.com/?to=/:account/
|
|
1
|
+
{"version":3,"file":"cloudflare-turn.addon.js","sources":["../src/cloudflare-turn.ts","../src/cloudflare-turn.addon.ts"],"sourcesContent":["import type { IScopedLogger } from '@camstack/types'\n\nexport interface CloudflareTurnConfig {\n readonly apiToken: string\n readonly accountId: string\n}\n\ntype TurnServer = { urls: string | string[]; username?: string; credential?: string }\n\n/**\n * Cloudflare TURN/STUN provider.\n *\n * Implements the `turn-provider` capability (system collection). Called\n * by the `webrtc` capability implementations when building ICE server\n * lists for a new peer connection. Each call SHOULD fetch fresh\n * short-lived credentials — currently stubbed with static values until\n * the Cloudflare Calls API integration is wired.\n */\nexport class CloudflareTurnService {\n readonly id = 'cloudflare-turn'\n readonly name = 'Cloudflare TURN'\n\n constructor(\n _config: CloudflareTurnConfig,\n private readonly logger: IScopedLogger,\n ) {}\n\n /**\n * Return the current TURN/STUN server list with credentials.\n * Implements `turn-provider` capability.\n */\n getTurnServers(): readonly TurnServer[] {\n this.logger.debug('Fetching TURN servers from Cloudflare')\n // TODO: implement real Cloudflare TURN integration.\n // Real integration requires a Cloudflare Calls API call using this.apiToken/accountId.\n return [\n {\n urls: [\n 'turn:turn.cloudflare.com:3478?transport=udp',\n 'turn:turn.cloudflare.com:3478?transport=tcp',\n ],\n username: 'temp-user',\n credential: 'temp-credential',\n },\n ]\n }\n}\n","import type { ProviderRegistration } from '@camstack/types'\nimport { BaseAddon, turnProviderCapability } from '@camstack/types'\nimport { CloudflareTurnService } from './cloudflare-turn'\nimport type { CloudflareTurnConfig } from './cloudflare-turn'\n\n/**\n * Settings redesign Phase 3: cloudflare-turn is node-level credentials\n * storage. Implements `getGlobalSettings`.\n */\nexport class CloudflareTurnAddon extends BaseAddon<CloudflareTurnConfig> {\n private service: CloudflareTurnService | null = null\n\n constructor() {\n super({ apiToken: '', accountId: '' })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.service = new CloudflareTurnService(this.config, this.ctx.logger)\n this.ctx.logger.info('Cloudflare TURN initialized')\n return [{ capability: turnProviderCapability, provider: this.service }]\n }\n\n protected async onShutdown(): Promise<void> {\n this.service = null\n }\n\n getService(): CloudflareTurnService {\n if (!this.service) throw new Error('Cloudflare TURN not initialized')\n return this.service\n }\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [\n {\n id: 'credentials',\n title: 'Cloudflare Credentials',\n description: 'API credentials for fetching TURN relay tokens from Cloudflare Realtime.',\n immediate: true,\n fields: [\n {\n type: 'info' as const,\n key: 'turnHelp',\n label: 'How to get the Account ID + API Token',\n format: 'html' as const,\n content:\n '<ul>' +\n '<li><strong>Account ID</strong> — open ' +\n '<a href=\"https://dash.cloudflare.com/\">dash.cloudflare.com</a>, ' +\n 'pick your account; the URL shows ' +\n '<code>dash.cloudflare.com/<accountId>/...</code>. ' +\n 'Copy that hex id.</li>' +\n '<li><strong>TURN Token</strong> — go to ' +\n '<a href=\"https://dash.cloudflare.com/?to=/:account/realtime/turn/overview\" ' +\n 'target=\"_blank\" rel=\"noopener noreferrer\">' +\n 'Realtime → TURN</a>, click <strong>Create TURN App</strong>, ' +\n 'copy the generated <em>Token ID</em> and <em>API Token</em>.</li>' +\n '<li>Paste the <em>API Token</em> below (the Token ID is the ' +\n 'Account ID variant for TURN apps — Cloudflare uses both naming ' +\n 'conventions across their dashboard).</li>' +\n '<li>Permissions auto-set when you create a Realtime app — ' +\n 'no manual scope picker needed.</li>' +\n '</ul>',\n variant: 'info' as const,\n },\n this.field({\n type: 'text',\n key: 'accountId',\n label: 'Account ID',\n description: 'Your Cloudflare account ID (the hex slug in the dashboard URL).',\n placeholder: 'a1b2c3d4e5f6...',\n required: true,\n }),\n this.field({\n type: 'password',\n key: 'apiToken',\n label: 'API Token',\n description: 'Cloudflare Realtime TURN app token (Calls: Read).',\n showToggle: true,\n required: true,\n }),\n ],\n },\n ],\n })\n }\n}\n"],"names":["BaseAddon","turnProviderCapability"],"mappings":";;;AAkBO,MAAM,sBAAsB;AAAA,EAIjC,YACE,SACiB,QACjB;AADiB,SAAA,SAAA;AAAA,EAChB;AAAA,EANM,KAAK;AAAA,EACL,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAWhB,iBAAwC;AACtC,SAAK,OAAO,MAAM,uCAAuC;AAGzD,WAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,QAAA;AAAA,QAEF,UAAU;AAAA,QACV,YAAY;AAAA,MAAA;AAAA,IACd;AAAA,EAEJ;AACF;ACrCO,MAAM,4BAA4BA,MAAAA,UAAgC;AAAA,EAC/D,UAAwC;AAAA,EAEhD,cAAc;AACZ,UAAM,EAAE,UAAU,IAAI,WAAW,IAAI;AAAA,EACvC;AAAA,EAEA,MAAgB,eAAgD;AAC9D,SAAK,UAAU,IAAI,sBAAsB,KAAK,QAAQ,KAAK,IAAI,MAAM;AACrE,SAAK,IAAI,OAAO,KAAK,6BAA6B;AAClD,WAAO,CAAC,EAAE,YAAYC,MAAAA,wBAAwB,UAAU,KAAK,SAAS;AAAA,EACxE;AAAA,EAEA,MAAgB,aAA4B;AAC1C,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,aAAoC;AAClC,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,iCAAiC;AACpE,WAAO,KAAK;AAAA,EACd;AAAA,EAEU,uBAAuB;AAC/B,WAAO,KAAK,OAAO;AAAA,MACjB,UAAU;AAAA,QACR;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,aAAa;AAAA,UACb,WAAW;AAAA,UACX,QAAQ;AAAA,YACN;AAAA,cACE,MAAM;AAAA,cACN,KAAK;AAAA,cACL,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,SACE;AAAA,cAiBF,SAAS;AAAA,YAAA;AAAA,YAEX,KAAK,MAAM;AAAA,cACT,MAAM;AAAA,cACN,KAAK;AAAA,cACL,OAAO;AAAA,cACP,aAAa;AAAA,cACb,aAAa;AAAA,cACb,UAAU;AAAA,YAAA,CACX;AAAA,YACD,KAAK,MAAM;AAAA,cACT,MAAM;AAAA,cACN,KAAK;AAAA,cACL,OAAO;AAAA,cACP,aAAa;AAAA,cACb,YAAY;AAAA,cACZ,UAAU;AAAA,YAAA,CACX;AAAA,UAAA;AAAA,QACH;AAAA,MACF;AAAA,IACF,CACD;AAAA,EACH;AACF;;;"}
|
|
@@ -54,7 +54,7 @@ class CloudflareTurnAddon extends BaseAddon {
|
|
|
54
54
|
key: "turnHelp",
|
|
55
55
|
label: "How to get the Account ID + API Token",
|
|
56
56
|
format: "html",
|
|
57
|
-
content: '<ul><li><strong>Account ID</strong> — open <a href="https://dash.cloudflare.com/">dash.cloudflare.com</a>, pick your account; the URL shows <code>dash.cloudflare.com/<accountId>/...</code>. Copy that hex id.</li><li><strong>TURN Token</strong> — go to <a href="https://dash.cloudflare.com/?to=/:account/
|
|
57
|
+
content: '<ul><li><strong>Account ID</strong> — open <a href="https://dash.cloudflare.com/">dash.cloudflare.com</a>, pick your account; the URL shows <code>dash.cloudflare.com/<accountId>/...</code>. Copy that hex id.</li><li><strong>TURN Token</strong> — go to <a href="https://dash.cloudflare.com/?to=/:account/realtime/turn/overview" target="_blank" rel="noopener noreferrer">Realtime → TURN</a>, click <strong>Create TURN App</strong>, copy the generated <em>Token ID</em> and <em>API Token</em>.</li><li>Paste the <em>API Token</em> below (the Token ID is the Account ID variant for TURN apps — Cloudflare uses both naming conventions across their dashboard).</li><li>Permissions auto-set when you create a Realtime app — no manual scope picker needed.</li></ul>',
|
|
58
58
|
variant: "info"
|
|
59
59
|
},
|
|
60
60
|
this.field({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare-turn.addon.mjs","sources":["../src/cloudflare-turn.ts","../src/cloudflare-turn.addon.ts"],"sourcesContent":["import type { IScopedLogger } from '@camstack/types'\n\nexport interface CloudflareTurnConfig {\n readonly apiToken: string\n readonly accountId: string\n}\n\ntype TurnServer = { urls: string | string[]; username?: string; credential?: string }\n\n/**\n * Cloudflare TURN/STUN provider.\n *\n * Implements the `turn-provider` capability (system collection). Called\n * by the `webrtc` capability implementations when building ICE server\n * lists for a new peer connection. Each call SHOULD fetch fresh\n * short-lived credentials — currently stubbed with static values until\n * the Cloudflare Calls API integration is wired.\n */\nexport class CloudflareTurnService {\n readonly id = 'cloudflare-turn'\n readonly name = 'Cloudflare TURN'\n\n constructor(\n _config: CloudflareTurnConfig,\n private readonly logger: IScopedLogger,\n ) {}\n\n /**\n * Return the current TURN/STUN server list with credentials.\n * Implements `turn-provider` capability.\n */\n getTurnServers(): readonly TurnServer[] {\n this.logger.debug('Fetching TURN servers from Cloudflare')\n // TODO: implement real Cloudflare TURN integration.\n // Real integration requires a Cloudflare Calls API call using this.apiToken/accountId.\n return [\n {\n urls: [\n 'turn:turn.cloudflare.com:3478?transport=udp',\n 'turn:turn.cloudflare.com:3478?transport=tcp',\n ],\n username: 'temp-user',\n credential: 'temp-credential',\n },\n ]\n }\n}\n","import type { ProviderRegistration } from '@camstack/types'\nimport { BaseAddon, turnProviderCapability } from '@camstack/types'\nimport { CloudflareTurnService } from './cloudflare-turn'\nimport type { CloudflareTurnConfig } from './cloudflare-turn'\n\n/**\n * Settings redesign Phase 3: cloudflare-turn is node-level credentials\n * storage. Implements `getGlobalSettings`.\n */\nexport class CloudflareTurnAddon extends BaseAddon<CloudflareTurnConfig> {\n private service: CloudflareTurnService | null = null\n\n constructor() {\n super({ apiToken: '', accountId: '' })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.service = new CloudflareTurnService(this.config, this.ctx.logger)\n this.ctx.logger.info('Cloudflare TURN initialized')\n return [{ capability: turnProviderCapability, provider: this.service }]\n }\n\n protected async onShutdown(): Promise<void> {\n this.service = null\n }\n\n getService(): CloudflareTurnService {\n if (!this.service) throw new Error('Cloudflare TURN not initialized')\n return this.service\n }\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [\n {\n id: 'credentials',\n title: 'Cloudflare Credentials',\n description: 'API credentials for fetching TURN relay tokens from Cloudflare Realtime.',\n immediate: true,\n fields: [\n {\n type: 'info' as const,\n key: 'turnHelp',\n label: 'How to get the Account ID + API Token',\n format: 'html' as const,\n content:\n '<ul>' +\n '<li><strong>Account ID</strong> — open ' +\n '<a href=\"https://dash.cloudflare.com/\">dash.cloudflare.com</a>, ' +\n 'pick your account; the URL shows ' +\n '<code>dash.cloudflare.com/<accountId>/...</code>. ' +\n 'Copy that hex id.</li>' +\n '<li><strong>TURN Token</strong> — go to ' +\n '<a href=\"https://dash.cloudflare.com/?to=/:account/
|
|
1
|
+
{"version":3,"file":"cloudflare-turn.addon.mjs","sources":["../src/cloudflare-turn.ts","../src/cloudflare-turn.addon.ts"],"sourcesContent":["import type { IScopedLogger } from '@camstack/types'\n\nexport interface CloudflareTurnConfig {\n readonly apiToken: string\n readonly accountId: string\n}\n\ntype TurnServer = { urls: string | string[]; username?: string; credential?: string }\n\n/**\n * Cloudflare TURN/STUN provider.\n *\n * Implements the `turn-provider` capability (system collection). Called\n * by the `webrtc` capability implementations when building ICE server\n * lists for a new peer connection. Each call SHOULD fetch fresh\n * short-lived credentials — currently stubbed with static values until\n * the Cloudflare Calls API integration is wired.\n */\nexport class CloudflareTurnService {\n readonly id = 'cloudflare-turn'\n readonly name = 'Cloudflare TURN'\n\n constructor(\n _config: CloudflareTurnConfig,\n private readonly logger: IScopedLogger,\n ) {}\n\n /**\n * Return the current TURN/STUN server list with credentials.\n * Implements `turn-provider` capability.\n */\n getTurnServers(): readonly TurnServer[] {\n this.logger.debug('Fetching TURN servers from Cloudflare')\n // TODO: implement real Cloudflare TURN integration.\n // Real integration requires a Cloudflare Calls API call using this.apiToken/accountId.\n return [\n {\n urls: [\n 'turn:turn.cloudflare.com:3478?transport=udp',\n 'turn:turn.cloudflare.com:3478?transport=tcp',\n ],\n username: 'temp-user',\n credential: 'temp-credential',\n },\n ]\n }\n}\n","import type { ProviderRegistration } from '@camstack/types'\nimport { BaseAddon, turnProviderCapability } from '@camstack/types'\nimport { CloudflareTurnService } from './cloudflare-turn'\nimport type { CloudflareTurnConfig } from './cloudflare-turn'\n\n/**\n * Settings redesign Phase 3: cloudflare-turn is node-level credentials\n * storage. Implements `getGlobalSettings`.\n */\nexport class CloudflareTurnAddon extends BaseAddon<CloudflareTurnConfig> {\n private service: CloudflareTurnService | null = null\n\n constructor() {\n super({ apiToken: '', accountId: '' })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.service = new CloudflareTurnService(this.config, this.ctx.logger)\n this.ctx.logger.info('Cloudflare TURN initialized')\n return [{ capability: turnProviderCapability, provider: this.service }]\n }\n\n protected async onShutdown(): Promise<void> {\n this.service = null\n }\n\n getService(): CloudflareTurnService {\n if (!this.service) throw new Error('Cloudflare TURN not initialized')\n return this.service\n }\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [\n {\n id: 'credentials',\n title: 'Cloudflare Credentials',\n description: 'API credentials for fetching TURN relay tokens from Cloudflare Realtime.',\n immediate: true,\n fields: [\n {\n type: 'info' as const,\n key: 'turnHelp',\n label: 'How to get the Account ID + API Token',\n format: 'html' as const,\n content:\n '<ul>' +\n '<li><strong>Account ID</strong> — open ' +\n '<a href=\"https://dash.cloudflare.com/\">dash.cloudflare.com</a>, ' +\n 'pick your account; the URL shows ' +\n '<code>dash.cloudflare.com/<accountId>/...</code>. ' +\n 'Copy that hex id.</li>' +\n '<li><strong>TURN Token</strong> — go to ' +\n '<a href=\"https://dash.cloudflare.com/?to=/:account/realtime/turn/overview\" ' +\n 'target=\"_blank\" rel=\"noopener noreferrer\">' +\n 'Realtime → TURN</a>, click <strong>Create TURN App</strong>, ' +\n 'copy the generated <em>Token ID</em> and <em>API Token</em>.</li>' +\n '<li>Paste the <em>API Token</em> below (the Token ID is the ' +\n 'Account ID variant for TURN apps — Cloudflare uses both naming ' +\n 'conventions across their dashboard).</li>' +\n '<li>Permissions auto-set when you create a Realtime app — ' +\n 'no manual scope picker needed.</li>' +\n '</ul>',\n variant: 'info' as const,\n },\n this.field({\n type: 'text',\n key: 'accountId',\n label: 'Account ID',\n description: 'Your Cloudflare account ID (the hex slug in the dashboard URL).',\n placeholder: 'a1b2c3d4e5f6...',\n required: true,\n }),\n this.field({\n type: 'password',\n key: 'apiToken',\n label: 'API Token',\n description: 'Cloudflare Realtime TURN app token (Calls: Read).',\n showToggle: true,\n required: true,\n }),\n ],\n },\n ],\n })\n }\n}\n"],"names":[],"mappings":";AAkBO,MAAM,sBAAsB;AAAA,EAIjC,YACE,SACiB,QACjB;AADiB,SAAA,SAAA;AAAA,EAChB;AAAA,EANM,KAAK;AAAA,EACL,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAWhB,iBAAwC;AACtC,SAAK,OAAO,MAAM,uCAAuC;AAGzD,WAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,QAAA;AAAA,QAEF,UAAU;AAAA,QACV,YAAY;AAAA,MAAA;AAAA,IACd;AAAA,EAEJ;AACF;ACrCO,MAAM,4BAA4B,UAAgC;AAAA,EAC/D,UAAwC;AAAA,EAEhD,cAAc;AACZ,UAAM,EAAE,UAAU,IAAI,WAAW,IAAI;AAAA,EACvC;AAAA,EAEA,MAAgB,eAAgD;AAC9D,SAAK,UAAU,IAAI,sBAAsB,KAAK,QAAQ,KAAK,IAAI,MAAM;AACrE,SAAK,IAAI,OAAO,KAAK,6BAA6B;AAClD,WAAO,CAAC,EAAE,YAAY,wBAAwB,UAAU,KAAK,SAAS;AAAA,EACxE;AAAA,EAEA,MAAgB,aAA4B;AAC1C,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,aAAoC;AAClC,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,iCAAiC;AACpE,WAAO,KAAK;AAAA,EACd;AAAA,EAEU,uBAAuB;AAC/B,WAAO,KAAK,OAAO;AAAA,MACjB,UAAU;AAAA,QACR;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,aAAa;AAAA,UACb,WAAW;AAAA,UACX,QAAQ;AAAA,YACN;AAAA,cACE,MAAM;AAAA,cACN,KAAK;AAAA,cACL,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,SACE;AAAA,cAiBF,SAAS;AAAA,YAAA;AAAA,YAEX,KAAK,MAAM;AAAA,cACT,MAAM;AAAA,cACN,KAAK;AAAA,cACL,OAAO;AAAA,cACP,aAAa;AAAA,cACb,aAAa;AAAA,cACb,UAAU;AAAA,YAAA,CACX;AAAA,YACD,KAAK,MAAM;AAAA,cACT,MAAM;AAAA,cACN,KAAK;AAAA,cACL,OAAO;AAAA,cACP,aAAa;AAAA,cACb,YAAY;AAAA,cACZ,UAAU;AAAA,YAAA,CACX;AAAA,UAAA;AAAA,QACH;AAAA,MACF;AAAA,IACF,CACD;AAAA,EACH;AACF;"}
|