archos-sdk 0.0.0-stage → 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,34 @@
1
+ # Non-Commercial Source Available License
2
+
3
+ Copyright (c) 2026 Arch Technologies Inc
4
+
5
+ ## Permissions
6
+
7
+ You may:
8
+
9
+ - Use this software for personal, non-commercial purposes
10
+ - Modify the software for your own use
11
+ - Distribute copies of the software to others for non-commercial use
12
+ - Run instances of this software for yourself, your friends, or your family
13
+
14
+ ## Restrictions
15
+
16
+ You may NOT:
17
+
18
+ - Use this software for any commercial purpose without a separate written agreement
19
+ - Host this software as a service for paying users
20
+ - Sell access to an instance of this software
21
+ - Integrate this software into a commercial product
22
+ - Use this software to generate revenue in any form (including advertising, donations, or paid support)
23
+
24
+ ## Commercial Use
25
+
26
+ If you wish to use this software commercially (including hosting as a service), please contact:
27
+
28
+ hello@chuksemmanuel.com
29
+
30
+ A commercial license agreement will be provided. Pricing is negotiable based on your use case.
31
+
32
+ ## No Warranty
33
+
34
+ This software is provided "as is" without warranty of any kind.
package/README.md CHANGED
@@ -1,3 +1,65 @@
1
- # Temporary Holding Version
1
+ # archos-sdk
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Client library for archOS apps: verify the session the shell hands your app, talk to the platform,
4
+ and run the App Bridge.
5
+
6
+ ```bash
7
+ pnpm add archos-sdk
8
+ ```
9
+
10
+ ## SvelteKit
11
+
12
+ `createArchHandle` builds the server hook that authenticates requests coming from the archOS shell.
13
+ It works standalone too: with no credentials configured, the app runs on its own.
14
+
15
+ ```ts
16
+ // src/hooks.server.ts
17
+ import { createArchHandle } from 'archos-sdk/sveltekit';
18
+ import { env } from '$env/dynamic/private';
19
+ import { redirect } from '@sveltejs/kit';
20
+
21
+ export const handle = createArchHandle(
22
+ {
23
+ rootRoute: '/',
24
+ sdkConfig: {
25
+ apiKey: env.LIVE_API_KEY,
26
+ appSlug: env.APP_SLUG,
27
+ baseUrl: env.PLATFORM_URL
28
+ }
29
+ },
30
+ redirect
31
+ );
32
+ ```
33
+
34
+ ## Server-side API calls
35
+
36
+ ```ts
37
+ import { PlatformSDK } from 'archos-sdk';
38
+
39
+ const platform = new PlatformSDK({
40
+ apiKey: env.LIVE_API_KEY,
41
+ appSlug: env.APP_SLUG,
42
+ baseUrl: env.PLATFORM_URL
43
+ });
44
+ ```
45
+
46
+ ## In the browser
47
+
48
+ `archos-sdk/app-bridge` is the other half: the connection between your app and the shell that hosts
49
+ it. It carries the session, the platform theme, and the messages the two exchange — charges, wallet
50
+ state, and navigation.
51
+
52
+ | Export | What it is |
53
+ | -------------------------------------------- | ----------------------------------------- |
54
+ | `createAppBridge`, `AppBridge`, `AppBridgeError` | The connection to the shell |
55
+ | `isAppBridgeEnvelope`, `ARCH_APP_BRIDGE_CHANNEL` | The message protocol |
56
+ | `PlatformSDK`, `ArchAppClient` | The platform API |
57
+ | `resolvePlatformBaseUrl` | Work out the platform origin |
58
+
59
+ ## Links
60
+
61
+ - Repository: <https://github.com/chuksemmanuel/archos>
62
+ - Components and theme: [`archos-ui`](https://www.npmjs.com/package/archos-ui)
63
+ - CLI: [`archos`](https://www.npmjs.com/package/archos)
64
+
65
+ Licensed for personal use; commercial use needs a written agreement. See [LICENSE](./LICENSE).
@@ -0,0 +1,2 @@
1
+ 'use strict';var d="arch.app-bridge",T=1,g="arch.app-bridge.bootstrap",s=class extends Error{constructor(t,i){super(i);this.code=t;this.name="AppBridgeError";}code};function E(n){if(!n||typeof n!="object")return null;let e=n;if(e.channel!==d||e.version!==1||typeof e.platformOrigin!="string"||typeof e.bridgeSession!="string"||!e.bridgeSession)return null;try{let t=new URL(e.platformOrigin);return ["http:","https:"].includes(t.protocol)?{platformOrigin:t.origin,bridgeSession:e.bridgeSession}:null}catch{return null}}function p(n){try{window.sessionStorage.setItem(g,JSON.stringify(n));}catch{}}function A(n){if(typeof window>"u")return null;let e=n.platformOrigin||n.targetOrigin;if(e&&e!=="*"&&n.bridgeSession){let r={platformOrigin:new URL(e).origin,bridgeSession:n.bridgeSession};return p(r),r}try{let r=E(JSON.parse(window.name));if(r)return p(r),r}catch{}let t=new URL(window.location.href),i=e||t.searchParams.get("platform_origin"),o=n.bridgeSession||t.searchParams.get("bridge_session");if(i&&i!=="*"&&o){let r={platformOrigin:new URL(i).origin,bridgeSession:o};return p(r),r}try{let r=window.sessionStorage.getItem(g);if(!r)return null;let c=JSON.parse(r);return !c.platformOrigin||!c.bridgeSession?null:{platformOrigin:new URL(c.platformOrigin).origin,bridgeSession:c.bridgeSession}}catch{return null}}function R(n){if(!n||typeof n!="object")return false;let e=n;return e.channel===d&&e.version===1&&typeof e.bridgeSession=="string"&&typeof e.type=="string"}var l=class{timeoutMs;bootstrap;pending=new Map;connection=null;connectionPromise=null;resolveConnection=null;rejectConnection=null;connectionTimeout=null;connectionRetry=null;cachedToken=null;cachedPreferences=null;listening=false;checkout={open:e=>this.openCheckout(e)};constructor(e={}){this.timeoutMs=e.timeoutMs??3e3,this.bootstrap=A(e);}isEmbedded(){if(typeof window>"u")return false;try{return window.self!==window.top}catch{return true}}connect(){return this.connection?Promise.resolve(this.connection):this.connectionPromise?this.connectionPromise:this.isEmbedded()?this.bootstrap?(this.startListening(),this.connectionPromise=new Promise((e,t)=>{this.resolveConnection=e,this.rejectConnection=t,this.connectionTimeout=setTimeout(()=>{let i=new s("BRIDGE_UNAVAILABLE",`Arch App Bridge did not connect within ${this.timeoutMs}ms.`);this.rejectConnection?.(i),this.resetConnection();},this.timeoutMs),this.post("APP_READY"),this.connectionRetry=setInterval(()=>{this.connection||this.post("APP_READY");},250);}),this.connectionPromise):Promise.reject(new s("BOOTSTRAP_MISSING","Arch App Bridge bootstrap is missing.")):Promise.reject(new s("NOT_EMBEDDED","The app is not embedded in the Arch platform."))}async getSessionToken(){if(this.cachedToken&&Date.now()<this.cachedToken.expiresAt-1e4)return this.cachedToken.token;await this.connect();let e=await this.request("GET_SESSION_TOKEN","SESSION_TOKEN_RESPONSE",void 0,"AUTH_TIMEOUT");if(!e.token)throw new s("REQUEST_FAILED",e.error||"The platform did not issue a token.");return this.cachedToken={token:e.token,expiresAt:e.expiresAt||Date.now()+6e4},e.preferences&&(this.cachedPreferences=e.preferences),e.token}async getPreferences(){return this.cachedPreferences||await this.getSessionToken(),this.cachedPreferences}async fetch(e,t={}){let i=await this.getSessionToken(),o=new Headers(t.headers||{});return o.set("Authorization",`Bearer ${i}`),o.set("x-arch-session-token",i),this.bootstrap&&o.set("x-arch-platform-origin",this.bootstrap.platformOrigin),fetch(e,{...t,headers:o})}async openCheckout(e){if(!e.chargeRef)throw new s("REQUEST_FAILED","chargeRef is required.");if(!this.isEmbedded()){if(!e.confirmationUrl)throw new s("BRIDGE_UNAVAILABLE","Standalone checkout requires a confirmationUrl.");return window.open(e.confirmationUrl,"arch_checkout","width=500,height=700,scrollbars=yes,resizable=yes"),{chargeRef:e.chargeRef,status:"pending"}}return await this.connect(),this.request("OPEN_CHECKOUT","CHECKOUT_RESULT",{chargeRef:e.chargeRef},"CHECKOUT_TIMEOUT",Math.max(this.timeoutMs,5*6e4))}openBillingModal(e){let t=e.chargeRef||e.chargeId;return t?this.openCheckout({chargeRef:t,confirmationUrl:e.confirmationUrl}):Promise.reject(new s("REQUEST_FAILED","chargeRef is required."))}destroy(){this.listening&&typeof window<"u"&&window.removeEventListener("message",this.handleMessage),this.listening=false;for(let e of this.pending.values())clearTimeout(e.timeout),e.reject(new s("BRIDGE_UNAVAILABLE","App Bridge was destroyed."));this.pending.clear(),this.resetConnection();}startListening(){this.listening||(window.addEventListener("message",this.handleMessage),this.listening=true);}handleMessage=e=>{if(!this.bootstrap||e.origin!==this.bootstrap.platformOrigin||e.source!==window.parent||!R(e.data)||e.data.bridgeSession!==this.bootstrap.bridgeSession)return;if(e.data.type==="HOST_READY"){this.post("APP_READY");return}if(e.data.type==="CONNECTED"){let i=e.data.payload||{};this.connection={capabilities:i.capabilities||[]},this.connectionTimeout&&clearTimeout(this.connectionTimeout),this.connectionRetry&&clearInterval(this.connectionRetry),this.connectionRetry=null,this.resolveConnection?.(this.connection),this.resolveConnection=null,this.rejectConnection=null;return}if(!e.data.requestId)return;let t=this.pending.get(e.data.requestId);!t||t.expectedType!==e.data.type||(clearTimeout(t.timeout),this.pending.delete(e.data.requestId),t.resolve(e.data.payload));};request(e,t,i,o="REQUEST_FAILED",r=this.timeoutMs){let c=crypto.randomUUID();return new Promise((h,u)=>{let f=setTimeout(()=>{this.pending.delete(c),u(new s(o,`${e} timed out after ${r}ms.`));},r);this.pending.set(c,{expectedType:t,resolve:m=>h(m),reject:u,timeout:f}),this.post(e,c,i);})}post(e,t,i){if(!this.bootstrap)throw new s("BRIDGE_UNAVAILABLE","App Bridge bootstrap is missing.");let o={channel:d,version:1,bridgeSession:this.bootstrap.bridgeSession,type:e};t&&(o.requestId=t),i!==void 0&&(o.payload=i),window.parent.postMessage(o,this.bootstrap.platformOrigin);}resetConnection(){this.connectionTimeout&&clearTimeout(this.connectionTimeout),this.connectionRetry&&clearInterval(this.connectionRetry),this.connectionTimeout=null,this.connectionRetry=null,this.connectionPromise=null,this.resolveConnection=null,this.rejectConnection=null,this.connection=null;}},a=null;function B(n){return a||(a=new l(n)),a}function w(){a?.destroy(),a=null;}exports.ARCH_APP_BRIDGE_CHANNEL=d;exports.ARCH_APP_BRIDGE_VERSION=T;exports.AppBridge=l;exports.AppBridgeError=s;exports.createAppBridge=B;exports.isAppBridgeEnvelope=R;exports.resetAppBridge=w;//# sourceMappingURL=app-bridge.cjs.map
2
+ //# sourceMappingURL=app-bridge.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/app-bridge.ts"],"names":["ARCH_APP_BRIDGE_CHANNEL","ARCH_APP_BRIDGE_VERSION","BRIDGE_BOOTSTRAP_STORAGE_KEY","AppBridgeError","code","message","parseNamedBootstrap","value","bootstrap","origin","persistBootstrap","readBootstrap","options","explicitOrigin","url","platformOrigin","bridgeSession","stored","parsed","isAppBridgeEnvelope","AppBridge","resolve","reject","error","payload","input","init","token","headers","chargeRef","pending","event","type","expectedType","timeoutCode","timeoutMs","requestId","timeout","envelope","singleton","createAppBridge","resetAppBridge"],"mappings":"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","file":"app-bridge.cjs","sourcesContent":["export const ARCH_APP_BRIDGE_CHANNEL = 'arch.app-bridge';\nexport const ARCH_APP_BRIDGE_VERSION = 1;\n\nconst BRIDGE_BOOTSTRAP_STORAGE_KEY = 'arch.app-bridge.bootstrap';\n\nexport type AppBridgeErrorCode =\n | 'NOT_EMBEDDED'\n | 'BOOTSTRAP_MISSING'\n | 'BOOTSTRAP_INVALID'\n | 'BRIDGE_UNAVAILABLE'\n | 'ORIGIN_MISMATCH'\n | 'AUTH_TIMEOUT'\n | 'CHECKOUT_TIMEOUT'\n | 'PROTOCOL_MISMATCH'\n | 'REQUEST_FAILED';\n\nexport class AppBridgeError extends Error {\n constructor(\n public readonly code: AppBridgeErrorCode,\n message: string\n ) {\n super(message);\n this.name = 'AppBridgeError';\n }\n}\n\nexport interface UserPreferences {\n mode: 'dark' | 'light';\n theme: string;\n radius: string;\n [key: string]: unknown;\n}\n\nexport interface AppBridgeOptions {\n platformOrigin?: string;\n bridgeSession?: string;\n /** @deprecated Use platformOrigin. */\n targetOrigin?: string;\n timeoutMs?: number;\n}\n\nexport interface BridgeConnection {\n capabilities: string[];\n}\n\nexport interface CheckoutOptions {\n chargeRef: string;\n /** Used only for the standalone popup fallback. */\n confirmationUrl?: string;\n}\n\nexport interface CheckoutResult {\n chargeRef: string;\n status: 'approved' | 'declined' | 'pending' | 'unknown';\n error?: string;\n}\n\n/** @deprecated Use CheckoutOptions. */\nexport interface BillingModalOptions {\n chargeRef?: string;\n chargeId?: string;\n confirmationUrl?: string;\n}\n\nexport interface AppBridgeEnvelope<T = unknown> {\n channel: typeof ARCH_APP_BRIDGE_CHANNEL;\n version: typeof ARCH_APP_BRIDGE_VERSION;\n bridgeSession: string;\n type: string;\n requestId?: string;\n payload?: T;\n}\n\ninterface PendingRequest {\n expectedType: string;\n resolve: (payload: unknown) => void;\n reject: (error: AppBridgeError) => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\ninterface BridgeBootstrap {\n platformOrigin: string;\n bridgeSession: string;\n}\n\nfunction parseNamedBootstrap(value: unknown): BridgeBootstrap | null {\n if (!value || typeof value !== 'object') return null;\n const bootstrap = value as Partial<BridgeBootstrap & { channel: string; version: number }>;\n if (\n bootstrap.channel !== ARCH_APP_BRIDGE_CHANNEL ||\n bootstrap.version !== ARCH_APP_BRIDGE_VERSION ||\n typeof bootstrap.platformOrigin !== 'string' ||\n typeof bootstrap.bridgeSession !== 'string' ||\n !bootstrap.bridgeSession\n ) {\n return null;\n }\n try {\n const origin = new URL(bootstrap.platformOrigin);\n if (!['http:', 'https:'].includes(origin.protocol)) return null;\n return { platformOrigin: origin.origin, bridgeSession: bootstrap.bridgeSession };\n } catch {\n return null;\n }\n}\n\nfunction persistBootstrap(bootstrap: BridgeBootstrap) {\n try {\n window.sessionStorage.setItem(BRIDGE_BOOTSTRAP_STORAGE_KEY, JSON.stringify(bootstrap));\n } catch {\n // Storage can be disabled; window.name still survives iframe navigation.\n }\n}\n\nfunction readBootstrap(options: AppBridgeOptions): BridgeBootstrap | null {\n if (typeof window === 'undefined') return null;\n\n const explicitOrigin = options.platformOrigin || options.targetOrigin;\n if (explicitOrigin && explicitOrigin !== '*' && options.bridgeSession) {\n const bootstrap = {\n platformOrigin: new URL(explicitOrigin).origin,\n bridgeSession: options.bridgeSession\n };\n persistBootstrap(bootstrap);\n return bootstrap;\n }\n\n try {\n const bootstrap = parseNamedBootstrap(JSON.parse(window.name));\n if (bootstrap) {\n persistBootstrap(bootstrap);\n return bootstrap;\n }\n } catch {\n // window.name may be empty or owned by unrelated embedding software.\n }\n\n const url = new URL(window.location.href);\n const platformOrigin = explicitOrigin || url.searchParams.get('platform_origin');\n const bridgeSession = options.bridgeSession || url.searchParams.get('bridge_session');\n\n if (platformOrigin && platformOrigin !== '*' && bridgeSession) {\n const bootstrap = {\n platformOrigin: new URL(platformOrigin).origin,\n bridgeSession\n };\n persistBootstrap(bootstrap);\n return bootstrap;\n }\n\n try {\n const stored = window.sessionStorage.getItem(BRIDGE_BOOTSTRAP_STORAGE_KEY);\n if (!stored) return null;\n const parsed = JSON.parse(stored) as Partial<BridgeBootstrap>;\n if (!parsed.platformOrigin || !parsed.bridgeSession) return null;\n return {\n platformOrigin: new URL(parsed.platformOrigin).origin,\n bridgeSession: parsed.bridgeSession\n };\n } catch {\n return null;\n }\n}\n\nexport function isAppBridgeEnvelope(value: unknown): value is AppBridgeEnvelope {\n if (!value || typeof value !== 'object') return false;\n const message = value as Partial<AppBridgeEnvelope>;\n return (\n message.channel === ARCH_APP_BRIDGE_CHANNEL &&\n message.version === ARCH_APP_BRIDGE_VERSION &&\n typeof message.bridgeSession === 'string' &&\n typeof message.type === 'string'\n );\n}\n\nexport class AppBridge {\n private readonly timeoutMs: number;\n private readonly bootstrap: BridgeBootstrap | null;\n private readonly pending = new Map<string, PendingRequest>();\n private connection: BridgeConnection | null = null;\n private connectionPromise: Promise<BridgeConnection> | null = null;\n private resolveConnection: ((connection: BridgeConnection) => void) | null = null;\n private rejectConnection: ((error: AppBridgeError) => void) | null = null;\n private connectionTimeout: ReturnType<typeof setTimeout> | null = null;\n private connectionRetry: ReturnType<typeof setInterval> | null = null;\n private cachedToken: { token: string; expiresAt: number } | null = null;\n private cachedPreferences: UserPreferences | null = null;\n private listening = false;\n\n public readonly checkout = {\n open: (options: CheckoutOptions) => this.openCheckout(options)\n };\n\n constructor(options: AppBridgeOptions = {}) {\n this.timeoutMs = options.timeoutMs ?? 3000;\n this.bootstrap = readBootstrap(options);\n }\n\n public isEmbedded(): boolean {\n if (typeof window === 'undefined') return false;\n try {\n return window.self !== window.top;\n } catch {\n return true;\n }\n }\n\n public connect(): Promise<BridgeConnection> {\n if (this.connection) return Promise.resolve(this.connection);\n if (this.connectionPromise) return this.connectionPromise;\n if (!this.isEmbedded()) {\n return Promise.reject(\n new AppBridgeError('NOT_EMBEDDED', 'The app is not embedded in the Arch platform.')\n );\n }\n if (!this.bootstrap) {\n return Promise.reject(\n new AppBridgeError('BOOTSTRAP_MISSING', 'Arch App Bridge bootstrap is missing.')\n );\n }\n\n this.startListening();\n this.connectionPromise = new Promise<BridgeConnection>((resolve, reject) => {\n this.resolveConnection = resolve;\n this.rejectConnection = reject;\n this.connectionTimeout = setTimeout(() => {\n const error = new AppBridgeError(\n 'BRIDGE_UNAVAILABLE',\n `Arch App Bridge did not connect within ${this.timeoutMs}ms.`\n );\n this.rejectConnection?.(error);\n this.resetConnection();\n }, this.timeoutMs);\n this.post('APP_READY');\n this.connectionRetry = setInterval(() => {\n if (!this.connection) this.post('APP_READY');\n }, 250);\n });\n return this.connectionPromise;\n }\n\n public async getSessionToken(): Promise<string> {\n if (this.cachedToken && Date.now() < this.cachedToken.expiresAt - 10_000) {\n return this.cachedToken.token;\n }\n\n await this.connect();\n const payload = await this.request<{\n token?: string;\n expiresAt?: number;\n preferences?: UserPreferences;\n error?: string;\n }>('GET_SESSION_TOKEN', 'SESSION_TOKEN_RESPONSE', undefined, 'AUTH_TIMEOUT');\n\n if (!payload.token) {\n throw new AppBridgeError(\n 'REQUEST_FAILED',\n payload.error || 'The platform did not issue a token.'\n );\n }\n\n this.cachedToken = {\n token: payload.token,\n expiresAt: payload.expiresAt || Date.now() + 60_000\n };\n if (payload.preferences) this.cachedPreferences = payload.preferences;\n return payload.token;\n }\n\n public async getPreferences(): Promise<UserPreferences | null> {\n if (!this.cachedPreferences) await this.getSessionToken();\n return this.cachedPreferences;\n }\n\n public async fetch(input: RequestInfo | URL, init: RequestInit = {}): Promise<Response> {\n const token = await this.getSessionToken();\n const headers = new Headers(init.headers || {});\n headers.set('Authorization', `Bearer ${token}`);\n headers.set('x-arch-session-token', token);\n if (this.bootstrap) {\n headers.set('x-arch-platform-origin', this.bootstrap.platformOrigin);\n }\n return fetch(input, { ...init, headers });\n }\n\n public async openCheckout(options: CheckoutOptions): Promise<CheckoutResult> {\n if (!options.chargeRef) {\n throw new AppBridgeError('REQUEST_FAILED', 'chargeRef is required.');\n }\n\n if (!this.isEmbedded()) {\n if (!options.confirmationUrl) {\n throw new AppBridgeError(\n 'BRIDGE_UNAVAILABLE',\n 'Standalone checkout requires a confirmationUrl.'\n );\n }\n window.open(\n options.confirmationUrl,\n 'arch_checkout',\n 'width=500,height=700,scrollbars=yes,resizable=yes'\n );\n return { chargeRef: options.chargeRef, status: 'pending' };\n }\n\n await this.connect();\n return this.request<CheckoutResult>(\n 'OPEN_CHECKOUT',\n 'CHECKOUT_RESULT',\n { chargeRef: options.chargeRef },\n 'CHECKOUT_TIMEOUT',\n Math.max(this.timeoutMs, 5 * 60_000)\n );\n }\n\n /** @deprecated Use bridge.checkout.open({ chargeRef }). */\n public openBillingModal(options: BillingModalOptions): Promise<CheckoutResult> {\n const chargeRef = options.chargeRef || options.chargeId;\n if (!chargeRef) {\n return Promise.reject(new AppBridgeError('REQUEST_FAILED', 'chargeRef is required.'));\n }\n return this.openCheckout({\n chargeRef,\n confirmationUrl: options.confirmationUrl\n });\n }\n\n public destroy(): void {\n if (this.listening && typeof window !== 'undefined') {\n window.removeEventListener('message', this.handleMessage);\n }\n this.listening = false;\n for (const pending of this.pending.values()) {\n clearTimeout(pending.timeout);\n pending.reject(new AppBridgeError('BRIDGE_UNAVAILABLE', 'App Bridge was destroyed.'));\n }\n this.pending.clear();\n this.resetConnection();\n }\n\n private startListening() {\n if (this.listening) return;\n window.addEventListener('message', this.handleMessage);\n this.listening = true;\n }\n\n private readonly handleMessage = (event: MessageEvent) => {\n if (!this.bootstrap || event.origin !== this.bootstrap.platformOrigin) return;\n if (event.source !== window.parent) return;\n if (!isAppBridgeEnvelope(event.data)) return;\n if (event.data.bridgeSession !== this.bootstrap.bridgeSession) return;\n\n if (event.data.type === 'HOST_READY') {\n this.post('APP_READY');\n return;\n }\n\n if (event.data.type === 'CONNECTED') {\n const payload = (event.data.payload || {}) as Partial<BridgeConnection>;\n this.connection = { capabilities: payload.capabilities || [] };\n if (this.connectionTimeout) clearTimeout(this.connectionTimeout);\n if (this.connectionRetry) clearInterval(this.connectionRetry);\n this.connectionRetry = null;\n this.resolveConnection?.(this.connection);\n this.resolveConnection = null;\n this.rejectConnection = null;\n return;\n }\n\n if (!event.data.requestId) return;\n const pending = this.pending.get(event.data.requestId);\n if (!pending || pending.expectedType !== event.data.type) return;\n clearTimeout(pending.timeout);\n this.pending.delete(event.data.requestId);\n pending.resolve(event.data.payload);\n };\n\n private request<T>(\n type: string,\n expectedType: string,\n payload?: unknown,\n timeoutCode: AppBridgeErrorCode = 'REQUEST_FAILED',\n timeoutMs = this.timeoutMs\n ): Promise<T> {\n const requestId = crypto.randomUUID();\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pending.delete(requestId);\n reject(new AppBridgeError(timeoutCode, `${type} timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n this.pending.set(requestId, {\n expectedType,\n resolve: (value) => resolve(value as T),\n reject,\n timeout\n });\n this.post(type, requestId, payload);\n });\n }\n\n private post(type: string, requestId?: string, payload?: unknown) {\n if (!this.bootstrap) {\n throw new AppBridgeError('BRIDGE_UNAVAILABLE', 'App Bridge bootstrap is missing.');\n }\n const envelope: AppBridgeEnvelope = {\n channel: ARCH_APP_BRIDGE_CHANNEL,\n version: ARCH_APP_BRIDGE_VERSION,\n bridgeSession: this.bootstrap.bridgeSession,\n type\n };\n if (requestId) envelope.requestId = requestId;\n if (payload !== undefined) envelope.payload = payload;\n window.parent.postMessage(envelope, this.bootstrap.platformOrigin);\n }\n\n private resetConnection() {\n if (this.connectionTimeout) clearTimeout(this.connectionTimeout);\n if (this.connectionRetry) clearInterval(this.connectionRetry);\n this.connectionTimeout = null;\n this.connectionRetry = null;\n this.connectionPromise = null;\n this.resolveConnection = null;\n this.rejectConnection = null;\n this.connection = null;\n }\n}\n\nlet singleton: AppBridge | null = null;\n\nexport function createAppBridge(options?: AppBridgeOptions): AppBridge {\n if (!singleton) singleton = new AppBridge(options);\n return singleton;\n}\n\nexport function resetAppBridge(): void {\n singleton?.destroy();\n singleton = null;\n}\n"]}
@@ -0,0 +1,84 @@
1
+ declare const ARCH_APP_BRIDGE_CHANNEL = "arch.app-bridge";
2
+ declare const ARCH_APP_BRIDGE_VERSION = 1;
3
+ type AppBridgeErrorCode = 'NOT_EMBEDDED' | 'BOOTSTRAP_MISSING' | 'BOOTSTRAP_INVALID' | 'BRIDGE_UNAVAILABLE' | 'ORIGIN_MISMATCH' | 'AUTH_TIMEOUT' | 'CHECKOUT_TIMEOUT' | 'PROTOCOL_MISMATCH' | 'REQUEST_FAILED';
4
+ declare class AppBridgeError extends Error {
5
+ readonly code: AppBridgeErrorCode;
6
+ constructor(code: AppBridgeErrorCode, message: string);
7
+ }
8
+ interface UserPreferences {
9
+ mode: 'dark' | 'light';
10
+ theme: string;
11
+ radius: string;
12
+ [key: string]: unknown;
13
+ }
14
+ interface AppBridgeOptions {
15
+ platformOrigin?: string;
16
+ bridgeSession?: string;
17
+ /** @deprecated Use platformOrigin. */
18
+ targetOrigin?: string;
19
+ timeoutMs?: number;
20
+ }
21
+ interface BridgeConnection {
22
+ capabilities: string[];
23
+ }
24
+ interface CheckoutOptions {
25
+ chargeRef: string;
26
+ /** Used only for the standalone popup fallback. */
27
+ confirmationUrl?: string;
28
+ }
29
+ interface CheckoutResult {
30
+ chargeRef: string;
31
+ status: 'approved' | 'declined' | 'pending' | 'unknown';
32
+ error?: string;
33
+ }
34
+ /** @deprecated Use CheckoutOptions. */
35
+ interface BillingModalOptions {
36
+ chargeRef?: string;
37
+ chargeId?: string;
38
+ confirmationUrl?: string;
39
+ }
40
+ interface AppBridgeEnvelope<T = unknown> {
41
+ channel: typeof ARCH_APP_BRIDGE_CHANNEL;
42
+ version: typeof ARCH_APP_BRIDGE_VERSION;
43
+ bridgeSession: string;
44
+ type: string;
45
+ requestId?: string;
46
+ payload?: T;
47
+ }
48
+ declare function isAppBridgeEnvelope(value: unknown): value is AppBridgeEnvelope;
49
+ declare class AppBridge {
50
+ private readonly timeoutMs;
51
+ private readonly bootstrap;
52
+ private readonly pending;
53
+ private connection;
54
+ private connectionPromise;
55
+ private resolveConnection;
56
+ private rejectConnection;
57
+ private connectionTimeout;
58
+ private connectionRetry;
59
+ private cachedToken;
60
+ private cachedPreferences;
61
+ private listening;
62
+ readonly checkout: {
63
+ open: (options: CheckoutOptions) => Promise<CheckoutResult>;
64
+ };
65
+ constructor(options?: AppBridgeOptions);
66
+ isEmbedded(): boolean;
67
+ connect(): Promise<BridgeConnection>;
68
+ getSessionToken(): Promise<string>;
69
+ getPreferences(): Promise<UserPreferences | null>;
70
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
71
+ openCheckout(options: CheckoutOptions): Promise<CheckoutResult>;
72
+ /** @deprecated Use bridge.checkout.open({ chargeRef }). */
73
+ openBillingModal(options: BillingModalOptions): Promise<CheckoutResult>;
74
+ destroy(): void;
75
+ private startListening;
76
+ private readonly handleMessage;
77
+ private request;
78
+ private post;
79
+ private resetConnection;
80
+ }
81
+ declare function createAppBridge(options?: AppBridgeOptions): AppBridge;
82
+ declare function resetAppBridge(): void;
83
+
84
+ export { ARCH_APP_BRIDGE_CHANNEL, ARCH_APP_BRIDGE_VERSION, AppBridge, type AppBridgeEnvelope, AppBridgeError, type AppBridgeErrorCode, type AppBridgeOptions, type BillingModalOptions, type BridgeConnection, type CheckoutOptions, type CheckoutResult, type UserPreferences, createAppBridge, isAppBridgeEnvelope, resetAppBridge };
@@ -0,0 +1,84 @@
1
+ declare const ARCH_APP_BRIDGE_CHANNEL = "arch.app-bridge";
2
+ declare const ARCH_APP_BRIDGE_VERSION = 1;
3
+ type AppBridgeErrorCode = 'NOT_EMBEDDED' | 'BOOTSTRAP_MISSING' | 'BOOTSTRAP_INVALID' | 'BRIDGE_UNAVAILABLE' | 'ORIGIN_MISMATCH' | 'AUTH_TIMEOUT' | 'CHECKOUT_TIMEOUT' | 'PROTOCOL_MISMATCH' | 'REQUEST_FAILED';
4
+ declare class AppBridgeError extends Error {
5
+ readonly code: AppBridgeErrorCode;
6
+ constructor(code: AppBridgeErrorCode, message: string);
7
+ }
8
+ interface UserPreferences {
9
+ mode: 'dark' | 'light';
10
+ theme: string;
11
+ radius: string;
12
+ [key: string]: unknown;
13
+ }
14
+ interface AppBridgeOptions {
15
+ platformOrigin?: string;
16
+ bridgeSession?: string;
17
+ /** @deprecated Use platformOrigin. */
18
+ targetOrigin?: string;
19
+ timeoutMs?: number;
20
+ }
21
+ interface BridgeConnection {
22
+ capabilities: string[];
23
+ }
24
+ interface CheckoutOptions {
25
+ chargeRef: string;
26
+ /** Used only for the standalone popup fallback. */
27
+ confirmationUrl?: string;
28
+ }
29
+ interface CheckoutResult {
30
+ chargeRef: string;
31
+ status: 'approved' | 'declined' | 'pending' | 'unknown';
32
+ error?: string;
33
+ }
34
+ /** @deprecated Use CheckoutOptions. */
35
+ interface BillingModalOptions {
36
+ chargeRef?: string;
37
+ chargeId?: string;
38
+ confirmationUrl?: string;
39
+ }
40
+ interface AppBridgeEnvelope<T = unknown> {
41
+ channel: typeof ARCH_APP_BRIDGE_CHANNEL;
42
+ version: typeof ARCH_APP_BRIDGE_VERSION;
43
+ bridgeSession: string;
44
+ type: string;
45
+ requestId?: string;
46
+ payload?: T;
47
+ }
48
+ declare function isAppBridgeEnvelope(value: unknown): value is AppBridgeEnvelope;
49
+ declare class AppBridge {
50
+ private readonly timeoutMs;
51
+ private readonly bootstrap;
52
+ private readonly pending;
53
+ private connection;
54
+ private connectionPromise;
55
+ private resolveConnection;
56
+ private rejectConnection;
57
+ private connectionTimeout;
58
+ private connectionRetry;
59
+ private cachedToken;
60
+ private cachedPreferences;
61
+ private listening;
62
+ readonly checkout: {
63
+ open: (options: CheckoutOptions) => Promise<CheckoutResult>;
64
+ };
65
+ constructor(options?: AppBridgeOptions);
66
+ isEmbedded(): boolean;
67
+ connect(): Promise<BridgeConnection>;
68
+ getSessionToken(): Promise<string>;
69
+ getPreferences(): Promise<UserPreferences | null>;
70
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
71
+ openCheckout(options: CheckoutOptions): Promise<CheckoutResult>;
72
+ /** @deprecated Use bridge.checkout.open({ chargeRef }). */
73
+ openBillingModal(options: BillingModalOptions): Promise<CheckoutResult>;
74
+ destroy(): void;
75
+ private startListening;
76
+ private readonly handleMessage;
77
+ private request;
78
+ private post;
79
+ private resetConnection;
80
+ }
81
+ declare function createAppBridge(options?: AppBridgeOptions): AppBridge;
82
+ declare function resetAppBridge(): void;
83
+
84
+ export { ARCH_APP_BRIDGE_CHANNEL, ARCH_APP_BRIDGE_VERSION, AppBridge, type AppBridgeEnvelope, AppBridgeError, type AppBridgeErrorCode, type AppBridgeOptions, type BillingModalOptions, type BridgeConnection, type CheckoutOptions, type CheckoutResult, type UserPreferences, createAppBridge, isAppBridgeEnvelope, resetAppBridge };
@@ -0,0 +1,2 @@
1
+ var d="arch.app-bridge",T=1,g="arch.app-bridge.bootstrap",s=class extends Error{constructor(t,i){super(i);this.code=t;this.name="AppBridgeError";}code};function E(n){if(!n||typeof n!="object")return null;let e=n;if(e.channel!==d||e.version!==1||typeof e.platformOrigin!="string"||typeof e.bridgeSession!="string"||!e.bridgeSession)return null;try{let t=new URL(e.platformOrigin);return ["http:","https:"].includes(t.protocol)?{platformOrigin:t.origin,bridgeSession:e.bridgeSession}:null}catch{return null}}function p(n){try{window.sessionStorage.setItem(g,JSON.stringify(n));}catch{}}function A(n){if(typeof window>"u")return null;let e=n.platformOrigin||n.targetOrigin;if(e&&e!=="*"&&n.bridgeSession){let r={platformOrigin:new URL(e).origin,bridgeSession:n.bridgeSession};return p(r),r}try{let r=E(JSON.parse(window.name));if(r)return p(r),r}catch{}let t=new URL(window.location.href),i=e||t.searchParams.get("platform_origin"),o=n.bridgeSession||t.searchParams.get("bridge_session");if(i&&i!=="*"&&o){let r={platformOrigin:new URL(i).origin,bridgeSession:o};return p(r),r}try{let r=window.sessionStorage.getItem(g);if(!r)return null;let c=JSON.parse(r);return !c.platformOrigin||!c.bridgeSession?null:{platformOrigin:new URL(c.platformOrigin).origin,bridgeSession:c.bridgeSession}}catch{return null}}function R(n){if(!n||typeof n!="object")return false;let e=n;return e.channel===d&&e.version===1&&typeof e.bridgeSession=="string"&&typeof e.type=="string"}var l=class{timeoutMs;bootstrap;pending=new Map;connection=null;connectionPromise=null;resolveConnection=null;rejectConnection=null;connectionTimeout=null;connectionRetry=null;cachedToken=null;cachedPreferences=null;listening=false;checkout={open:e=>this.openCheckout(e)};constructor(e={}){this.timeoutMs=e.timeoutMs??3e3,this.bootstrap=A(e);}isEmbedded(){if(typeof window>"u")return false;try{return window.self!==window.top}catch{return true}}connect(){return this.connection?Promise.resolve(this.connection):this.connectionPromise?this.connectionPromise:this.isEmbedded()?this.bootstrap?(this.startListening(),this.connectionPromise=new Promise((e,t)=>{this.resolveConnection=e,this.rejectConnection=t,this.connectionTimeout=setTimeout(()=>{let i=new s("BRIDGE_UNAVAILABLE",`Arch App Bridge did not connect within ${this.timeoutMs}ms.`);this.rejectConnection?.(i),this.resetConnection();},this.timeoutMs),this.post("APP_READY"),this.connectionRetry=setInterval(()=>{this.connection||this.post("APP_READY");},250);}),this.connectionPromise):Promise.reject(new s("BOOTSTRAP_MISSING","Arch App Bridge bootstrap is missing.")):Promise.reject(new s("NOT_EMBEDDED","The app is not embedded in the Arch platform."))}async getSessionToken(){if(this.cachedToken&&Date.now()<this.cachedToken.expiresAt-1e4)return this.cachedToken.token;await this.connect();let e=await this.request("GET_SESSION_TOKEN","SESSION_TOKEN_RESPONSE",void 0,"AUTH_TIMEOUT");if(!e.token)throw new s("REQUEST_FAILED",e.error||"The platform did not issue a token.");return this.cachedToken={token:e.token,expiresAt:e.expiresAt||Date.now()+6e4},e.preferences&&(this.cachedPreferences=e.preferences),e.token}async getPreferences(){return this.cachedPreferences||await this.getSessionToken(),this.cachedPreferences}async fetch(e,t={}){let i=await this.getSessionToken(),o=new Headers(t.headers||{});return o.set("Authorization",`Bearer ${i}`),o.set("x-arch-session-token",i),this.bootstrap&&o.set("x-arch-platform-origin",this.bootstrap.platformOrigin),fetch(e,{...t,headers:o})}async openCheckout(e){if(!e.chargeRef)throw new s("REQUEST_FAILED","chargeRef is required.");if(!this.isEmbedded()){if(!e.confirmationUrl)throw new s("BRIDGE_UNAVAILABLE","Standalone checkout requires a confirmationUrl.");return window.open(e.confirmationUrl,"arch_checkout","width=500,height=700,scrollbars=yes,resizable=yes"),{chargeRef:e.chargeRef,status:"pending"}}return await this.connect(),this.request("OPEN_CHECKOUT","CHECKOUT_RESULT",{chargeRef:e.chargeRef},"CHECKOUT_TIMEOUT",Math.max(this.timeoutMs,5*6e4))}openBillingModal(e){let t=e.chargeRef||e.chargeId;return t?this.openCheckout({chargeRef:t,confirmationUrl:e.confirmationUrl}):Promise.reject(new s("REQUEST_FAILED","chargeRef is required."))}destroy(){this.listening&&typeof window<"u"&&window.removeEventListener("message",this.handleMessage),this.listening=false;for(let e of this.pending.values())clearTimeout(e.timeout),e.reject(new s("BRIDGE_UNAVAILABLE","App Bridge was destroyed."));this.pending.clear(),this.resetConnection();}startListening(){this.listening||(window.addEventListener("message",this.handleMessage),this.listening=true);}handleMessage=e=>{if(!this.bootstrap||e.origin!==this.bootstrap.platformOrigin||e.source!==window.parent||!R(e.data)||e.data.bridgeSession!==this.bootstrap.bridgeSession)return;if(e.data.type==="HOST_READY"){this.post("APP_READY");return}if(e.data.type==="CONNECTED"){let i=e.data.payload||{};this.connection={capabilities:i.capabilities||[]},this.connectionTimeout&&clearTimeout(this.connectionTimeout),this.connectionRetry&&clearInterval(this.connectionRetry),this.connectionRetry=null,this.resolveConnection?.(this.connection),this.resolveConnection=null,this.rejectConnection=null;return}if(!e.data.requestId)return;let t=this.pending.get(e.data.requestId);!t||t.expectedType!==e.data.type||(clearTimeout(t.timeout),this.pending.delete(e.data.requestId),t.resolve(e.data.payload));};request(e,t,i,o="REQUEST_FAILED",r=this.timeoutMs){let c=crypto.randomUUID();return new Promise((h,u)=>{let f=setTimeout(()=>{this.pending.delete(c),u(new s(o,`${e} timed out after ${r}ms.`));},r);this.pending.set(c,{expectedType:t,resolve:m=>h(m),reject:u,timeout:f}),this.post(e,c,i);})}post(e,t,i){if(!this.bootstrap)throw new s("BRIDGE_UNAVAILABLE","App Bridge bootstrap is missing.");let o={channel:d,version:1,bridgeSession:this.bootstrap.bridgeSession,type:e};t&&(o.requestId=t),i!==void 0&&(o.payload=i),window.parent.postMessage(o,this.bootstrap.platformOrigin);}resetConnection(){this.connectionTimeout&&clearTimeout(this.connectionTimeout),this.connectionRetry&&clearInterval(this.connectionRetry),this.connectionTimeout=null,this.connectionRetry=null,this.connectionPromise=null,this.resolveConnection=null,this.rejectConnection=null,this.connection=null;}},a=null;function B(n){return a||(a=new l(n)),a}function w(){a?.destroy(),a=null;}export{d as ARCH_APP_BRIDGE_CHANNEL,T as ARCH_APP_BRIDGE_VERSION,l as AppBridge,s as AppBridgeError,B as createAppBridge,R as isAppBridgeEnvelope,w as resetAppBridge};//# sourceMappingURL=app-bridge.js.map
2
+ //# sourceMappingURL=app-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/app-bridge.ts"],"names":["ARCH_APP_BRIDGE_CHANNEL","ARCH_APP_BRIDGE_VERSION","BRIDGE_BOOTSTRAP_STORAGE_KEY","AppBridgeError","code","message","parseNamedBootstrap","value","bootstrap","origin","persistBootstrap","readBootstrap","options","explicitOrigin","url","platformOrigin","bridgeSession","stored","parsed","isAppBridgeEnvelope","AppBridge","resolve","reject","error","payload","input","init","token","headers","chargeRef","pending","event","type","expectedType","timeoutCode","timeoutMs","requestId","timeout","envelope","singleton","createAppBridge","resetAppBridge"],"mappings":"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","file":"app-bridge.js","sourcesContent":["export const ARCH_APP_BRIDGE_CHANNEL = 'arch.app-bridge';\nexport const ARCH_APP_BRIDGE_VERSION = 1;\n\nconst BRIDGE_BOOTSTRAP_STORAGE_KEY = 'arch.app-bridge.bootstrap';\n\nexport type AppBridgeErrorCode =\n | 'NOT_EMBEDDED'\n | 'BOOTSTRAP_MISSING'\n | 'BOOTSTRAP_INVALID'\n | 'BRIDGE_UNAVAILABLE'\n | 'ORIGIN_MISMATCH'\n | 'AUTH_TIMEOUT'\n | 'CHECKOUT_TIMEOUT'\n | 'PROTOCOL_MISMATCH'\n | 'REQUEST_FAILED';\n\nexport class AppBridgeError extends Error {\n constructor(\n public readonly code: AppBridgeErrorCode,\n message: string\n ) {\n super(message);\n this.name = 'AppBridgeError';\n }\n}\n\nexport interface UserPreferences {\n mode: 'dark' | 'light';\n theme: string;\n radius: string;\n [key: string]: unknown;\n}\n\nexport interface AppBridgeOptions {\n platformOrigin?: string;\n bridgeSession?: string;\n /** @deprecated Use platformOrigin. */\n targetOrigin?: string;\n timeoutMs?: number;\n}\n\nexport interface BridgeConnection {\n capabilities: string[];\n}\n\nexport interface CheckoutOptions {\n chargeRef: string;\n /** Used only for the standalone popup fallback. */\n confirmationUrl?: string;\n}\n\nexport interface CheckoutResult {\n chargeRef: string;\n status: 'approved' | 'declined' | 'pending' | 'unknown';\n error?: string;\n}\n\n/** @deprecated Use CheckoutOptions. */\nexport interface BillingModalOptions {\n chargeRef?: string;\n chargeId?: string;\n confirmationUrl?: string;\n}\n\nexport interface AppBridgeEnvelope<T = unknown> {\n channel: typeof ARCH_APP_BRIDGE_CHANNEL;\n version: typeof ARCH_APP_BRIDGE_VERSION;\n bridgeSession: string;\n type: string;\n requestId?: string;\n payload?: T;\n}\n\ninterface PendingRequest {\n expectedType: string;\n resolve: (payload: unknown) => void;\n reject: (error: AppBridgeError) => void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\ninterface BridgeBootstrap {\n platformOrigin: string;\n bridgeSession: string;\n}\n\nfunction parseNamedBootstrap(value: unknown): BridgeBootstrap | null {\n if (!value || typeof value !== 'object') return null;\n const bootstrap = value as Partial<BridgeBootstrap & { channel: string; version: number }>;\n if (\n bootstrap.channel !== ARCH_APP_BRIDGE_CHANNEL ||\n bootstrap.version !== ARCH_APP_BRIDGE_VERSION ||\n typeof bootstrap.platformOrigin !== 'string' ||\n typeof bootstrap.bridgeSession !== 'string' ||\n !bootstrap.bridgeSession\n ) {\n return null;\n }\n try {\n const origin = new URL(bootstrap.platformOrigin);\n if (!['http:', 'https:'].includes(origin.protocol)) return null;\n return { platformOrigin: origin.origin, bridgeSession: bootstrap.bridgeSession };\n } catch {\n return null;\n }\n}\n\nfunction persistBootstrap(bootstrap: BridgeBootstrap) {\n try {\n window.sessionStorage.setItem(BRIDGE_BOOTSTRAP_STORAGE_KEY, JSON.stringify(bootstrap));\n } catch {\n // Storage can be disabled; window.name still survives iframe navigation.\n }\n}\n\nfunction readBootstrap(options: AppBridgeOptions): BridgeBootstrap | null {\n if (typeof window === 'undefined') return null;\n\n const explicitOrigin = options.platformOrigin || options.targetOrigin;\n if (explicitOrigin && explicitOrigin !== '*' && options.bridgeSession) {\n const bootstrap = {\n platformOrigin: new URL(explicitOrigin).origin,\n bridgeSession: options.bridgeSession\n };\n persistBootstrap(bootstrap);\n return bootstrap;\n }\n\n try {\n const bootstrap = parseNamedBootstrap(JSON.parse(window.name));\n if (bootstrap) {\n persistBootstrap(bootstrap);\n return bootstrap;\n }\n } catch {\n // window.name may be empty or owned by unrelated embedding software.\n }\n\n const url = new URL(window.location.href);\n const platformOrigin = explicitOrigin || url.searchParams.get('platform_origin');\n const bridgeSession = options.bridgeSession || url.searchParams.get('bridge_session');\n\n if (platformOrigin && platformOrigin !== '*' && bridgeSession) {\n const bootstrap = {\n platformOrigin: new URL(platformOrigin).origin,\n bridgeSession\n };\n persistBootstrap(bootstrap);\n return bootstrap;\n }\n\n try {\n const stored = window.sessionStorage.getItem(BRIDGE_BOOTSTRAP_STORAGE_KEY);\n if (!stored) return null;\n const parsed = JSON.parse(stored) as Partial<BridgeBootstrap>;\n if (!parsed.platformOrigin || !parsed.bridgeSession) return null;\n return {\n platformOrigin: new URL(parsed.platformOrigin).origin,\n bridgeSession: parsed.bridgeSession\n };\n } catch {\n return null;\n }\n}\n\nexport function isAppBridgeEnvelope(value: unknown): value is AppBridgeEnvelope {\n if (!value || typeof value !== 'object') return false;\n const message = value as Partial<AppBridgeEnvelope>;\n return (\n message.channel === ARCH_APP_BRIDGE_CHANNEL &&\n message.version === ARCH_APP_BRIDGE_VERSION &&\n typeof message.bridgeSession === 'string' &&\n typeof message.type === 'string'\n );\n}\n\nexport class AppBridge {\n private readonly timeoutMs: number;\n private readonly bootstrap: BridgeBootstrap | null;\n private readonly pending = new Map<string, PendingRequest>();\n private connection: BridgeConnection | null = null;\n private connectionPromise: Promise<BridgeConnection> | null = null;\n private resolveConnection: ((connection: BridgeConnection) => void) | null = null;\n private rejectConnection: ((error: AppBridgeError) => void) | null = null;\n private connectionTimeout: ReturnType<typeof setTimeout> | null = null;\n private connectionRetry: ReturnType<typeof setInterval> | null = null;\n private cachedToken: { token: string; expiresAt: number } | null = null;\n private cachedPreferences: UserPreferences | null = null;\n private listening = false;\n\n public readonly checkout = {\n open: (options: CheckoutOptions) => this.openCheckout(options)\n };\n\n constructor(options: AppBridgeOptions = {}) {\n this.timeoutMs = options.timeoutMs ?? 3000;\n this.bootstrap = readBootstrap(options);\n }\n\n public isEmbedded(): boolean {\n if (typeof window === 'undefined') return false;\n try {\n return window.self !== window.top;\n } catch {\n return true;\n }\n }\n\n public connect(): Promise<BridgeConnection> {\n if (this.connection) return Promise.resolve(this.connection);\n if (this.connectionPromise) return this.connectionPromise;\n if (!this.isEmbedded()) {\n return Promise.reject(\n new AppBridgeError('NOT_EMBEDDED', 'The app is not embedded in the Arch platform.')\n );\n }\n if (!this.bootstrap) {\n return Promise.reject(\n new AppBridgeError('BOOTSTRAP_MISSING', 'Arch App Bridge bootstrap is missing.')\n );\n }\n\n this.startListening();\n this.connectionPromise = new Promise<BridgeConnection>((resolve, reject) => {\n this.resolveConnection = resolve;\n this.rejectConnection = reject;\n this.connectionTimeout = setTimeout(() => {\n const error = new AppBridgeError(\n 'BRIDGE_UNAVAILABLE',\n `Arch App Bridge did not connect within ${this.timeoutMs}ms.`\n );\n this.rejectConnection?.(error);\n this.resetConnection();\n }, this.timeoutMs);\n this.post('APP_READY');\n this.connectionRetry = setInterval(() => {\n if (!this.connection) this.post('APP_READY');\n }, 250);\n });\n return this.connectionPromise;\n }\n\n public async getSessionToken(): Promise<string> {\n if (this.cachedToken && Date.now() < this.cachedToken.expiresAt - 10_000) {\n return this.cachedToken.token;\n }\n\n await this.connect();\n const payload = await this.request<{\n token?: string;\n expiresAt?: number;\n preferences?: UserPreferences;\n error?: string;\n }>('GET_SESSION_TOKEN', 'SESSION_TOKEN_RESPONSE', undefined, 'AUTH_TIMEOUT');\n\n if (!payload.token) {\n throw new AppBridgeError(\n 'REQUEST_FAILED',\n payload.error || 'The platform did not issue a token.'\n );\n }\n\n this.cachedToken = {\n token: payload.token,\n expiresAt: payload.expiresAt || Date.now() + 60_000\n };\n if (payload.preferences) this.cachedPreferences = payload.preferences;\n return payload.token;\n }\n\n public async getPreferences(): Promise<UserPreferences | null> {\n if (!this.cachedPreferences) await this.getSessionToken();\n return this.cachedPreferences;\n }\n\n public async fetch(input: RequestInfo | URL, init: RequestInit = {}): Promise<Response> {\n const token = await this.getSessionToken();\n const headers = new Headers(init.headers || {});\n headers.set('Authorization', `Bearer ${token}`);\n headers.set('x-arch-session-token', token);\n if (this.bootstrap) {\n headers.set('x-arch-platform-origin', this.bootstrap.platformOrigin);\n }\n return fetch(input, { ...init, headers });\n }\n\n public async openCheckout(options: CheckoutOptions): Promise<CheckoutResult> {\n if (!options.chargeRef) {\n throw new AppBridgeError('REQUEST_FAILED', 'chargeRef is required.');\n }\n\n if (!this.isEmbedded()) {\n if (!options.confirmationUrl) {\n throw new AppBridgeError(\n 'BRIDGE_UNAVAILABLE',\n 'Standalone checkout requires a confirmationUrl.'\n );\n }\n window.open(\n options.confirmationUrl,\n 'arch_checkout',\n 'width=500,height=700,scrollbars=yes,resizable=yes'\n );\n return { chargeRef: options.chargeRef, status: 'pending' };\n }\n\n await this.connect();\n return this.request<CheckoutResult>(\n 'OPEN_CHECKOUT',\n 'CHECKOUT_RESULT',\n { chargeRef: options.chargeRef },\n 'CHECKOUT_TIMEOUT',\n Math.max(this.timeoutMs, 5 * 60_000)\n );\n }\n\n /** @deprecated Use bridge.checkout.open({ chargeRef }). */\n public openBillingModal(options: BillingModalOptions): Promise<CheckoutResult> {\n const chargeRef = options.chargeRef || options.chargeId;\n if (!chargeRef) {\n return Promise.reject(new AppBridgeError('REQUEST_FAILED', 'chargeRef is required.'));\n }\n return this.openCheckout({\n chargeRef,\n confirmationUrl: options.confirmationUrl\n });\n }\n\n public destroy(): void {\n if (this.listening && typeof window !== 'undefined') {\n window.removeEventListener('message', this.handleMessage);\n }\n this.listening = false;\n for (const pending of this.pending.values()) {\n clearTimeout(pending.timeout);\n pending.reject(new AppBridgeError('BRIDGE_UNAVAILABLE', 'App Bridge was destroyed.'));\n }\n this.pending.clear();\n this.resetConnection();\n }\n\n private startListening() {\n if (this.listening) return;\n window.addEventListener('message', this.handleMessage);\n this.listening = true;\n }\n\n private readonly handleMessage = (event: MessageEvent) => {\n if (!this.bootstrap || event.origin !== this.bootstrap.platformOrigin) return;\n if (event.source !== window.parent) return;\n if (!isAppBridgeEnvelope(event.data)) return;\n if (event.data.bridgeSession !== this.bootstrap.bridgeSession) return;\n\n if (event.data.type === 'HOST_READY') {\n this.post('APP_READY');\n return;\n }\n\n if (event.data.type === 'CONNECTED') {\n const payload = (event.data.payload || {}) as Partial<BridgeConnection>;\n this.connection = { capabilities: payload.capabilities || [] };\n if (this.connectionTimeout) clearTimeout(this.connectionTimeout);\n if (this.connectionRetry) clearInterval(this.connectionRetry);\n this.connectionRetry = null;\n this.resolveConnection?.(this.connection);\n this.resolveConnection = null;\n this.rejectConnection = null;\n return;\n }\n\n if (!event.data.requestId) return;\n const pending = this.pending.get(event.data.requestId);\n if (!pending || pending.expectedType !== event.data.type) return;\n clearTimeout(pending.timeout);\n this.pending.delete(event.data.requestId);\n pending.resolve(event.data.payload);\n };\n\n private request<T>(\n type: string,\n expectedType: string,\n payload?: unknown,\n timeoutCode: AppBridgeErrorCode = 'REQUEST_FAILED',\n timeoutMs = this.timeoutMs\n ): Promise<T> {\n const requestId = crypto.randomUUID();\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pending.delete(requestId);\n reject(new AppBridgeError(timeoutCode, `${type} timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n this.pending.set(requestId, {\n expectedType,\n resolve: (value) => resolve(value as T),\n reject,\n timeout\n });\n this.post(type, requestId, payload);\n });\n }\n\n private post(type: string, requestId?: string, payload?: unknown) {\n if (!this.bootstrap) {\n throw new AppBridgeError('BRIDGE_UNAVAILABLE', 'App Bridge bootstrap is missing.');\n }\n const envelope: AppBridgeEnvelope = {\n channel: ARCH_APP_BRIDGE_CHANNEL,\n version: ARCH_APP_BRIDGE_VERSION,\n bridgeSession: this.bootstrap.bridgeSession,\n type\n };\n if (requestId) envelope.requestId = requestId;\n if (payload !== undefined) envelope.payload = payload;\n window.parent.postMessage(envelope, this.bootstrap.platformOrigin);\n }\n\n private resetConnection() {\n if (this.connectionTimeout) clearTimeout(this.connectionTimeout);\n if (this.connectionRetry) clearInterval(this.connectionRetry);\n this.connectionTimeout = null;\n this.connectionRetry = null;\n this.connectionPromise = null;\n this.resolveConnection = null;\n this.rejectConnection = null;\n this.connection = null;\n }\n}\n\nlet singleton: AppBridge | null = null;\n\nexport function createAppBridge(options?: AppBridgeOptions): AppBridge {\n if (!singleton) singleton = new AppBridge(options);\n return singleton;\n}\n\nexport function resetAppBridge(): void {\n singleton?.destroy();\n singleton = null;\n}\n"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var p=require('crypto'),jose=require('jose');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var p__default=/*#__PURE__*/_interopDefault(p);function d(o){let e=o||(typeof process<"u"?process.env?.ARCH_PLATFORM_URL||process.env?.PLATFORM_URL:void 0);return e?e.replace(/\/+$/,""):void 0}var i=class{apiKey;appSlug;baseUrl;constructor(e){if(!e.apiKey)throw new Error("PlatformSDK: apiKey is required");if(!e.appSlug)throw new Error("PlatformSDK: appSlug is required");this.apiKey=e.apiKey,this.appSlug=e.appSlug;let r=d(e.baseUrl);if(!r)throw new Error("PlatformSDK: baseUrl is required. Pass baseUrl or set ARCH_PLATFORM_URL (or PLATFORM_URL).");this.baseUrl=r;}async request(e,r={}){let s=`${this.baseUrl}${e}`,t=new Headers(r.headers||{});t.set("Authorization",`Bearer ${this.apiKey}`),r.body&&!t.has("Content-Type")&&t.set("Content-Type","application/json");try{let n=await fetch(s,{...r,headers:t}),a=await n.json().catch(()=>({}));return n.ok?a:{error:a.error||`HTTP ${n.status}: ${n.statusText}`}}catch(n){return {error:n instanceof Error?n.message:"Unknown network error"}}}async verifyToken(e){if(!e)return {valid:false,error:"Token is required"};let r=`/api/v1/apps/${this.appSlug}/verify?token=${encodeURIComponent(e)}`;return this.request(r,{method:"GET"})}async verifySessionToken(e,r){if(!e)return {valid:false,error:"Token is required"};try{let s=jose.createRemoteJWKSet(new URL(`${this.baseUrl}/api/v1/app-bridge/jwks`)),{payload:t}=await jose.jwtVerify(e,s,{issuer:new URL(r||this.baseUrl).origin,audience:this.appSlug,algorithms:["EdDSA"]});return {valid:!0,installationId:t.sub,preferences:t.preferences}}catch(s){return {valid:false,error:s instanceof Error?s.message:"Failed to verify session token"}}}async createCharge(e){if(!e.type||!e.installationId||!e.returnUrl)return {error:"Missing required charge configuration fields"};let r=`/api/v1/apps/${this.appSlug}/charges`;return this.request(r,{method:"POST",body:JSON.stringify(e)})}async getUserAccess(e){if(!e)return {error:"installationId is required"};let r=`/api/v1/apps/${this.appSlug}/installations/${encodeURIComponent(e)}/access`;return this.request(r,{method:"GET"})}async getCharge(e){if(!e)return {error:"chargeId is required"};let r=`/api/v1/apps/${this.appSlug}/charges/${encodeURIComponent(e)}`;return this.request(r,{method:"GET"})}async sendNotification(e){if(!e.installationId||!e.title||!e.message)return {success:false,error:"Missing required notification fields"};let r=`/api/v1/apps/${this.appSlug}/notifications`;return this.request(r,{method:"POST",body:JSON.stringify(e)})}static stylesToCss(e){return Object.entries(e).map(([r,s])=>`${r}: ${s};`).join(" ")}static verifyWebhookSignature(e,r,s){return p__default.default.createHmac("sha256",s).update(e).digest("hex")===r}},f=i;exports.ArchAppClient=f;exports.PlatformSDK=i;exports.resolvePlatformBaseUrl=d;//# sourceMappingURL=index.cjs.map
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["resolvePlatformBaseUrl","baseUrl","candidate","PlatformSDK","config","endpoint","options","url","headers","res","data","err","token","expectedIssuer","jwks","createRemoteJWKSet","payload","jwtVerify","chargeDetails","installationId","chargeId","params","styles","key","value","body","signature","secret","crypto","ArchAppClient"],"mappings":"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","file":"index.cjs","sourcesContent":["import crypto from 'node:crypto';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\n\nexport interface PlatformSDKConfig {\n /** The app's live key. Required when the SDK is constructed; optional here so a\n * standalone app can pass whatever its environment provides. */\n apiKey?: string;\n appSlug?: string;\n baseUrl?: string;\n}\n\nexport interface VerifyTokenResponse {\n valid: boolean;\n installationId?: string;\n installedAt?: string;\n lastAccessedAt?: string;\n preferences?: {\n mode: 'light' | 'dark';\n theme: string;\n radius: string;\n styles: Record<string, string>;\n };\n error?: string;\n}\n\nexport interface ChargeConfig {\n type: 'plan' | 'one_time';\n installationId: string;\n returnUrl: string;\n planId?: string;\n productId?: string;\n description?: string;\n}\n\nexport interface CreateChargeResponse {\n confirmationUrl?: string;\n chargeRef?: string;\n chargeId?: string;\n status?: 'pending' | 'scheduled';\n message?: string;\n error?: string;\n}\n\nexport interface ChargeStatusResponse {\n id?: string;\n chargeRef?: string;\n type?: 'plan' | 'one_time' | 'usage_based';\n status?: 'pending' | 'approved' | 'declined' | 'expired';\n amount?: string;\n installationId?: string;\n productId?: string;\n planId?: string;\n error?: string;\n}\n\nexport interface UserAccessResponse {\n subscriptions?: Array<{\n planId: string;\n planIdentifier?: string;\n status: string;\n currentPeriodEnd: string | null;\n scheduledPlanId: string | null;\n }>;\n purchases?: Array<{\n productId: string;\n productIdentifier?: string;\n quantity: number;\n }>;\n error?: string;\n}\n\nexport function resolvePlatformBaseUrl(baseUrl?: string): string | undefined {\n const candidate =\n baseUrl ||\n (typeof process !== 'undefined'\n ? process.env?.ARCH_PLATFORM_URL || process.env?.PLATFORM_URL\n : undefined);\n return candidate ? candidate.replace(/\\/+$/, '') : undefined;\n}\n\nexport class PlatformSDK {\n private apiKey: string;\n private appSlug: string;\n private baseUrl: string;\n\n constructor(config: PlatformSDKConfig) {\n if (!config.apiKey) throw new Error('PlatformSDK: apiKey is required');\n if (!config.appSlug) throw new Error('PlatformSDK: appSlug is required');\n\n this.apiKey = config.apiKey;\n this.appSlug = config.appSlug;\n const baseUrl = resolvePlatformBaseUrl(config.baseUrl);\n if (!baseUrl) {\n throw new Error(\n 'PlatformSDK: baseUrl is required. Pass baseUrl or set ARCH_PLATFORM_URL (or PLATFORM_URL).'\n );\n }\n this.baseUrl = baseUrl;\n }\n\n private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = new Headers(options.headers || {});\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n\n if (options.body && !headers.has('Content-Type')) {\n headers.set('Content-Type', 'application/json');\n }\n\n try {\n const res = await fetch(url, { ...options, headers });\n const data = await res.json().catch(() => ({}));\n\n if (!res.ok) {\n return { error: data.error || `HTTP ${res.status}: ${res.statusText}` } as T;\n }\n return data as T;\n } catch (err) {\n return { error: err instanceof Error ? err.message : 'Unknown network error' } as T;\n }\n }\n\n async verifyToken(token: string): Promise<VerifyTokenResponse> {\n if (!token) return { valid: false, error: 'Token is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/verify?token=${encodeURIComponent(token)}`;\n return this.request<VerifyTokenResponse>(endpoint, { method: 'GET' });\n }\n\n async verifySessionToken(\n token: string,\n expectedIssuer?: string\n ): Promise<{\n valid: boolean;\n appId?: string;\n installationId?: string;\n userId?: string;\n userRole?: string;\n preferences?: VerifyTokenResponse['preferences'];\n error?: string;\n }> {\n if (!token) return { valid: false, error: 'Token is required' };\n\n try {\n const jwks = createRemoteJWKSet(new URL(`${this.baseUrl}/api/v1/app-bridge/jwks`));\n const { payload } = await jwtVerify(token, jwks, {\n issuer: new URL(expectedIssuer || this.baseUrl).origin,\n audience: this.appSlug,\n algorithms: ['EdDSA']\n });\n\n return {\n valid: true,\n installationId: payload.sub,\n preferences: payload.preferences as VerifyTokenResponse['preferences']\n };\n } catch (err: unknown) {\n return {\n valid: false,\n error: err instanceof Error ? err.message : 'Failed to verify session token'\n };\n }\n }\n\n async createCharge(chargeDetails: ChargeConfig): Promise<CreateChargeResponse> {\n if (!chargeDetails.type || !chargeDetails.installationId || !chargeDetails.returnUrl) {\n return { error: 'Missing required charge configuration fields' };\n }\n\n const endpoint = `/api/v1/apps/${this.appSlug}/charges`;\n return this.request<CreateChargeResponse>(endpoint, {\n method: 'POST',\n body: JSON.stringify(chargeDetails)\n });\n }\n\n async getUserAccess(installationId: string): Promise<UserAccessResponse> {\n if (!installationId) return { error: 'installationId is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/installations/${encodeURIComponent(installationId)}/access`;\n return this.request<UserAccessResponse>(endpoint, { method: 'GET' });\n }\n\n async getCharge(chargeId: string): Promise<ChargeStatusResponse> {\n if (!chargeId) return { error: 'chargeId is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/charges/${encodeURIComponent(chargeId)}`;\n return this.request<ChargeStatusResponse>(endpoint, { method: 'GET' });\n }\n\n async sendNotification(params: {\n installationId: string;\n title: string;\n message: string;\n link?: string;\n imageUrl?: string;\n forwardToEmail?: boolean;\n }): Promise<{ success: boolean; error?: string; notificationId?: string }> {\n if (!params.installationId || !params.title || !params.message) {\n return { success: false, error: 'Missing required notification fields' };\n }\n const endpoint = `/api/v1/apps/${this.appSlug}/notifications`;\n return this.request<{ success: boolean; error?: string; notificationId?: string }>(endpoint, {\n method: 'POST',\n body: JSON.stringify(params)\n });\n }\n\n /**\n * Helper to convert platform styles object to CSS variable string\n */\n static stylesToCss(styles: Record<string, string>): string {\n return Object.entries(styles)\n .map(([key, value]) => `${key}: ${value};`)\n .join(' ');\n }\n\n /**\n * Helper to verify Arch webhook signatures\n */\n static verifyWebhookSignature(body: string, signature: string, secret: string): boolean {\n const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');\n return expected === signature;\n }\n}\n\nexport type ArchAppClient = PlatformSDK;\nexport const ArchAppClient = PlatformSDK;\nexport type ArchAppClientConfig = PlatformSDKConfig;\n"]}
@@ -0,0 +1,108 @@
1
+ interface PlatformSDKConfig {
2
+ /** The app's live key. Required when the SDK is constructed; optional here so a
3
+ * standalone app can pass whatever its environment provides. */
4
+ apiKey?: string;
5
+ appSlug?: string;
6
+ baseUrl?: string;
7
+ }
8
+ interface VerifyTokenResponse {
9
+ valid: boolean;
10
+ installationId?: string;
11
+ installedAt?: string;
12
+ lastAccessedAt?: string;
13
+ preferences?: {
14
+ mode: 'light' | 'dark';
15
+ theme: string;
16
+ radius: string;
17
+ styles: Record<string, string>;
18
+ };
19
+ error?: string;
20
+ }
21
+ interface ChargeConfig {
22
+ type: 'plan' | 'one_time';
23
+ installationId: string;
24
+ returnUrl: string;
25
+ planId?: string;
26
+ productId?: string;
27
+ description?: string;
28
+ }
29
+ interface CreateChargeResponse {
30
+ confirmationUrl?: string;
31
+ chargeRef?: string;
32
+ chargeId?: string;
33
+ status?: 'pending' | 'scheduled';
34
+ message?: string;
35
+ error?: string;
36
+ }
37
+ interface ChargeStatusResponse {
38
+ id?: string;
39
+ chargeRef?: string;
40
+ type?: 'plan' | 'one_time' | 'usage_based';
41
+ status?: 'pending' | 'approved' | 'declined' | 'expired';
42
+ amount?: string;
43
+ installationId?: string;
44
+ productId?: string;
45
+ planId?: string;
46
+ error?: string;
47
+ }
48
+ interface UserAccessResponse {
49
+ subscriptions?: Array<{
50
+ planId: string;
51
+ planIdentifier?: string;
52
+ status: string;
53
+ currentPeriodEnd: string | null;
54
+ scheduledPlanId: string | null;
55
+ }>;
56
+ purchases?: Array<{
57
+ productId: string;
58
+ productIdentifier?: string;
59
+ quantity: number;
60
+ }>;
61
+ error?: string;
62
+ }
63
+ declare function resolvePlatformBaseUrl(baseUrl?: string): string | undefined;
64
+ declare class PlatformSDK {
65
+ private apiKey;
66
+ private appSlug;
67
+ private baseUrl;
68
+ constructor(config: PlatformSDKConfig);
69
+ private request;
70
+ verifyToken(token: string): Promise<VerifyTokenResponse>;
71
+ verifySessionToken(token: string, expectedIssuer?: string): Promise<{
72
+ valid: boolean;
73
+ appId?: string;
74
+ installationId?: string;
75
+ userId?: string;
76
+ userRole?: string;
77
+ preferences?: VerifyTokenResponse['preferences'];
78
+ error?: string;
79
+ }>;
80
+ createCharge(chargeDetails: ChargeConfig): Promise<CreateChargeResponse>;
81
+ getUserAccess(installationId: string): Promise<UserAccessResponse>;
82
+ getCharge(chargeId: string): Promise<ChargeStatusResponse>;
83
+ sendNotification(params: {
84
+ installationId: string;
85
+ title: string;
86
+ message: string;
87
+ link?: string;
88
+ imageUrl?: string;
89
+ forwardToEmail?: boolean;
90
+ }): Promise<{
91
+ success: boolean;
92
+ error?: string;
93
+ notificationId?: string;
94
+ }>;
95
+ /**
96
+ * Helper to convert platform styles object to CSS variable string
97
+ */
98
+ static stylesToCss(styles: Record<string, string>): string;
99
+ /**
100
+ * Helper to verify Arch webhook signatures
101
+ */
102
+ static verifyWebhookSignature(body: string, signature: string, secret: string): boolean;
103
+ }
104
+ type ArchAppClient = PlatformSDK;
105
+ declare const ArchAppClient: typeof PlatformSDK;
106
+ type ArchAppClientConfig = PlatformSDKConfig;
107
+
108
+ export { ArchAppClient, type ArchAppClientConfig, type ChargeConfig, type ChargeStatusResponse, type CreateChargeResponse, PlatformSDK, type PlatformSDKConfig, type UserAccessResponse, type VerifyTokenResponse, resolvePlatformBaseUrl };
@@ -0,0 +1,108 @@
1
+ interface PlatformSDKConfig {
2
+ /** The app's live key. Required when the SDK is constructed; optional here so a
3
+ * standalone app can pass whatever its environment provides. */
4
+ apiKey?: string;
5
+ appSlug?: string;
6
+ baseUrl?: string;
7
+ }
8
+ interface VerifyTokenResponse {
9
+ valid: boolean;
10
+ installationId?: string;
11
+ installedAt?: string;
12
+ lastAccessedAt?: string;
13
+ preferences?: {
14
+ mode: 'light' | 'dark';
15
+ theme: string;
16
+ radius: string;
17
+ styles: Record<string, string>;
18
+ };
19
+ error?: string;
20
+ }
21
+ interface ChargeConfig {
22
+ type: 'plan' | 'one_time';
23
+ installationId: string;
24
+ returnUrl: string;
25
+ planId?: string;
26
+ productId?: string;
27
+ description?: string;
28
+ }
29
+ interface CreateChargeResponse {
30
+ confirmationUrl?: string;
31
+ chargeRef?: string;
32
+ chargeId?: string;
33
+ status?: 'pending' | 'scheduled';
34
+ message?: string;
35
+ error?: string;
36
+ }
37
+ interface ChargeStatusResponse {
38
+ id?: string;
39
+ chargeRef?: string;
40
+ type?: 'plan' | 'one_time' | 'usage_based';
41
+ status?: 'pending' | 'approved' | 'declined' | 'expired';
42
+ amount?: string;
43
+ installationId?: string;
44
+ productId?: string;
45
+ planId?: string;
46
+ error?: string;
47
+ }
48
+ interface UserAccessResponse {
49
+ subscriptions?: Array<{
50
+ planId: string;
51
+ planIdentifier?: string;
52
+ status: string;
53
+ currentPeriodEnd: string | null;
54
+ scheduledPlanId: string | null;
55
+ }>;
56
+ purchases?: Array<{
57
+ productId: string;
58
+ productIdentifier?: string;
59
+ quantity: number;
60
+ }>;
61
+ error?: string;
62
+ }
63
+ declare function resolvePlatformBaseUrl(baseUrl?: string): string | undefined;
64
+ declare class PlatformSDK {
65
+ private apiKey;
66
+ private appSlug;
67
+ private baseUrl;
68
+ constructor(config: PlatformSDKConfig);
69
+ private request;
70
+ verifyToken(token: string): Promise<VerifyTokenResponse>;
71
+ verifySessionToken(token: string, expectedIssuer?: string): Promise<{
72
+ valid: boolean;
73
+ appId?: string;
74
+ installationId?: string;
75
+ userId?: string;
76
+ userRole?: string;
77
+ preferences?: VerifyTokenResponse['preferences'];
78
+ error?: string;
79
+ }>;
80
+ createCharge(chargeDetails: ChargeConfig): Promise<CreateChargeResponse>;
81
+ getUserAccess(installationId: string): Promise<UserAccessResponse>;
82
+ getCharge(chargeId: string): Promise<ChargeStatusResponse>;
83
+ sendNotification(params: {
84
+ installationId: string;
85
+ title: string;
86
+ message: string;
87
+ link?: string;
88
+ imageUrl?: string;
89
+ forwardToEmail?: boolean;
90
+ }): Promise<{
91
+ success: boolean;
92
+ error?: string;
93
+ notificationId?: string;
94
+ }>;
95
+ /**
96
+ * Helper to convert platform styles object to CSS variable string
97
+ */
98
+ static stylesToCss(styles: Record<string, string>): string;
99
+ /**
100
+ * Helper to verify Arch webhook signatures
101
+ */
102
+ static verifyWebhookSignature(body: string, signature: string, secret: string): boolean;
103
+ }
104
+ type ArchAppClient = PlatformSDK;
105
+ declare const ArchAppClient: typeof PlatformSDK;
106
+ type ArchAppClientConfig = PlatformSDKConfig;
107
+
108
+ export { ArchAppClient, type ArchAppClientConfig, type ChargeConfig, type ChargeStatusResponse, type CreateChargeResponse, PlatformSDK, type PlatformSDKConfig, type UserAccessResponse, type VerifyTokenResponse, resolvePlatformBaseUrl };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import p from'crypto';import {createRemoteJWKSet,jwtVerify}from'jose';function d(o){let e=o||(typeof process<"u"?process.env?.ARCH_PLATFORM_URL||process.env?.PLATFORM_URL:void 0);return e?e.replace(/\/+$/,""):void 0}var i=class{apiKey;appSlug;baseUrl;constructor(e){if(!e.apiKey)throw new Error("PlatformSDK: apiKey is required");if(!e.appSlug)throw new Error("PlatformSDK: appSlug is required");this.apiKey=e.apiKey,this.appSlug=e.appSlug;let r=d(e.baseUrl);if(!r)throw new Error("PlatformSDK: baseUrl is required. Pass baseUrl or set ARCH_PLATFORM_URL (or PLATFORM_URL).");this.baseUrl=r;}async request(e,r={}){let s=`${this.baseUrl}${e}`,t=new Headers(r.headers||{});t.set("Authorization",`Bearer ${this.apiKey}`),r.body&&!t.has("Content-Type")&&t.set("Content-Type","application/json");try{let n=await fetch(s,{...r,headers:t}),a=await n.json().catch(()=>({}));return n.ok?a:{error:a.error||`HTTP ${n.status}: ${n.statusText}`}}catch(n){return {error:n instanceof Error?n.message:"Unknown network error"}}}async verifyToken(e){if(!e)return {valid:false,error:"Token is required"};let r=`/api/v1/apps/${this.appSlug}/verify?token=${encodeURIComponent(e)}`;return this.request(r,{method:"GET"})}async verifySessionToken(e,r){if(!e)return {valid:false,error:"Token is required"};try{let s=createRemoteJWKSet(new URL(`${this.baseUrl}/api/v1/app-bridge/jwks`)),{payload:t}=await jwtVerify(e,s,{issuer:new URL(r||this.baseUrl).origin,audience:this.appSlug,algorithms:["EdDSA"]});return {valid:!0,installationId:t.sub,preferences:t.preferences}}catch(s){return {valid:false,error:s instanceof Error?s.message:"Failed to verify session token"}}}async createCharge(e){if(!e.type||!e.installationId||!e.returnUrl)return {error:"Missing required charge configuration fields"};let r=`/api/v1/apps/${this.appSlug}/charges`;return this.request(r,{method:"POST",body:JSON.stringify(e)})}async getUserAccess(e){if(!e)return {error:"installationId is required"};let r=`/api/v1/apps/${this.appSlug}/installations/${encodeURIComponent(e)}/access`;return this.request(r,{method:"GET"})}async getCharge(e){if(!e)return {error:"chargeId is required"};let r=`/api/v1/apps/${this.appSlug}/charges/${encodeURIComponent(e)}`;return this.request(r,{method:"GET"})}async sendNotification(e){if(!e.installationId||!e.title||!e.message)return {success:false,error:"Missing required notification fields"};let r=`/api/v1/apps/${this.appSlug}/notifications`;return this.request(r,{method:"POST",body:JSON.stringify(e)})}static stylesToCss(e){return Object.entries(e).map(([r,s])=>`${r}: ${s};`).join(" ")}static verifyWebhookSignature(e,r,s){return p.createHmac("sha256",s).update(e).digest("hex")===r}},f=i;export{f as ArchAppClient,i as PlatformSDK,d as resolvePlatformBaseUrl};//# sourceMappingURL=index.js.map
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["resolvePlatformBaseUrl","baseUrl","candidate","PlatformSDK","config","endpoint","options","url","headers","res","data","err","token","expectedIssuer","jwks","createRemoteJWKSet","payload","jwtVerify","chargeDetails","installationId","chargeId","params","styles","key","value","body","signature","secret","crypto","ArchAppClient"],"mappings":"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","file":"index.js","sourcesContent":["import crypto from 'node:crypto';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\n\nexport interface PlatformSDKConfig {\n /** The app's live key. Required when the SDK is constructed; optional here so a\n * standalone app can pass whatever its environment provides. */\n apiKey?: string;\n appSlug?: string;\n baseUrl?: string;\n}\n\nexport interface VerifyTokenResponse {\n valid: boolean;\n installationId?: string;\n installedAt?: string;\n lastAccessedAt?: string;\n preferences?: {\n mode: 'light' | 'dark';\n theme: string;\n radius: string;\n styles: Record<string, string>;\n };\n error?: string;\n}\n\nexport interface ChargeConfig {\n type: 'plan' | 'one_time';\n installationId: string;\n returnUrl: string;\n planId?: string;\n productId?: string;\n description?: string;\n}\n\nexport interface CreateChargeResponse {\n confirmationUrl?: string;\n chargeRef?: string;\n chargeId?: string;\n status?: 'pending' | 'scheduled';\n message?: string;\n error?: string;\n}\n\nexport interface ChargeStatusResponse {\n id?: string;\n chargeRef?: string;\n type?: 'plan' | 'one_time' | 'usage_based';\n status?: 'pending' | 'approved' | 'declined' | 'expired';\n amount?: string;\n installationId?: string;\n productId?: string;\n planId?: string;\n error?: string;\n}\n\nexport interface UserAccessResponse {\n subscriptions?: Array<{\n planId: string;\n planIdentifier?: string;\n status: string;\n currentPeriodEnd: string | null;\n scheduledPlanId: string | null;\n }>;\n purchases?: Array<{\n productId: string;\n productIdentifier?: string;\n quantity: number;\n }>;\n error?: string;\n}\n\nexport function resolvePlatformBaseUrl(baseUrl?: string): string | undefined {\n const candidate =\n baseUrl ||\n (typeof process !== 'undefined'\n ? process.env?.ARCH_PLATFORM_URL || process.env?.PLATFORM_URL\n : undefined);\n return candidate ? candidate.replace(/\\/+$/, '') : undefined;\n}\n\nexport class PlatformSDK {\n private apiKey: string;\n private appSlug: string;\n private baseUrl: string;\n\n constructor(config: PlatformSDKConfig) {\n if (!config.apiKey) throw new Error('PlatformSDK: apiKey is required');\n if (!config.appSlug) throw new Error('PlatformSDK: appSlug is required');\n\n this.apiKey = config.apiKey;\n this.appSlug = config.appSlug;\n const baseUrl = resolvePlatformBaseUrl(config.baseUrl);\n if (!baseUrl) {\n throw new Error(\n 'PlatformSDK: baseUrl is required. Pass baseUrl or set ARCH_PLATFORM_URL (or PLATFORM_URL).'\n );\n }\n this.baseUrl = baseUrl;\n }\n\n private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = new Headers(options.headers || {});\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n\n if (options.body && !headers.has('Content-Type')) {\n headers.set('Content-Type', 'application/json');\n }\n\n try {\n const res = await fetch(url, { ...options, headers });\n const data = await res.json().catch(() => ({}));\n\n if (!res.ok) {\n return { error: data.error || `HTTP ${res.status}: ${res.statusText}` } as T;\n }\n return data as T;\n } catch (err) {\n return { error: err instanceof Error ? err.message : 'Unknown network error' } as T;\n }\n }\n\n async verifyToken(token: string): Promise<VerifyTokenResponse> {\n if (!token) return { valid: false, error: 'Token is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/verify?token=${encodeURIComponent(token)}`;\n return this.request<VerifyTokenResponse>(endpoint, { method: 'GET' });\n }\n\n async verifySessionToken(\n token: string,\n expectedIssuer?: string\n ): Promise<{\n valid: boolean;\n appId?: string;\n installationId?: string;\n userId?: string;\n userRole?: string;\n preferences?: VerifyTokenResponse['preferences'];\n error?: string;\n }> {\n if (!token) return { valid: false, error: 'Token is required' };\n\n try {\n const jwks = createRemoteJWKSet(new URL(`${this.baseUrl}/api/v1/app-bridge/jwks`));\n const { payload } = await jwtVerify(token, jwks, {\n issuer: new URL(expectedIssuer || this.baseUrl).origin,\n audience: this.appSlug,\n algorithms: ['EdDSA']\n });\n\n return {\n valid: true,\n installationId: payload.sub,\n preferences: payload.preferences as VerifyTokenResponse['preferences']\n };\n } catch (err: unknown) {\n return {\n valid: false,\n error: err instanceof Error ? err.message : 'Failed to verify session token'\n };\n }\n }\n\n async createCharge(chargeDetails: ChargeConfig): Promise<CreateChargeResponse> {\n if (!chargeDetails.type || !chargeDetails.installationId || !chargeDetails.returnUrl) {\n return { error: 'Missing required charge configuration fields' };\n }\n\n const endpoint = `/api/v1/apps/${this.appSlug}/charges`;\n return this.request<CreateChargeResponse>(endpoint, {\n method: 'POST',\n body: JSON.stringify(chargeDetails)\n });\n }\n\n async getUserAccess(installationId: string): Promise<UserAccessResponse> {\n if (!installationId) return { error: 'installationId is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/installations/${encodeURIComponent(installationId)}/access`;\n return this.request<UserAccessResponse>(endpoint, { method: 'GET' });\n }\n\n async getCharge(chargeId: string): Promise<ChargeStatusResponse> {\n if (!chargeId) return { error: 'chargeId is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/charges/${encodeURIComponent(chargeId)}`;\n return this.request<ChargeStatusResponse>(endpoint, { method: 'GET' });\n }\n\n async sendNotification(params: {\n installationId: string;\n title: string;\n message: string;\n link?: string;\n imageUrl?: string;\n forwardToEmail?: boolean;\n }): Promise<{ success: boolean; error?: string; notificationId?: string }> {\n if (!params.installationId || !params.title || !params.message) {\n return { success: false, error: 'Missing required notification fields' };\n }\n const endpoint = `/api/v1/apps/${this.appSlug}/notifications`;\n return this.request<{ success: boolean; error?: string; notificationId?: string }>(endpoint, {\n method: 'POST',\n body: JSON.stringify(params)\n });\n }\n\n /**\n * Helper to convert platform styles object to CSS variable string\n */\n static stylesToCss(styles: Record<string, string>): string {\n return Object.entries(styles)\n .map(([key, value]) => `${key}: ${value};`)\n .join(' ');\n }\n\n /**\n * Helper to verify Arch webhook signatures\n */\n static verifyWebhookSignature(body: string, signature: string, secret: string): boolean {\n const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');\n return expected === signature;\n }\n}\n\nexport type ArchAppClient = PlatformSDK;\nexport const ArchAppClient = PlatformSDK;\nexport type ArchAppClientConfig = PlatformSDKConfig;\n"]}
@@ -0,0 +1,2 @@
1
+ 'use strict';var A=require('crypto'),jose=require('jose');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var A__default=/*#__PURE__*/_interopDefault(A);function m(o){let e=o||(typeof process<"u"?process.env?.ARCH_PLATFORM_URL||process.env?.PLATFORM_URL:void 0);return e?e.replace(/\/+$/,""):void 0}var g=class{apiKey;appSlug;baseUrl;constructor(e){if(!e.apiKey)throw new Error("PlatformSDK: apiKey is required");if(!e.appSlug)throw new Error("PlatformSDK: appSlug is required");this.apiKey=e.apiKey,this.appSlug=e.appSlug;let r=m(e.baseUrl);if(!r)throw new Error("PlatformSDK: baseUrl is required. Pass baseUrl or set ARCH_PLATFORM_URL (or PLATFORM_URL).");this.baseUrl=r;}async request(e,r={}){let n=`${this.baseUrl}${e}`,i=new Headers(r.headers||{});i.set("Authorization",`Bearer ${this.apiKey}`),r.body&&!i.has("Content-Type")&&i.set("Content-Type","application/json");try{let t=await fetch(n,{...r,headers:i}),p=await t.json().catch(()=>({}));return t.ok?p:{error:p.error||`HTTP ${t.status}: ${t.statusText}`}}catch(t){return {error:t instanceof Error?t.message:"Unknown network error"}}}async verifyToken(e){if(!e)return {valid:false,error:"Token is required"};let r=`/api/v1/apps/${this.appSlug}/verify?token=${encodeURIComponent(e)}`;return this.request(r,{method:"GET"})}async verifySessionToken(e,r){if(!e)return {valid:false,error:"Token is required"};try{let n=jose.createRemoteJWKSet(new URL(`${this.baseUrl}/api/v1/app-bridge/jwks`)),{payload:i}=await jose.jwtVerify(e,n,{issuer:new URL(r||this.baseUrl).origin,audience:this.appSlug,algorithms:["EdDSA"]});return {valid:!0,installationId:i.sub,preferences:i.preferences}}catch(n){return {valid:false,error:n instanceof Error?n.message:"Failed to verify session token"}}}async createCharge(e){if(!e.type||!e.installationId||!e.returnUrl)return {error:"Missing required charge configuration fields"};let r=`/api/v1/apps/${this.appSlug}/charges`;return this.request(r,{method:"POST",body:JSON.stringify(e)})}async getUserAccess(e){if(!e)return {error:"installationId is required"};let r=`/api/v1/apps/${this.appSlug}/installations/${encodeURIComponent(e)}/access`;return this.request(r,{method:"GET"})}async getCharge(e){if(!e)return {error:"chargeId is required"};let r=`/api/v1/apps/${this.appSlug}/charges/${encodeURIComponent(e)}`;return this.request(r,{method:"GET"})}async sendNotification(e){if(!e.installationId||!e.title||!e.message)return {success:false,error:"Missing required notification fields"};let r=`/api/v1/apps/${this.appSlug}/notifications`;return this.request(r,{method:"POST",body:JSON.stringify(e)})}static stylesToCss(e){return Object.entries(e).map(([r,n])=>`${r}: ${n};`).join(" ")}static verifyWebhookSignature(e,r,n){return A__default.default.createHmac("sha256",n).update(e).digest("hex")===r}};function U(o,e){let r=m(o.sdkConfig.baseUrl);if(!r&&(o.sdkConfig.apiKey||o.sdkConfig.appSlug))throw new Error("createArchHandle: a platform URL is required when LIVE_API_KEY or APP_SLUG is set. Set PLATFORM_URL (or ARCH_PLATFORM_URL), or leave both credentials empty to run standalone.");let n=r?new g({...o.sdkConfig,baseUrl:r}):null,i=o.sessionCookieName||"arch_session";return async({event:t,resolve:p})=>{if(!n)return p(t);let{url:y,cookies:f}=t,R=y.searchParams.get("platform_token"),u=y.protocol==="https:"||t.request.headers.get("x-forwarded-proto")==="https",l,c,h=t.request.headers.get("Authorization")||t.request.headers.get("authorization"),k=t.request.headers.get("x-arch-session-token"),I=t.request.headers.get("x-arch-platform-origin"),d;if(h&&h.toLowerCase().startsWith("bearer ")?d=h.substring(7).trim():k&&(d=k.trim()),d){let s=await n.verifySessionToken(d,I||void 0);s.valid&&s.installationId?(l=s.installationId,c=s.preferences):console.warn("[Arch SDK] Session token verification failed: Invalid signature or mismatched LIVE_API_KEY. Verify LIVE_API_KEY in $env/static/private.");}let C=f.get(i);if(!l&&C)try{let s=JSON.parse(C);l=s.installationId,c=s.preferences;}catch{f.delete(i,{path:"/"});}if(R){let s=await n.verifyToken(R);if(s.valid&&s.installationId){l=s.installationId,c=s.preferences||{mode:"dark",theme:"arch",radius:"0.5",styles:{}};let a={path:"/",httpOnly:true,sameSite:u?"none":"lax",secure:u,maxAge:3600*24*7};u&&(a.partitioned=true),f.set(i,JSON.stringify({installationId:s.installationId,preferences:s.preferences}),a),t.locals.installationId=l,t.locals.preferences=c;}}return l&&(t.locals.installationId=l,t.locals.preferences=c),p(t,{transformPageChunk:({html:s})=>{let a=t.locals.preferences,S=a&&typeof a=="object"&&"mode"in a&&typeof a.mode=="string"?a.mode:"dark";return s.replace("%mode%",S)}})}}var q=U;exports.createArchHandle=U;exports.handleArchAuth=q;//# sourceMappingURL=sveltekit.cjs.map
2
+ //# sourceMappingURL=sveltekit.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/sveltekit.ts"],"names":["resolvePlatformBaseUrl","baseUrl","candidate","PlatformSDK","config","endpoint","options","url","headers","res","data","err","token","expectedIssuer","jwks","createRemoteJWKSet","payload","jwtVerify","chargeDetails","installationId","chargeId","params","styles","key","value","body","signature","secret","crypto","createArchHandle","redirect","sdk","sessionCookie","event","resolve","cookies","isSecure","currentInstallationId","currentPreferences","authHeader","customHeader","platformOriginHeader","bearerToken","sessionResult","rawSession","parsed","result","cookieOpts","html","prefs","mode","handleArchAuth"],"mappings":"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","file":"sveltekit.cjs","sourcesContent":["import crypto from 'node:crypto';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\n\nexport interface PlatformSDKConfig {\n /** The app's live key. Required when the SDK is constructed; optional here so a\n * standalone app can pass whatever its environment provides. */\n apiKey?: string;\n appSlug?: string;\n baseUrl?: string;\n}\n\nexport interface VerifyTokenResponse {\n valid: boolean;\n installationId?: string;\n installedAt?: string;\n lastAccessedAt?: string;\n preferences?: {\n mode: 'light' | 'dark';\n theme: string;\n radius: string;\n styles: Record<string, string>;\n };\n error?: string;\n}\n\nexport interface ChargeConfig {\n type: 'plan' | 'one_time';\n installationId: string;\n returnUrl: string;\n planId?: string;\n productId?: string;\n description?: string;\n}\n\nexport interface CreateChargeResponse {\n confirmationUrl?: string;\n chargeRef?: string;\n chargeId?: string;\n status?: 'pending' | 'scheduled';\n message?: string;\n error?: string;\n}\n\nexport interface ChargeStatusResponse {\n id?: string;\n chargeRef?: string;\n type?: 'plan' | 'one_time' | 'usage_based';\n status?: 'pending' | 'approved' | 'declined' | 'expired';\n amount?: string;\n installationId?: string;\n productId?: string;\n planId?: string;\n error?: string;\n}\n\nexport interface UserAccessResponse {\n subscriptions?: Array<{\n planId: string;\n planIdentifier?: string;\n status: string;\n currentPeriodEnd: string | null;\n scheduledPlanId: string | null;\n }>;\n purchases?: Array<{\n productId: string;\n productIdentifier?: string;\n quantity: number;\n }>;\n error?: string;\n}\n\nexport function resolvePlatformBaseUrl(baseUrl?: string): string | undefined {\n const candidate =\n baseUrl ||\n (typeof process !== 'undefined'\n ? process.env?.ARCH_PLATFORM_URL || process.env?.PLATFORM_URL\n : undefined);\n return candidate ? candidate.replace(/\\/+$/, '') : undefined;\n}\n\nexport class PlatformSDK {\n private apiKey: string;\n private appSlug: string;\n private baseUrl: string;\n\n constructor(config: PlatformSDKConfig) {\n if (!config.apiKey) throw new Error('PlatformSDK: apiKey is required');\n if (!config.appSlug) throw new Error('PlatformSDK: appSlug is required');\n\n this.apiKey = config.apiKey;\n this.appSlug = config.appSlug;\n const baseUrl = resolvePlatformBaseUrl(config.baseUrl);\n if (!baseUrl) {\n throw new Error(\n 'PlatformSDK: baseUrl is required. Pass baseUrl or set ARCH_PLATFORM_URL (or PLATFORM_URL).'\n );\n }\n this.baseUrl = baseUrl;\n }\n\n private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = new Headers(options.headers || {});\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n\n if (options.body && !headers.has('Content-Type')) {\n headers.set('Content-Type', 'application/json');\n }\n\n try {\n const res = await fetch(url, { ...options, headers });\n const data = await res.json().catch(() => ({}));\n\n if (!res.ok) {\n return { error: data.error || `HTTP ${res.status}: ${res.statusText}` } as T;\n }\n return data as T;\n } catch (err) {\n return { error: err instanceof Error ? err.message : 'Unknown network error' } as T;\n }\n }\n\n async verifyToken(token: string): Promise<VerifyTokenResponse> {\n if (!token) return { valid: false, error: 'Token is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/verify?token=${encodeURIComponent(token)}`;\n return this.request<VerifyTokenResponse>(endpoint, { method: 'GET' });\n }\n\n async verifySessionToken(\n token: string,\n expectedIssuer?: string\n ): Promise<{\n valid: boolean;\n appId?: string;\n installationId?: string;\n userId?: string;\n userRole?: string;\n preferences?: VerifyTokenResponse['preferences'];\n error?: string;\n }> {\n if (!token) return { valid: false, error: 'Token is required' };\n\n try {\n const jwks = createRemoteJWKSet(new URL(`${this.baseUrl}/api/v1/app-bridge/jwks`));\n const { payload } = await jwtVerify(token, jwks, {\n issuer: new URL(expectedIssuer || this.baseUrl).origin,\n audience: this.appSlug,\n algorithms: ['EdDSA']\n });\n\n return {\n valid: true,\n installationId: payload.sub,\n preferences: payload.preferences as VerifyTokenResponse['preferences']\n };\n } catch (err: unknown) {\n return {\n valid: false,\n error: err instanceof Error ? err.message : 'Failed to verify session token'\n };\n }\n }\n\n async createCharge(chargeDetails: ChargeConfig): Promise<CreateChargeResponse> {\n if (!chargeDetails.type || !chargeDetails.installationId || !chargeDetails.returnUrl) {\n return { error: 'Missing required charge configuration fields' };\n }\n\n const endpoint = `/api/v1/apps/${this.appSlug}/charges`;\n return this.request<CreateChargeResponse>(endpoint, {\n method: 'POST',\n body: JSON.stringify(chargeDetails)\n });\n }\n\n async getUserAccess(installationId: string): Promise<UserAccessResponse> {\n if (!installationId) return { error: 'installationId is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/installations/${encodeURIComponent(installationId)}/access`;\n return this.request<UserAccessResponse>(endpoint, { method: 'GET' });\n }\n\n async getCharge(chargeId: string): Promise<ChargeStatusResponse> {\n if (!chargeId) return { error: 'chargeId is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/charges/${encodeURIComponent(chargeId)}`;\n return this.request<ChargeStatusResponse>(endpoint, { method: 'GET' });\n }\n\n async sendNotification(params: {\n installationId: string;\n title: string;\n message: string;\n link?: string;\n imageUrl?: string;\n forwardToEmail?: boolean;\n }): Promise<{ success: boolean; error?: string; notificationId?: string }> {\n if (!params.installationId || !params.title || !params.message) {\n return { success: false, error: 'Missing required notification fields' };\n }\n const endpoint = `/api/v1/apps/${this.appSlug}/notifications`;\n return this.request<{ success: boolean; error?: string; notificationId?: string }>(endpoint, {\n method: 'POST',\n body: JSON.stringify(params)\n });\n }\n\n /**\n * Helper to convert platform styles object to CSS variable string\n */\n static stylesToCss(styles: Record<string, string>): string {\n return Object.entries(styles)\n .map(([key, value]) => `${key}: ${value};`)\n .join(' ');\n }\n\n /**\n * Helper to verify Arch webhook signatures\n */\n static verifyWebhookSignature(body: string, signature: string, secret: string): boolean {\n const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');\n return expected === signature;\n }\n}\n\nexport type ArchAppClient = PlatformSDK;\nexport const ArchAppClient = PlatformSDK;\nexport type ArchAppClientConfig = PlatformSDKConfig;\n","import type { Handle } from '@sveltejs/kit';\nimport {\n PlatformSDK,\n resolvePlatformBaseUrl,\n type PlatformSDKConfig,\n type VerifyTokenResponse\n} from './index';\n\nexport interface ArchHandleConfig {\n sdkConfig: PlatformSDKConfig;\n /**\n * Optional override for the root route of your app (default: '/')\n */\n rootRoute?: string;\n /**\n * Cookie name for the session (default: 'arch_session')\n */\n sessionCookieName?: string;\n /**\n * Cookie name for path persistence (default: 'arch_last_path')\n */\n pathCookieName?: string;\n}\n\n/**\n * Creates a SvelteKit handle to manage Arch platform authentication and state.\n *\n * This handle:\n * 1. Detects `platform_token` in the URL and verifies it.\n * 2. Manages session cookies for `installationId` and `preferences`.\n * 3. Handles \"Last Path\" recovery after theme-induced reloads.\n */\nexport function createArchHandle(\n config: ArchHandleConfig,\n redirect: typeof import('@sveltejs/kit').redirect\n): Handle {\n void redirect; // Keep the legacy second argument until the SDK migration phase.\n // With no platform URL the app runs standalone: it is a plain SvelteKit app\n // rather than one embedded in Arch, so the handle simply passes requests through.\n // Credentials without a URL is a misconfiguration, and failing loudly beats\n // silently skipping session verification.\n const baseUrl = resolvePlatformBaseUrl(config.sdkConfig.baseUrl);\n if (!baseUrl && (config.sdkConfig.apiKey || config.sdkConfig.appSlug)) {\n throw new Error(\n 'createArchHandle: a platform URL is required when LIVE_API_KEY or APP_SLUG is set. Set PLATFORM_URL (or ARCH_PLATFORM_URL), or leave both credentials empty to run standalone.'\n );\n }\n const sdk = baseUrl ? new PlatformSDK({ ...config.sdkConfig, baseUrl }) : null;\n const sessionCookie = config.sessionCookieName || 'arch_session';\n\n return async ({ event, resolve }) => {\n if (!sdk) return resolve(event);\n\n const { url, cookies } = event;\n const token = url.searchParams.get('platform_token');\n const isSecure =\n url.protocol === 'https:' || event.request.headers.get('x-forwarded-proto') === 'https';\n\n let currentInstallationId: string | undefined;\n let currentPreferences: VerifyTokenResponse['preferences'];\n\n // 0. Cookieless JWT Bearer Token Resolution (Authorization: Bearer <token> or x-arch-session-token)\n const authHeader =\n event.request.headers.get('Authorization') || event.request.headers.get('authorization');\n const customHeader = event.request.headers.get('x-arch-session-token');\n const platformOriginHeader = event.request.headers.get('x-arch-platform-origin');\n\n let bearerToken: string | undefined;\n if (authHeader && authHeader.toLowerCase().startsWith('bearer ')) {\n bearerToken = authHeader.substring(7).trim();\n } else if (customHeader) {\n bearerToken = customHeader.trim();\n }\n\n if (bearerToken) {\n const sessionResult = await sdk.verifySessionToken(\n bearerToken,\n platformOriginHeader || undefined\n );\n if (sessionResult.valid && sessionResult.installationId) {\n currentInstallationId = sessionResult.installationId;\n currentPreferences = sessionResult.preferences;\n } else {\n console.warn(\n '[Arch SDK] Session token verification failed: Invalid signature or mismatched LIVE_API_KEY. Verify LIVE_API_KEY in $env/static/private.'\n );\n }\n }\n\n const rawSession = cookies.get(sessionCookie);\n if (!currentInstallationId && rawSession) {\n try {\n const parsed = JSON.parse(rawSession);\n currentInstallationId = parsed.installationId;\n currentPreferences = parsed.preferences;\n } catch {\n cookies.delete(sessionCookie, { path: '/' });\n }\n }\n\n // 1. Initial Handshake / Token Verification\n if (token) {\n const result = await sdk.verifyToken(token);\n\n if (result.valid && result.installationId) {\n currentInstallationId = result.installationId;\n currentPreferences = result.preferences || {\n mode: 'dark',\n theme: 'arch',\n radius: '0.5',\n styles: {}\n };\n\n // Build session cookie options cleanly\n const cookieOpts: Parameters<typeof cookies.set>[2] = {\n path: '/',\n httpOnly: true,\n sameSite: isSecure ? 'none' : 'lax',\n secure: isSecure,\n maxAge: 60 * 60 * 24 * 7 // 1 week\n };\n if (isSecure) {\n cookieOpts.partitioned = true;\n }\n\n // Set session cookie\n cookies.set(\n sessionCookie,\n JSON.stringify({\n installationId: result.installationId,\n preferences: result.preferences\n }),\n cookieOpts\n );\n\n event.locals.installationId = currentInstallationId;\n event.locals.preferences = currentPreferences;\n }\n }\n\n // Attach to locals for every request\n if (currentInstallationId) {\n event.locals.installationId = currentInstallationId;\n event.locals.preferences = currentPreferences;\n }\n\n return resolve(event, {\n transformPageChunk: ({ html }) => {\n const prefs = event.locals.preferences;\n const mode =\n prefs && typeof prefs === 'object' && 'mode' in prefs && typeof prefs.mode === 'string'\n ? prefs.mode\n : 'dark';\n return html.replace('%mode%', mode);\n }\n });\n };\n}\n\nexport const handleArchAuth = createArchHandle;\n\n// Extend SvelteKit Locals interface\ndeclare global {\n // SvelteKit's ambient App namespace is the documented locals extension point.\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace App {\n interface Locals {\n installationId?: string;\n preferences?: {\n mode: 'light' | 'dark';\n theme: string;\n radius: string;\n styles: Record<string, string>;\n };\n }\n }\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import * as _sveltejs_kit from '@sveltejs/kit';
2
+ import { Handle } from '@sveltejs/kit';
3
+ import { PlatformSDKConfig } from './index.cjs';
4
+
5
+ interface ArchHandleConfig {
6
+ sdkConfig: PlatformSDKConfig;
7
+ /**
8
+ * Optional override for the root route of your app (default: '/')
9
+ */
10
+ rootRoute?: string;
11
+ /**
12
+ * Cookie name for the session (default: 'arch_session')
13
+ */
14
+ sessionCookieName?: string;
15
+ /**
16
+ * Cookie name for path persistence (default: 'arch_last_path')
17
+ */
18
+ pathCookieName?: string;
19
+ }
20
+ /**
21
+ * Creates a SvelteKit handle to manage Arch platform authentication and state.
22
+ *
23
+ * This handle:
24
+ * 1. Detects `platform_token` in the URL and verifies it.
25
+ * 2. Manages session cookies for `installationId` and `preferences`.
26
+ * 3. Handles "Last Path" recovery after theme-induced reloads.
27
+ */
28
+ declare function createArchHandle(config: ArchHandleConfig, redirect: typeof _sveltejs_kit.redirect): Handle;
29
+ declare const handleArchAuth: typeof createArchHandle;
30
+ declare global {
31
+ namespace App {
32
+ interface Locals {
33
+ installationId?: string;
34
+ preferences?: {
35
+ mode: 'light' | 'dark';
36
+ theme: string;
37
+ radius: string;
38
+ styles: Record<string, string>;
39
+ };
40
+ }
41
+ }
42
+ }
43
+
44
+ export { type ArchHandleConfig, createArchHandle, handleArchAuth };
@@ -0,0 +1,44 @@
1
+ import * as _sveltejs_kit from '@sveltejs/kit';
2
+ import { Handle } from '@sveltejs/kit';
3
+ import { PlatformSDKConfig } from './index.js';
4
+
5
+ interface ArchHandleConfig {
6
+ sdkConfig: PlatformSDKConfig;
7
+ /**
8
+ * Optional override for the root route of your app (default: '/')
9
+ */
10
+ rootRoute?: string;
11
+ /**
12
+ * Cookie name for the session (default: 'arch_session')
13
+ */
14
+ sessionCookieName?: string;
15
+ /**
16
+ * Cookie name for path persistence (default: 'arch_last_path')
17
+ */
18
+ pathCookieName?: string;
19
+ }
20
+ /**
21
+ * Creates a SvelteKit handle to manage Arch platform authentication and state.
22
+ *
23
+ * This handle:
24
+ * 1. Detects `platform_token` in the URL and verifies it.
25
+ * 2. Manages session cookies for `installationId` and `preferences`.
26
+ * 3. Handles "Last Path" recovery after theme-induced reloads.
27
+ */
28
+ declare function createArchHandle(config: ArchHandleConfig, redirect: typeof _sveltejs_kit.redirect): Handle;
29
+ declare const handleArchAuth: typeof createArchHandle;
30
+ declare global {
31
+ namespace App {
32
+ interface Locals {
33
+ installationId?: string;
34
+ preferences?: {
35
+ mode: 'light' | 'dark';
36
+ theme: string;
37
+ radius: string;
38
+ styles: Record<string, string>;
39
+ };
40
+ }
41
+ }
42
+ }
43
+
44
+ export { type ArchHandleConfig, createArchHandle, handleArchAuth };
@@ -0,0 +1,2 @@
1
+ import A from'crypto';import {createRemoteJWKSet,jwtVerify}from'jose';function m(o){let e=o||(typeof process<"u"?process.env?.ARCH_PLATFORM_URL||process.env?.PLATFORM_URL:void 0);return e?e.replace(/\/+$/,""):void 0}var g=class{apiKey;appSlug;baseUrl;constructor(e){if(!e.apiKey)throw new Error("PlatformSDK: apiKey is required");if(!e.appSlug)throw new Error("PlatformSDK: appSlug is required");this.apiKey=e.apiKey,this.appSlug=e.appSlug;let r=m(e.baseUrl);if(!r)throw new Error("PlatformSDK: baseUrl is required. Pass baseUrl or set ARCH_PLATFORM_URL (or PLATFORM_URL).");this.baseUrl=r;}async request(e,r={}){let n=`${this.baseUrl}${e}`,i=new Headers(r.headers||{});i.set("Authorization",`Bearer ${this.apiKey}`),r.body&&!i.has("Content-Type")&&i.set("Content-Type","application/json");try{let t=await fetch(n,{...r,headers:i}),p=await t.json().catch(()=>({}));return t.ok?p:{error:p.error||`HTTP ${t.status}: ${t.statusText}`}}catch(t){return {error:t instanceof Error?t.message:"Unknown network error"}}}async verifyToken(e){if(!e)return {valid:false,error:"Token is required"};let r=`/api/v1/apps/${this.appSlug}/verify?token=${encodeURIComponent(e)}`;return this.request(r,{method:"GET"})}async verifySessionToken(e,r){if(!e)return {valid:false,error:"Token is required"};try{let n=createRemoteJWKSet(new URL(`${this.baseUrl}/api/v1/app-bridge/jwks`)),{payload:i}=await jwtVerify(e,n,{issuer:new URL(r||this.baseUrl).origin,audience:this.appSlug,algorithms:["EdDSA"]});return {valid:!0,installationId:i.sub,preferences:i.preferences}}catch(n){return {valid:false,error:n instanceof Error?n.message:"Failed to verify session token"}}}async createCharge(e){if(!e.type||!e.installationId||!e.returnUrl)return {error:"Missing required charge configuration fields"};let r=`/api/v1/apps/${this.appSlug}/charges`;return this.request(r,{method:"POST",body:JSON.stringify(e)})}async getUserAccess(e){if(!e)return {error:"installationId is required"};let r=`/api/v1/apps/${this.appSlug}/installations/${encodeURIComponent(e)}/access`;return this.request(r,{method:"GET"})}async getCharge(e){if(!e)return {error:"chargeId is required"};let r=`/api/v1/apps/${this.appSlug}/charges/${encodeURIComponent(e)}`;return this.request(r,{method:"GET"})}async sendNotification(e){if(!e.installationId||!e.title||!e.message)return {success:false,error:"Missing required notification fields"};let r=`/api/v1/apps/${this.appSlug}/notifications`;return this.request(r,{method:"POST",body:JSON.stringify(e)})}static stylesToCss(e){return Object.entries(e).map(([r,n])=>`${r}: ${n};`).join(" ")}static verifyWebhookSignature(e,r,n){return A.createHmac("sha256",n).update(e).digest("hex")===r}};function U(o,e){let r=m(o.sdkConfig.baseUrl);if(!r&&(o.sdkConfig.apiKey||o.sdkConfig.appSlug))throw new Error("createArchHandle: a platform URL is required when LIVE_API_KEY or APP_SLUG is set. Set PLATFORM_URL (or ARCH_PLATFORM_URL), or leave both credentials empty to run standalone.");let n=r?new g({...o.sdkConfig,baseUrl:r}):null,i=o.sessionCookieName||"arch_session";return async({event:t,resolve:p})=>{if(!n)return p(t);let{url:y,cookies:f}=t,R=y.searchParams.get("platform_token"),u=y.protocol==="https:"||t.request.headers.get("x-forwarded-proto")==="https",l,c,h=t.request.headers.get("Authorization")||t.request.headers.get("authorization"),k=t.request.headers.get("x-arch-session-token"),I=t.request.headers.get("x-arch-platform-origin"),d;if(h&&h.toLowerCase().startsWith("bearer ")?d=h.substring(7).trim():k&&(d=k.trim()),d){let s=await n.verifySessionToken(d,I||void 0);s.valid&&s.installationId?(l=s.installationId,c=s.preferences):console.warn("[Arch SDK] Session token verification failed: Invalid signature or mismatched LIVE_API_KEY. Verify LIVE_API_KEY in $env/static/private.");}let C=f.get(i);if(!l&&C)try{let s=JSON.parse(C);l=s.installationId,c=s.preferences;}catch{f.delete(i,{path:"/"});}if(R){let s=await n.verifyToken(R);if(s.valid&&s.installationId){l=s.installationId,c=s.preferences||{mode:"dark",theme:"arch",radius:"0.5",styles:{}};let a={path:"/",httpOnly:true,sameSite:u?"none":"lax",secure:u,maxAge:3600*24*7};u&&(a.partitioned=true),f.set(i,JSON.stringify({installationId:s.installationId,preferences:s.preferences}),a),t.locals.installationId=l,t.locals.preferences=c;}}return l&&(t.locals.installationId=l,t.locals.preferences=c),p(t,{transformPageChunk:({html:s})=>{let a=t.locals.preferences,S=a&&typeof a=="object"&&"mode"in a&&typeof a.mode=="string"?a.mode:"dark";return s.replace("%mode%",S)}})}}var q=U;export{U as createArchHandle,q as handleArchAuth};//# sourceMappingURL=sveltekit.js.map
2
+ //# sourceMappingURL=sveltekit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/sveltekit.ts"],"names":["resolvePlatformBaseUrl","baseUrl","candidate","PlatformSDK","config","endpoint","options","url","headers","res","data","err","token","expectedIssuer","jwks","createRemoteJWKSet","payload","jwtVerify","chargeDetails","installationId","chargeId","params","styles","key","value","body","signature","secret","crypto","createArchHandle","redirect","sdk","sessionCookie","event","resolve","cookies","isSecure","currentInstallationId","currentPreferences","authHeader","customHeader","platformOriginHeader","bearerToken","sessionResult","rawSession","parsed","result","cookieOpts","html","prefs","mode","handleArchAuth"],"mappings":"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","file":"sveltekit.js","sourcesContent":["import crypto from 'node:crypto';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\n\nexport interface PlatformSDKConfig {\n /** The app's live key. Required when the SDK is constructed; optional here so a\n * standalone app can pass whatever its environment provides. */\n apiKey?: string;\n appSlug?: string;\n baseUrl?: string;\n}\n\nexport interface VerifyTokenResponse {\n valid: boolean;\n installationId?: string;\n installedAt?: string;\n lastAccessedAt?: string;\n preferences?: {\n mode: 'light' | 'dark';\n theme: string;\n radius: string;\n styles: Record<string, string>;\n };\n error?: string;\n}\n\nexport interface ChargeConfig {\n type: 'plan' | 'one_time';\n installationId: string;\n returnUrl: string;\n planId?: string;\n productId?: string;\n description?: string;\n}\n\nexport interface CreateChargeResponse {\n confirmationUrl?: string;\n chargeRef?: string;\n chargeId?: string;\n status?: 'pending' | 'scheduled';\n message?: string;\n error?: string;\n}\n\nexport interface ChargeStatusResponse {\n id?: string;\n chargeRef?: string;\n type?: 'plan' | 'one_time' | 'usage_based';\n status?: 'pending' | 'approved' | 'declined' | 'expired';\n amount?: string;\n installationId?: string;\n productId?: string;\n planId?: string;\n error?: string;\n}\n\nexport interface UserAccessResponse {\n subscriptions?: Array<{\n planId: string;\n planIdentifier?: string;\n status: string;\n currentPeriodEnd: string | null;\n scheduledPlanId: string | null;\n }>;\n purchases?: Array<{\n productId: string;\n productIdentifier?: string;\n quantity: number;\n }>;\n error?: string;\n}\n\nexport function resolvePlatformBaseUrl(baseUrl?: string): string | undefined {\n const candidate =\n baseUrl ||\n (typeof process !== 'undefined'\n ? process.env?.ARCH_PLATFORM_URL || process.env?.PLATFORM_URL\n : undefined);\n return candidate ? candidate.replace(/\\/+$/, '') : undefined;\n}\n\nexport class PlatformSDK {\n private apiKey: string;\n private appSlug: string;\n private baseUrl: string;\n\n constructor(config: PlatformSDKConfig) {\n if (!config.apiKey) throw new Error('PlatformSDK: apiKey is required');\n if (!config.appSlug) throw new Error('PlatformSDK: appSlug is required');\n\n this.apiKey = config.apiKey;\n this.appSlug = config.appSlug;\n const baseUrl = resolvePlatformBaseUrl(config.baseUrl);\n if (!baseUrl) {\n throw new Error(\n 'PlatformSDK: baseUrl is required. Pass baseUrl or set ARCH_PLATFORM_URL (or PLATFORM_URL).'\n );\n }\n this.baseUrl = baseUrl;\n }\n\n private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = new Headers(options.headers || {});\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n\n if (options.body && !headers.has('Content-Type')) {\n headers.set('Content-Type', 'application/json');\n }\n\n try {\n const res = await fetch(url, { ...options, headers });\n const data = await res.json().catch(() => ({}));\n\n if (!res.ok) {\n return { error: data.error || `HTTP ${res.status}: ${res.statusText}` } as T;\n }\n return data as T;\n } catch (err) {\n return { error: err instanceof Error ? err.message : 'Unknown network error' } as T;\n }\n }\n\n async verifyToken(token: string): Promise<VerifyTokenResponse> {\n if (!token) return { valid: false, error: 'Token is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/verify?token=${encodeURIComponent(token)}`;\n return this.request<VerifyTokenResponse>(endpoint, { method: 'GET' });\n }\n\n async verifySessionToken(\n token: string,\n expectedIssuer?: string\n ): Promise<{\n valid: boolean;\n appId?: string;\n installationId?: string;\n userId?: string;\n userRole?: string;\n preferences?: VerifyTokenResponse['preferences'];\n error?: string;\n }> {\n if (!token) return { valid: false, error: 'Token is required' };\n\n try {\n const jwks = createRemoteJWKSet(new URL(`${this.baseUrl}/api/v1/app-bridge/jwks`));\n const { payload } = await jwtVerify(token, jwks, {\n issuer: new URL(expectedIssuer || this.baseUrl).origin,\n audience: this.appSlug,\n algorithms: ['EdDSA']\n });\n\n return {\n valid: true,\n installationId: payload.sub,\n preferences: payload.preferences as VerifyTokenResponse['preferences']\n };\n } catch (err: unknown) {\n return {\n valid: false,\n error: err instanceof Error ? err.message : 'Failed to verify session token'\n };\n }\n }\n\n async createCharge(chargeDetails: ChargeConfig): Promise<CreateChargeResponse> {\n if (!chargeDetails.type || !chargeDetails.installationId || !chargeDetails.returnUrl) {\n return { error: 'Missing required charge configuration fields' };\n }\n\n const endpoint = `/api/v1/apps/${this.appSlug}/charges`;\n return this.request<CreateChargeResponse>(endpoint, {\n method: 'POST',\n body: JSON.stringify(chargeDetails)\n });\n }\n\n async getUserAccess(installationId: string): Promise<UserAccessResponse> {\n if (!installationId) return { error: 'installationId is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/installations/${encodeURIComponent(installationId)}/access`;\n return this.request<UserAccessResponse>(endpoint, { method: 'GET' });\n }\n\n async getCharge(chargeId: string): Promise<ChargeStatusResponse> {\n if (!chargeId) return { error: 'chargeId is required' };\n const endpoint = `/api/v1/apps/${this.appSlug}/charges/${encodeURIComponent(chargeId)}`;\n return this.request<ChargeStatusResponse>(endpoint, { method: 'GET' });\n }\n\n async sendNotification(params: {\n installationId: string;\n title: string;\n message: string;\n link?: string;\n imageUrl?: string;\n forwardToEmail?: boolean;\n }): Promise<{ success: boolean; error?: string; notificationId?: string }> {\n if (!params.installationId || !params.title || !params.message) {\n return { success: false, error: 'Missing required notification fields' };\n }\n const endpoint = `/api/v1/apps/${this.appSlug}/notifications`;\n return this.request<{ success: boolean; error?: string; notificationId?: string }>(endpoint, {\n method: 'POST',\n body: JSON.stringify(params)\n });\n }\n\n /**\n * Helper to convert platform styles object to CSS variable string\n */\n static stylesToCss(styles: Record<string, string>): string {\n return Object.entries(styles)\n .map(([key, value]) => `${key}: ${value};`)\n .join(' ');\n }\n\n /**\n * Helper to verify Arch webhook signatures\n */\n static verifyWebhookSignature(body: string, signature: string, secret: string): boolean {\n const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');\n return expected === signature;\n }\n}\n\nexport type ArchAppClient = PlatformSDK;\nexport const ArchAppClient = PlatformSDK;\nexport type ArchAppClientConfig = PlatformSDKConfig;\n","import type { Handle } from '@sveltejs/kit';\nimport {\n PlatformSDK,\n resolvePlatformBaseUrl,\n type PlatformSDKConfig,\n type VerifyTokenResponse\n} from './index';\n\nexport interface ArchHandleConfig {\n sdkConfig: PlatformSDKConfig;\n /**\n * Optional override for the root route of your app (default: '/')\n */\n rootRoute?: string;\n /**\n * Cookie name for the session (default: 'arch_session')\n */\n sessionCookieName?: string;\n /**\n * Cookie name for path persistence (default: 'arch_last_path')\n */\n pathCookieName?: string;\n}\n\n/**\n * Creates a SvelteKit handle to manage Arch platform authentication and state.\n *\n * This handle:\n * 1. Detects `platform_token` in the URL and verifies it.\n * 2. Manages session cookies for `installationId` and `preferences`.\n * 3. Handles \"Last Path\" recovery after theme-induced reloads.\n */\nexport function createArchHandle(\n config: ArchHandleConfig,\n redirect: typeof import('@sveltejs/kit').redirect\n): Handle {\n void redirect; // Keep the legacy second argument until the SDK migration phase.\n // With no platform URL the app runs standalone: it is a plain SvelteKit app\n // rather than one embedded in Arch, so the handle simply passes requests through.\n // Credentials without a URL is a misconfiguration, and failing loudly beats\n // silently skipping session verification.\n const baseUrl = resolvePlatformBaseUrl(config.sdkConfig.baseUrl);\n if (!baseUrl && (config.sdkConfig.apiKey || config.sdkConfig.appSlug)) {\n throw new Error(\n 'createArchHandle: a platform URL is required when LIVE_API_KEY or APP_SLUG is set. Set PLATFORM_URL (or ARCH_PLATFORM_URL), or leave both credentials empty to run standalone.'\n );\n }\n const sdk = baseUrl ? new PlatformSDK({ ...config.sdkConfig, baseUrl }) : null;\n const sessionCookie = config.sessionCookieName || 'arch_session';\n\n return async ({ event, resolve }) => {\n if (!sdk) return resolve(event);\n\n const { url, cookies } = event;\n const token = url.searchParams.get('platform_token');\n const isSecure =\n url.protocol === 'https:' || event.request.headers.get('x-forwarded-proto') === 'https';\n\n let currentInstallationId: string | undefined;\n let currentPreferences: VerifyTokenResponse['preferences'];\n\n // 0. Cookieless JWT Bearer Token Resolution (Authorization: Bearer <token> or x-arch-session-token)\n const authHeader =\n event.request.headers.get('Authorization') || event.request.headers.get('authorization');\n const customHeader = event.request.headers.get('x-arch-session-token');\n const platformOriginHeader = event.request.headers.get('x-arch-platform-origin');\n\n let bearerToken: string | undefined;\n if (authHeader && authHeader.toLowerCase().startsWith('bearer ')) {\n bearerToken = authHeader.substring(7).trim();\n } else if (customHeader) {\n bearerToken = customHeader.trim();\n }\n\n if (bearerToken) {\n const sessionResult = await sdk.verifySessionToken(\n bearerToken,\n platformOriginHeader || undefined\n );\n if (sessionResult.valid && sessionResult.installationId) {\n currentInstallationId = sessionResult.installationId;\n currentPreferences = sessionResult.preferences;\n } else {\n console.warn(\n '[Arch SDK] Session token verification failed: Invalid signature or mismatched LIVE_API_KEY. Verify LIVE_API_KEY in $env/static/private.'\n );\n }\n }\n\n const rawSession = cookies.get(sessionCookie);\n if (!currentInstallationId && rawSession) {\n try {\n const parsed = JSON.parse(rawSession);\n currentInstallationId = parsed.installationId;\n currentPreferences = parsed.preferences;\n } catch {\n cookies.delete(sessionCookie, { path: '/' });\n }\n }\n\n // 1. Initial Handshake / Token Verification\n if (token) {\n const result = await sdk.verifyToken(token);\n\n if (result.valid && result.installationId) {\n currentInstallationId = result.installationId;\n currentPreferences = result.preferences || {\n mode: 'dark',\n theme: 'arch',\n radius: '0.5',\n styles: {}\n };\n\n // Build session cookie options cleanly\n const cookieOpts: Parameters<typeof cookies.set>[2] = {\n path: '/',\n httpOnly: true,\n sameSite: isSecure ? 'none' : 'lax',\n secure: isSecure,\n maxAge: 60 * 60 * 24 * 7 // 1 week\n };\n if (isSecure) {\n cookieOpts.partitioned = true;\n }\n\n // Set session cookie\n cookies.set(\n sessionCookie,\n JSON.stringify({\n installationId: result.installationId,\n preferences: result.preferences\n }),\n cookieOpts\n );\n\n event.locals.installationId = currentInstallationId;\n event.locals.preferences = currentPreferences;\n }\n }\n\n // Attach to locals for every request\n if (currentInstallationId) {\n event.locals.installationId = currentInstallationId;\n event.locals.preferences = currentPreferences;\n }\n\n return resolve(event, {\n transformPageChunk: ({ html }) => {\n const prefs = event.locals.preferences;\n const mode =\n prefs && typeof prefs === 'object' && 'mode' in prefs && typeof prefs.mode === 'string'\n ? prefs.mode\n : 'dark';\n return html.replace('%mode%', mode);\n }\n });\n };\n}\n\nexport const handleArchAuth = createArchHandle;\n\n// Extend SvelteKit Locals interface\ndeclare global {\n // SvelteKit's ambient App namespace is the documented locals extension point.\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace App {\n interface Locals {\n installationId?: string;\n preferences?: {\n mode: 'light' | 'dark';\n theme: string;\n radius: string;\n styles: Record<string, string>;\n };\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,61 @@
1
1
  {
2
2
  "name": "archos-sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Client library for archOS apps: session verification, the App Bridge, and the platform API.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./sveltekit": {
16
+ "types": "./dist/sveltekit.d.ts",
17
+ "import": "./dist/sveltekit.js",
18
+ "require": "./dist/sveltekit.cjs"
19
+ },
20
+ "./app-bridge": {
21
+ "types": "./dist/app-bridge.d.ts",
22
+ "import": "./dist/app-bridge.js",
23
+ "require": "./dist/app-bridge.cjs"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md"
29
+ ],
30
+ "devDependencies": {
31
+ "@types/node": "^22.10.1",
32
+ "tsup": "^8.3.6",
33
+ "typescript": "^5.7.3",
34
+ "vitest": "^4.1.10"
35
+ },
36
+ "peerDependencies": {
37
+ "@sveltejs/kit": "^2.0.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "@sveltejs/kit": {
41
+ "optional": true
42
+ }
43
+ },
44
+ "dependencies": {
45
+ "jose": "^6.2.4"
46
+ },
47
+ "license": "SEE LICENSE IN LICENSE",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/chuksemmanuel/archos.git",
51
+ "directory": "packages/sdk"
52
+ },
53
+ "homepage": "https://github.com/chuksemmanuel/archos#readme",
54
+ "scripts": {
55
+ "build": "tsup",
56
+ "dev": "tsup --watch",
57
+ "test": "pnpm exec vitest run",
58
+ "check": "tsc --noEmit",
59
+ "lint": "prettier --check src *.ts package.json && eslint src *.ts --max-warnings 0"
60
+ }
6
61
  }