@nextclaw/app-runtime 0.7.9 → 0.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge/host-bridge.service.d.ts +2 -1
- package/dist/bridge/host-bridge.service.d.ts.map +1 -0
- package/dist/bridge/host-bridge.service.js +2 -0
- package/dist/bridge/host-bridge.service.js.map +1 -0
- package/dist/bundle/app-bundle.service.d.ts +2 -1
- package/dist/bundle/app-bundle.service.d.ts.map +1 -0
- package/dist/bundle/app-bundle.service.js +2 -0
- package/dist/bundle/app-bundle.service.js.map +1 -0
- package/dist/bundle/app-bundle.types.d.ts +2 -1
- package/dist/bundle/app-bundle.types.d.ts.map +1 -0
- package/dist/cli/app-runtime-cli.service.d.ts +2 -1
- package/dist/cli/app-runtime-cli.service.d.ts.map +1 -0
- package/dist/cli/app-runtime-cli.service.js +2 -0
- package/dist/cli/app-runtime-cli.service.js.map +1 -0
- package/dist/cli/app-runtime-options.service.d.ts +2 -1
- package/dist/cli/app-runtime-options.service.d.ts.map +1 -0
- package/dist/cli/app-runtime-options.service.js +2 -0
- package/dist/cli/app-runtime-options.service.js.map +1 -0
- package/dist/commands/create.controller.d.ts +2 -1
- package/dist/commands/create.controller.d.ts.map +1 -0
- package/dist/commands/create.controller.js +2 -0
- package/dist/commands/create.controller.js.map +1 -0
- package/dist/commands/dev.controller.js +2 -0
- package/dist/commands/dev.controller.js.map +1 -0
- package/dist/commands/grant.controller.d.ts +2 -1
- package/dist/commands/grant.controller.d.ts.map +1 -0
- package/dist/commands/grant.controller.js +2 -0
- package/dist/commands/grant.controller.js.map +1 -0
- package/dist/commands/info.controller.d.ts +2 -1
- package/dist/commands/info.controller.d.ts.map +1 -0
- package/dist/commands/info.controller.js +2 -0
- package/dist/commands/info.controller.js.map +1 -0
- package/dist/commands/inspect.controller.js +2 -0
- package/dist/commands/inspect.controller.js.map +1 -0
- package/dist/commands/install.controller.d.ts +2 -1
- package/dist/commands/install.controller.d.ts.map +1 -0
- package/dist/commands/install.controller.js +2 -0
- package/dist/commands/install.controller.js.map +1 -0
- package/dist/commands/list.controller.d.ts +2 -1
- package/dist/commands/list.controller.d.ts.map +1 -0
- package/dist/commands/list.controller.js +2 -0
- package/dist/commands/list.controller.js.map +1 -0
- package/dist/commands/pack.controller.d.ts +2 -1
- package/dist/commands/pack.controller.d.ts.map +1 -0
- package/dist/commands/pack.controller.js +2 -0
- package/dist/commands/pack.controller.js.map +1 -0
- package/dist/commands/permissions.controller.d.ts +2 -1
- package/dist/commands/permissions.controller.d.ts.map +1 -0
- package/dist/commands/permissions.controller.js +2 -0
- package/dist/commands/permissions.controller.js.map +1 -0
- package/dist/commands/publish.controller.d.ts +2 -1
- package/dist/commands/publish.controller.d.ts.map +1 -0
- package/dist/commands/publish.controller.js +2 -0
- package/dist/commands/publish.controller.js.map +1 -0
- package/dist/commands/registry.controller.d.ts +2 -1
- package/dist/commands/registry.controller.d.ts.map +1 -0
- package/dist/commands/registry.controller.js +2 -0
- package/dist/commands/registry.controller.js.map +1 -0
- package/dist/commands/revoke.controller.d.ts +2 -1
- package/dist/commands/revoke.controller.d.ts.map +1 -0
- package/dist/commands/revoke.controller.js +2 -0
- package/dist/commands/revoke.controller.js.map +1 -0
- package/dist/commands/run.controller.js +2 -0
- package/dist/commands/run.controller.js.map +1 -0
- package/dist/commands/uninstall.controller.d.ts +2 -1
- package/dist/commands/uninstall.controller.d.ts.map +1 -0
- package/dist/commands/uninstall.controller.js +2 -0
- package/dist/commands/uninstall.controller.js.map +1 -0
- package/dist/commands/update.controller.d.ts +2 -1
- package/dist/commands/update.controller.d.ts.map +1 -0
- package/dist/commands/update.controller.js +2 -0
- package/dist/commands/update.controller.js.map +1 -0
- package/dist/commands/validate-publish.controller.d.ts +2 -1
- package/dist/commands/validate-publish.controller.d.ts.map +1 -0
- package/dist/commands/validate-publish.controller.js +2 -0
- package/dist/commands/validate-publish.controller.js.map +1 -0
- package/dist/host/app-host.service.d.ts +2 -1
- package/dist/host/app-host.service.d.ts.map +1 -0
- package/dist/host/app-host.service.js +2 -0
- package/dist/host/app-host.service.js.map +1 -0
- package/dist/host/app-instance.service.d.ts +2 -1
- package/dist/host/app-instance.service.d.ts.map +1 -0
- package/dist/host/app-instance.service.js +2 -0
- package/dist/host/app-instance.service.js.map +1 -0
- package/dist/install/app-installation.service.d.ts +2 -1
- package/dist/install/app-installation.service.d.ts.map +1 -0
- package/dist/install/app-installation.service.js +2 -0
- package/dist/install/app-installation.service.js.map +1 -0
- package/dist/install/app-installation.types.d.ts +2 -1
- package/dist/install/app-installation.types.d.ts.map +1 -0
- package/dist/main.js +2 -0
- package/dist/main.js.map +1 -0
- package/dist/manifest/app-manifest.service.d.ts +2 -1
- package/dist/manifest/app-manifest.service.d.ts.map +1 -0
- package/dist/manifest/app-manifest.service.js +2 -0
- package/dist/manifest/app-manifest.service.js.map +1 -0
- package/dist/manifest/app-manifest.types.d.ts +2 -1
- package/dist/manifest/app-manifest.types.d.ts.map +1 -0
- package/dist/package.js +3 -1
- package/dist/package.js.map +1 -0
- package/dist/paths/app-home.service.d.ts +2 -1
- package/dist/paths/app-home.service.d.ts.map +1 -0
- package/dist/paths/app-home.service.js +2 -0
- package/dist/paths/app-home.service.js.map +1 -0
- package/dist/permissions/app-grant.service.d.ts +2 -1
- package/dist/permissions/app-grant.service.d.ts.map +1 -0
- package/dist/permissions/app-grant.service.js +2 -0
- package/dist/permissions/app-grant.service.js.map +1 -0
- package/dist/permissions/app-permissions.service.d.ts +2 -1
- package/dist/permissions/app-permissions.service.d.ts.map +1 -0
- package/dist/permissions/app-permissions.service.js +2 -0
- package/dist/permissions/app-permissions.service.js.map +1 -0
- package/dist/permissions/app-permissions.types.d.ts +2 -1
- package/dist/permissions/app-permissions.types.d.ts.map +1 -0
- package/dist/publish/app-marketplace-client.service.d.ts +2 -1
- package/dist/publish/app-marketplace-client.service.d.ts.map +1 -0
- package/dist/publish/app-marketplace-client.service.js +2 -0
- package/dist/publish/app-marketplace-client.service.js.map +1 -0
- package/dist/publish/app-marketplace-metadata.service.d.ts +2 -1
- package/dist/publish/app-marketplace-metadata.service.d.ts.map +1 -0
- package/dist/publish/app-marketplace-metadata.service.js +2 -0
- package/dist/publish/app-marketplace-metadata.service.js.map +1 -0
- package/dist/publish/app-publish-validation.service.d.ts +2 -1
- package/dist/publish/app-publish-validation.service.d.ts.map +1 -0
- package/dist/publish/app-publish-validation.service.js +2 -0
- package/dist/publish/app-publish-validation.service.js.map +1 -0
- package/dist/publish/app-publish.service.d.ts +2 -1
- package/dist/publish/app-publish.service.d.ts.map +1 -0
- package/dist/publish/app-publish.service.js +2 -0
- package/dist/publish/app-publish.service.js.map +1 -0
- package/dist/publish/app-publish.types.d.ts +2 -1
- package/dist/publish/app-publish.types.d.ts.map +1 -0
- package/dist/publish/app-publish.types.js +2 -0
- package/dist/publish/app-publish.types.js.map +1 -0
- package/dist/publish/platform-auth-state.service.d.ts +2 -1
- package/dist/publish/platform-auth-state.service.d.ts.map +1 -0
- package/dist/publish/platform-auth-state.service.js +2 -0
- package/dist/publish/platform-auth-state.service.js.map +1 -0
- package/dist/registry/app-registry-config.service.d.ts +2 -1
- package/dist/registry/app-registry-config.service.d.ts.map +1 -0
- package/dist/registry/app-registry-config.service.js +2 -0
- package/dist/registry/app-registry-config.service.js.map +1 -0
- package/dist/registry/app-registry.service.d.ts +2 -1
- package/dist/registry/app-registry.service.d.ts.map +1 -0
- package/dist/registry/app-registry.service.js +2 -0
- package/dist/registry/app-registry.service.js.map +1 -0
- package/dist/registry/app-registry.types.d.ts +2 -1
- package/dist/registry/app-registry.types.d.ts.map +1 -0
- package/dist/registry/app-remote-registry-client.service.d.ts +2 -1
- package/dist/registry/app-remote-registry-client.service.d.ts.map +1 -0
- package/dist/registry/app-remote-registry-client.service.js +2 -0
- package/dist/registry/app-remote-registry-client.service.js.map +1 -0
- package/dist/registry/app-remote-registry.types.d.ts +2 -1
- package/dist/registry/app-remote-registry.types.d.ts.map +1 -0
- package/dist/registry/app-remote-registry.types.js +2 -0
- package/dist/registry/app-remote-registry.types.js.map +1 -0
- package/dist/runtime/app-build.service.d.ts +2 -1
- package/dist/runtime/app-build.service.d.ts.map +1 -0
- package/dist/runtime/app-build.service.js +2 -0
- package/dist/runtime/app-build.service.js.map +1 -0
- package/dist/runtime/app-runtime-toolchain.service.d.ts +2 -1
- package/dist/runtime/app-runtime-toolchain.service.d.ts.map +1 -0
- package/dist/runtime/app-runtime-toolchain.service.js +2 -0
- package/dist/runtime/app-runtime-toolchain.service.js.map +1 -0
- package/dist/runtime/main-runner.service.d.ts +2 -1
- package/dist/runtime/main-runner.service.d.ts.map +1 -0
- package/dist/runtime/main-runner.service.js +2 -0
- package/dist/runtime/main-runner.service.js.map +1 -0
- package/dist/runtime/main-runner.types.d.ts +2 -1
- package/dist/runtime/main-runner.types.d.ts.map +1 -0
- package/dist/runtime/wasm-main-runner.service.d.ts +2 -1
- package/dist/runtime/wasm-main-runner.service.d.ts.map +1 -0
- package/dist/runtime/wasm-main-runner.service.js +2 -0
- package/dist/runtime/wasm-main-runner.service.js.map +1 -0
- package/dist/runtime/wasmtime-wasi-http-component.service.d.ts +2 -1
- package/dist/runtime/wasmtime-wasi-http-component.service.d.ts.map +1 -0
- package/dist/runtime/wasmtime-wasi-http-component.service.js +2 -0
- package/dist/runtime/wasmtime-wasi-http-component.service.js.map +1 -0
- package/dist/scaffold/app-scaffold.service.d.ts +2 -1
- package/dist/scaffold/app-scaffold.service.d.ts.map +1 -0
- package/dist/scaffold/app-scaffold.service.js +2 -0
- package/dist/scaffold/app-scaffold.service.js.map +1 -0
- package/dist/scaffold/app-ts-http-lite-scaffold-template.service.d.ts +2 -1
- package/dist/scaffold/app-ts-http-lite-scaffold-template.service.d.ts.map +1 -0
- package/dist/scaffold/app-ts-http-lite-scaffold-template.service.js +2 -0
- package/dist/scaffold/app-ts-http-lite-scaffold-template.service.js.map +1 -0
- package/dist/scaffold/app-ts-http-scaffold-template.service.d.ts +2 -1
- package/dist/scaffold/app-ts-http-scaffold-template.service.d.ts.map +1 -0
- package/dist/scaffold/app-ts-http-scaffold-template.service.js +2 -0
- package/dist/scaffold/app-ts-http-scaffold-template.service.js.map +1 -0
- package/dist/sidecar/wasm-sidecar-client.service.d.ts +2 -1
- package/dist/sidecar/wasm-sidecar-client.service.d.ts.map +1 -0
- package/dist/sidecar/wasm-sidecar-client.service.js +2 -0
- package/dist/sidecar/wasm-sidecar-client.service.js.map +1 -0
- package/dist/ui/ui-server.service.d.ts +2 -1
- package/dist/ui/ui-server.service.d.ts.map +1 -0
- package/dist/ui/ui-server.service.js +2 -0
- package/dist/ui/ui-server.service.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-publish.types.js","names":[],"sources":["../../src/publish/app-publish.types.ts"],"sourcesContent":["import type { AppDistributionMode } from \"../bundle/app-bundle.types.js\";\nimport type { AppManifest, AppPermissions } from \"../manifest/app-manifest.types.js\";\nimport type { AppPublisher } from \"../registry/app-remote-registry.types.js\";\n\nexport const DEFAULT_APP_MARKETPLACE_API_BASE = \"https://apps-registry.nextclaw.io\";\n\nexport type AppMarketplaceMetadata = {\n slug: string;\n summary: string;\n summaryI18n: Record<string, string>;\n description?: string;\n descriptionI18n?: Record<string, string>;\n author: string;\n tags: string[];\n sourceRepo?: string;\n homepage?: string;\n featured?: boolean;\n publisher?: AppPublisher;\n};\n\nexport type AppPublishFile = {\n path: string;\n contentBase64: string;\n};\n\nexport type AppPublishPayload = {\n slug: string;\n appId: string;\n name: string;\n version: string;\n summary: string;\n summaryI18n: Record<string, string>;\n description?: string;\n descriptionI18n?: Record<string, string>;\n author: string;\n tags: string[];\n sourceRepo?: string;\n homepage?: string;\n featured: boolean;\n publisher: AppPublisher;\n distributionMode: AppDistributionMode;\n manifest: AppManifest;\n permissions: AppPermissions;\n bundleBase64: string;\n bundleSha256: string;\n files: AppPublishFile[];\n};\n\nexport type AppPublishResult = {\n created: boolean;\n item: {\n slug: string;\n appId: string;\n name: string;\n latestVersion: string;\n webUrl?: string;\n install: {\n kind: \"registry\";\n spec: string;\n command: string;\n registry: string;\n };\n };\n distribution: {\n path: string;\n sha256: string;\n mode: AppDistributionMode;\n };\n fileCount: number;\n};\n"],"mappings":";AAIA,MAAa,mCAAmC"}
|
|
@@ -8,4 +8,5 @@ declare class PlatformAuthStateService {
|
|
|
8
8
|
private resolveConfigPath;
|
|
9
9
|
}
|
|
10
10
|
//#endregion
|
|
11
|
-
export { PlatformAuthStateService, PlatformPublishAuthState };
|
|
11
|
+
export { PlatformAuthStateService, PlatformPublishAuthState };
|
|
12
|
+
//# sourceMappingURL=platform-auth-state.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-auth-state.service.d.ts","names":[],"sources":["../../src/publish/platform-auth-state.service.ts"],"mappings":";KAWY,wBAAA;EACV,KAAA;EACA,UAAA;AAAA;AAAA,cAMW,wBAAA;EACX,oBAAA,QAA2B,wBAAA;EAAA,QA4BnB,iBAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-auth-state.service.js","names":[],"sources":["../../src/publish/platform-auth-state.service.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { homedir } from \"node:os\";\n\ntype NextclawConfig = {\n providers?: Record<string, {\n apiKey?: string;\n apiBase?: string | null;\n } | undefined>;\n};\n\nexport type PlatformPublishAuthState = {\n token: string | null;\n apiBaseUrl?: string;\n};\n\nconst NEXTCLAW_HOME_ENV_KEY = \"NEXTCLAW_HOME\";\nconst NEXTCLAW_DEFAULT_HOME_DIR = \".nextclaw\";\n\nexport class PlatformAuthStateService {\n readCurrentAuthState = (): PlatformPublishAuthState => {\n const configPath = this.resolveConfigPath();\n if (!existsSync(configPath)) {\n return {\n token: null,\n };\n }\n try {\n const raw = readFileSync(configPath, \"utf-8\");\n const parsed = JSON.parse(raw) as NextclawConfig;\n const provider = parsed.providers?.nextclaw;\n const token = typeof provider?.apiKey === \"string\" && provider.apiKey.trim().length > 0\n ? provider.apiKey.trim()\n : null;\n const apiBase = typeof provider?.apiBase === \"string\" && provider.apiBase.trim().length > 0\n ? provider.apiBase.trim()\n : undefined;\n return {\n token,\n apiBaseUrl: apiBase,\n };\n } catch {\n return {\n token: null,\n };\n }\n };\n\n private resolveConfigPath = (): string => {\n const nextclawHome = process.env[NEXTCLAW_HOME_ENV_KEY]?.trim();\n const dataHome = nextclawHome && nextclawHome.length > 0\n ? resolve(nextclawHome)\n : resolve(homedir(), NEXTCLAW_DEFAULT_HOME_DIR);\n return resolve(dataHome, \"config.json\");\n };\n}\n"],"mappings":";;;;AAgBA,MAAM,wBAAwB;AAC9B,MAAM,4BAA4B;AAElC,IAAa,2BAAb,MAAsC;CACpC,6BAAuD;EACrD,MAAM,aAAa,KAAK,mBAAmB;AAC3C,MAAI,CAAC,WAAW,WAAW,CACzB,QAAO,EACL,OAAO,MACR;AAEH,MAAI;GACF,MAAM,MAAM,aAAa,YAAY,QAAQ;GAE7C,MAAM,WADS,KAAK,MAAM,IAAI,CACN,WAAW;AAOnC,UAAO;IACL,OAPY,OAAO,UAAU,WAAW,YAAY,SAAS,OAAO,MAAM,CAAC,SAAS,IAClF,SAAS,OAAO,MAAM,GACtB;IAMF,YALc,OAAO,UAAU,YAAY,YAAY,SAAS,QAAQ,MAAM,CAAC,SAAS,IACtF,SAAS,QAAQ,MAAM,GACvB,KAAA;IAIH;UACK;AACN,UAAO,EACL,OAAO,MACR;;;CAIL,0BAA0C;EACxC,MAAM,eAAe,QAAQ,IAAI,wBAAwB,MAAM;AAI/D,SAAO,QAHU,gBAAgB,aAAa,SAAS,IACnD,QAAQ,aAAa,GACrB,QAAQ,SAAS,EAAE,0BAA0B,EACxB,cAAc"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-registry-config.service.d.ts","names":[],"sources":["../../src/registry/app-registry-config.service.ts"],"mappings":";;;;cAQa,wBAAA;EAAA,iBAEQ,cAAA;cAAA,cAAA,GAAgB,cAAA;EAGnC,WAAA,QAAwB,OAAA,CAAQ,yBAAA;EAwBhC,cAAA,GAAwB,WAAA,aAAsB,OAAA,CAAQ,yBAAA;EAwBtD,gBAAA,QAA6B,OAAA,CAAQ,yBAAA;EAAA,QAS7B,kBAAA;EAAA,QAiBA,WAAA;EAAA,QAwBA,oBAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-registry-config.service.js","names":[],"sources":["../../src/registry/app-registry-config.service.ts"],"sourcesContent":["import { readFile, rm, writeFile } from \"node:fs/promises\";\nimport { AppHomeService } from \"../paths/app-home.service.js\";\nimport {\n DEFAULT_APP_REGISTRY_URL,\n type AppRegistryConfig,\n type AppRegistryConfigSnapshot,\n} from \"./app-remote-registry.types.js\";\n\nexport class AppRegistryConfigService {\n constructor(\n private readonly appHomeService: AppHomeService = new AppHomeService(),\n ) {}\n\n getSnapshot = async (): Promise<AppRegistryConfigSnapshot> => {\n const envUrl = process.env.NEXTCLAW_APP_REGISTRY?.trim();\n if (envUrl) {\n return {\n defaultUrl: DEFAULT_APP_REGISTRY_URL,\n currentUrl: this.normalizeRegistryUrl(envUrl),\n source: \"env\",\n };\n }\n const config = await this.loadOptionalConfig();\n if (config) {\n return {\n defaultUrl: DEFAULT_APP_REGISTRY_URL,\n currentUrl: this.normalizeRegistryUrl(config.registry.url),\n source: \"config\",\n };\n }\n return {\n defaultUrl: DEFAULT_APP_REGISTRY_URL,\n currentUrl: DEFAULT_APP_REGISTRY_URL,\n source: \"default\",\n };\n };\n\n setRegistryUrl = async (registryUrl: string): Promise<AppRegistryConfigSnapshot> => {\n const normalized = this.normalizeRegistryUrl(registryUrl);\n await this.appHomeService.ensureBaseDirectories();\n await writeFile(\n this.appHomeService.getConfigPath(),\n `${JSON.stringify(\n {\n schemaVersion: 1,\n registry: {\n url: normalized,\n },\n } satisfies AppRegistryConfig,\n null,\n 2,\n )}\\n`,\n \"utf-8\",\n );\n return {\n defaultUrl: DEFAULT_APP_REGISTRY_URL,\n currentUrl: normalized,\n source: \"config\",\n };\n };\n\n resetRegistryUrl = async (): Promise<AppRegistryConfigSnapshot> => {\n await rm(this.appHomeService.getConfigPath(), { force: true });\n return {\n defaultUrl: DEFAULT_APP_REGISTRY_URL,\n currentUrl: DEFAULT_APP_REGISTRY_URL,\n source: \"default\",\n };\n };\n\n private loadOptionalConfig = async (): Promise<AppRegistryConfig | undefined> => {\n try {\n const raw = await readFile(this.appHomeService.getConfigPath(), \"utf-8\");\n return this.parseConfig(JSON.parse(raw) as unknown);\n } catch (error) {\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n error.code === \"ENOENT\"\n ) {\n return undefined;\n }\n throw error;\n }\n };\n\n private parseConfig = (rawConfig: unknown): AppRegistryConfig => {\n if (!rawConfig || typeof rawConfig !== \"object\" || Array.isArray(rawConfig)) {\n throw new Error(\"config.json 必须是对象。\");\n }\n const candidate = rawConfig as Record<string, unknown>;\n if (candidate.schemaVersion !== 1) {\n throw new Error(\"当前只支持 config schemaVersion = 1。\");\n }\n const registry = candidate.registry;\n if (!registry || typeof registry !== \"object\" || Array.isArray(registry)) {\n throw new Error(\"config.registry 必须是对象。\");\n }\n const registryUrl = (registry as Record<string, unknown>).url;\n if (typeof registryUrl !== \"string\" || !registryUrl.trim()) {\n throw new Error(\"config.registry.url 必须是非空字符串。\");\n }\n return {\n schemaVersion: 1,\n registry: {\n url: this.normalizeRegistryUrl(registryUrl),\n },\n };\n };\n\n private normalizeRegistryUrl = (registryUrl: string): string => {\n let normalized: URL;\n try {\n normalized = new URL(registryUrl);\n } catch {\n throw new Error(`非法 registry URL:${registryUrl}`);\n }\n if (normalized.protocol !== \"http:\" && normalized.protocol !== \"https:\") {\n throw new Error(`registry URL 只支持 http/https:${registryUrl}`);\n }\n const text = normalized.toString();\n return text.endsWith(\"/\") ? text : `${text}/`;\n };\n}\n"],"mappings":";;;;AAQA,IAAa,2BAAb,MAAsC;CACpC,YACE,iBAAkD,IAAI,gBAAgB,EACtE;AADiB,OAAA,iBAAA;;CAGnB,cAAc,YAAgD;EAC5D,MAAM,SAAS,QAAQ,IAAI,uBAAuB,MAAM;AACxD,MAAI,OACF,QAAO;GACL,YAAY;GACZ,YAAY,KAAK,qBAAqB,OAAO;GAC7C,QAAQ;GACT;EAEH,MAAM,SAAS,MAAM,KAAK,oBAAoB;AAC9C,MAAI,OACF,QAAO;GACL,YAAY;GACZ,YAAY,KAAK,qBAAqB,OAAO,SAAS,IAAI;GAC1D,QAAQ;GACT;AAEH,SAAO;GACL,YAAY;GACZ,YAAY;GACZ,QAAQ;GACT;;CAGH,iBAAiB,OAAO,gBAA4D;EAClF,MAAM,aAAa,KAAK,qBAAqB,YAAY;AACzD,QAAM,KAAK,eAAe,uBAAuB;AACjD,QAAM,UACJ,KAAK,eAAe,eAAe,EACnC,GAAG,KAAK,UACN;GACE,eAAe;GACf,UAAU,EACR,KAAK,YACN;GACF,EACD,MACA,EACD,CAAC,KACF,QACD;AACD,SAAO;GACL,YAAY;GACZ,YAAY;GACZ,QAAQ;GACT;;CAGH,mBAAmB,YAAgD;AACjE,QAAM,GAAG,KAAK,eAAe,eAAe,EAAE,EAAE,OAAO,MAAM,CAAC;AAC9D,SAAO;GACL,YAAY;GACZ,YAAY;GACZ,QAAQ;GACT;;CAGH,qBAA6B,YAAoD;AAC/E,MAAI;GACF,MAAM,MAAM,MAAM,SAAS,KAAK,eAAe,eAAe,EAAE,QAAQ;AACxE,UAAO,KAAK,YAAY,KAAK,MAAM,IAAI,CAAY;WAC5C,OAAO;AACd,OACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,MAAM,SAAS,SAEf;AAEF,SAAM;;;CAIV,eAAuB,cAA0C;AAC/D,MAAI,CAAC,aAAa,OAAO,cAAc,YAAY,MAAM,QAAQ,UAAU,CACzE,OAAM,IAAI,MAAM,qBAAqB;EAEvC,MAAM,YAAY;AAClB,MAAI,UAAU,kBAAkB,EAC9B,OAAM,IAAI,MAAM,kCAAkC;EAEpD,MAAM,WAAW,UAAU;AAC3B,MAAI,CAAC,YAAY,OAAO,aAAa,YAAY,MAAM,QAAQ,SAAS,CACtE,OAAM,IAAI,MAAM,yBAAyB;EAE3C,MAAM,cAAe,SAAqC;AAC1D,MAAI,OAAO,gBAAgB,YAAY,CAAC,YAAY,MAAM,CACxD,OAAM,IAAI,MAAM,gCAAgC;AAElD,SAAO;GACL,eAAe;GACf,UAAU,EACR,KAAK,KAAK,qBAAqB,YAAY,EAC5C;GACF;;CAGH,wBAAgC,gBAAgC;EAC9D,IAAI;AACJ,MAAI;AACF,gBAAa,IAAI,IAAI,YAAY;UAC3B;AACN,SAAM,IAAI,MAAM,mBAAmB,cAAc;;AAEnD,MAAI,WAAW,aAAa,WAAW,WAAW,aAAa,SAC7D,OAAM,IAAI,MAAM,+BAA+B,cAAc;EAE/D,MAAM,OAAO,WAAW,UAAU;AAClC,SAAO,KAAK,SAAS,IAAI,GAAG,OAAO,GAAG,KAAK"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-registry.service.d.ts","names":[],"sources":["../../src/registry/app-registry.service.ts"],"mappings":";;;;;;cAWa,kBAAA;EAAA,iBACkB,cAAA;cAAA,cAAA,GAAgB,cAAA;EAE7C,IAAA,QAAiB,OAAA,CAAQ,WAAA;EAsBzB,IAAA,GAAc,QAAA,EAAU,WAAA,KAAc,OAAA;EAStC,QAAA,QAAqB,OAAA,CAAQ,oBAAA;EAK7B,MAAA,GAAgB,KAAA,aAAgB,OAAA,CAAQ,oBAAA;EAKxC,gBAAA,GACE,KAAA,aACC,OAAA,CAAQ,2BAAA;EAQX,kBAAA,GAA4B,MAAA;IAC1B,KAAA;IACA,IAAA;IACA,WAAA;IACA,OAAA;IACA,gBAAA;IACA,aAAA;IACA,UAAA,EAAY,oBAAA;IACZ,SAAA;IACA,WAAA;IACA,gBAAA,GAAmB,2BAAA;IACnB,WAAA,EAAa,cAAA;IACb,WAAA;IACA,SAAA;IACA,MAAA;IACA,SAAA,GAAY,2BAAA;EAAA,MACV,OAAA,CAAQ,oBAAA;EAiDZ,YAAA,GACE,KAAA,UACA,MAAA,EAAQ,mBAAA,KACP,OAAA,CAAQ,oBAAA;EAeX,gBAAA,GACE,KAAA,UACA,OAAA,UACA,aAAA,aACC,OAAA,CAAQ,oBAAA;EAMX,mBAAA,GACE,KAAA,UACA,OAAA,aACC,OAAA;EAeH,SAAA,GAAmB,KAAA,aAAgB,OAAA,CAAQ,oBAAA;EAAA,QAWnC,aAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-registry.service.js","names":[],"sources":["../../src/registry/app-registry.service.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport type { AppPermissions } from \"../manifest/app-manifest.types.js\";\nimport type { AppDocumentGrantMap } from \"../permissions/app-permissions.types.js\";\nimport { AppHomeService } from \"../paths/app-home.service.js\";\nimport type {\n AppInstallSourceKind,\n AppRegistry,\n AppRegistryAppRecord,\n AppRegistryInstalledVersion,\n} from \"./app-registry.types.js\";\n\nexport class AppRegistryService {\n constructor(private readonly appHomeService: AppHomeService = new AppHomeService()) {}\n\n load = async (): Promise<AppRegistry> => {\n await this.appHomeService.ensureBaseDirectories();\n const registryPath = this.appHomeService.getRegistryPath();\n try {\n const raw = await readFile(registryPath, \"utf-8\");\n return this.parseRegistry(JSON.parse(raw) as unknown);\n } catch (error) {\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n error.code === \"ENOENT\"\n ) {\n return {\n schemaVersion: 1,\n apps: {},\n };\n }\n throw error;\n }\n };\n\n save = async (registry: AppRegistry): Promise<void> => {\n await this.appHomeService.ensureBaseDirectories();\n await writeFile(\n this.appHomeService.getRegistryPath(),\n `${JSON.stringify(registry, null, 2)}\\n`,\n \"utf-8\",\n );\n };\n\n listApps = async (): Promise<AppRegistryAppRecord[]> => {\n const registry = await this.load();\n return Object.values(registry.apps).sort((left, right) => left.appId.localeCompare(right.appId));\n };\n\n getApp = async (appId: string): Promise<AppRegistryAppRecord | undefined> => {\n const registry = await this.load();\n return registry.apps[appId];\n };\n\n getActiveVersion = async (\n appId: string,\n ): Promise<AppRegistryInstalledVersion | undefined> => {\n const appRecord = await this.getApp(appId);\n if (!appRecord) {\n return undefined;\n }\n return appRecord.installedVersions[appRecord.activeVersion];\n };\n\n upsertInstallation = async (params: {\n appId: string;\n name: string;\n description?: string;\n version: string;\n installDirectory: string;\n dataDirectory: string;\n sourceKind: AppInstallSourceKind;\n sourceRef: string;\n installedAt: string;\n distributionMode?: AppRegistryInstalledVersion[\"distributionMode\"];\n permissions: AppPermissions;\n registryUrl?: string;\n bundleUrl?: string;\n sha256?: string;\n publisher?: AppRegistryInstalledVersion[\"publisher\"];\n }): Promise<AppRegistryAppRecord> => {\n const {\n appId,\n name,\n description,\n version,\n installDirectory,\n dataDirectory,\n sourceKind,\n sourceRef,\n installedAt,\n distributionMode,\n permissions,\n registryUrl,\n bundleUrl,\n sha256,\n publisher,\n } = params;\n const registry = await this.load();\n const currentRecord = registry.apps[appId];\n const nextRecord: AppRegistryAppRecord = {\n appId,\n name,\n description,\n activeVersion: version,\n dataDirectory,\n installedVersions: {\n ...(currentRecord?.installedVersions ?? {}),\n [version]: {\n version,\n installDirectory,\n sourceKind,\n sourceRef,\n installedAt,\n distributionMode,\n permissions,\n registryUrl,\n bundleUrl,\n sha256,\n publisher,\n },\n },\n grants: currentRecord?.grants ?? {},\n };\n registry.apps[appId] = nextRecord;\n await this.save(registry);\n return nextRecord;\n };\n\n updateGrants = async (\n appId: string,\n grants: AppDocumentGrantMap,\n ): Promise<AppRegistryAppRecord> => {\n const registry = await this.load();\n const appRecord = registry.apps[appId];\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n appRecord.grants = {\n ...appRecord.grants,\n ...grants,\n };\n registry.apps[appId] = appRecord;\n await this.save(registry);\n return appRecord;\n };\n\n setDocumentGrant = async (\n appId: string,\n scopeId: string,\n directoryPath: string,\n ): Promise<AppRegistryAppRecord> => {\n return this.updateGrants(appId, {\n [scopeId]: directoryPath,\n });\n };\n\n removeDocumentGrant = async (\n appId: string,\n scopeId: string,\n ): Promise<boolean> => {\n const registry = await this.load();\n const appRecord = registry.apps[appId];\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n if (!(scopeId in appRecord.grants)) {\n return false;\n }\n delete appRecord.grants[scopeId];\n registry.apps[appId] = appRecord;\n await this.save(registry);\n return true;\n };\n\n removeApp = async (appId: string): Promise<AppRegistryAppRecord | undefined> => {\n const registry = await this.load();\n const appRecord = registry.apps[appId];\n if (!appRecord) {\n return undefined;\n }\n delete registry.apps[appId];\n await this.save(registry);\n return appRecord;\n };\n\n private parseRegistry = (rawRegistry: unknown): AppRegistry => {\n if (!rawRegistry || typeof rawRegistry !== \"object\" || Array.isArray(rawRegistry)) {\n throw new Error(\"registry.json 必须是对象。\");\n }\n const candidate = rawRegistry as Record<string, unknown>;\n if (candidate.schemaVersion !== 1) {\n throw new Error(\"当前只支持 registry schemaVersion = 1。\");\n }\n const apps = candidate.apps;\n if (!apps || typeof apps !== \"object\" || Array.isArray(apps)) {\n throw new Error(\"registry.apps 必须是对象。\");\n }\n return {\n schemaVersion: 1,\n apps: apps as Record<string, AppRegistryAppRecord>,\n };\n };\n}\n"],"mappings":";;;AAWA,IAAa,qBAAb,MAAgC;CAC9B,YAAY,iBAAkD,IAAI,gBAAgB,EAAE;AAAvD,OAAA,iBAAA;;CAE7B,OAAO,YAAkC;AACvC,QAAM,KAAK,eAAe,uBAAuB;EACjD,MAAM,eAAe,KAAK,eAAe,iBAAiB;AAC1D,MAAI;GACF,MAAM,MAAM,MAAM,SAAS,cAAc,QAAQ;AACjD,UAAO,KAAK,cAAc,KAAK,MAAM,IAAI,CAAY;WAC9C,OAAO;AACd,OACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,MAAM,SAAS,SAEf,QAAO;IACL,eAAe;IACf,MAAM,EAAE;IACT;AAEH,SAAM;;;CAIV,OAAO,OAAO,aAAyC;AACrD,QAAM,KAAK,eAAe,uBAAuB;AACjD,QAAM,UACJ,KAAK,eAAe,iBAAiB,EACrC,GAAG,KAAK,UAAU,UAAU,MAAM,EAAE,CAAC,KACrC,QACD;;CAGH,WAAW,YAA6C;EACtD,MAAM,WAAW,MAAM,KAAK,MAAM;AAClC,SAAO,OAAO,OAAO,SAAS,KAAK,CAAC,MAAM,MAAM,UAAU,KAAK,MAAM,cAAc,MAAM,MAAM,CAAC;;CAGlG,SAAS,OAAO,UAA6D;AAE3E,UADiB,MAAM,KAAK,MAAM,EAClB,KAAK;;CAGvB,mBAAmB,OACjB,UACqD;EACrD,MAAM,YAAY,MAAM,KAAK,OAAO,MAAM;AAC1C,MAAI,CAAC,UACH;AAEF,SAAO,UAAU,kBAAkB,UAAU;;CAG/C,qBAAqB,OAAO,WAgBS;EACnC,MAAM,EACJ,OACA,MACA,aACA,SACA,kBACA,eACA,YACA,WACA,aACA,kBACA,aACA,aACA,WACA,QACA,cACE;EACJ,MAAM,WAAW,MAAM,KAAK,MAAM;EAClC,MAAM,gBAAgB,SAAS,KAAK;EACpC,MAAM,aAAmC;GACvC;GACA;GACA;GACA,eAAe;GACf;GACA,mBAAmB;IACjB,GAAI,eAAe,qBAAqB,EAAE;KACzC,UAAU;KACT;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACD;IACF;GACD,QAAQ,eAAe,UAAU,EAAE;GACpC;AACD,WAAS,KAAK,SAAS;AACvB,QAAM,KAAK,KAAK,SAAS;AACzB,SAAO;;CAGT,eAAe,OACb,OACA,WACkC;EAClC,MAAM,WAAW,MAAM,KAAK,MAAM;EAClC,MAAM,YAAY,SAAS,KAAK;AAChC,MAAI,CAAC,UACH,OAAM,IAAI,MAAM,YAAY,QAAQ;AAEtC,YAAU,SAAS;GACjB,GAAG,UAAU;GACb,GAAG;GACJ;AACD,WAAS,KAAK,SAAS;AACvB,QAAM,KAAK,KAAK,SAAS;AACzB,SAAO;;CAGT,mBAAmB,OACjB,OACA,SACA,kBACkC;AAClC,SAAO,KAAK,aAAa,OAAO,GAC7B,UAAU,eACZ,CAAC;;CAGJ,sBAAsB,OACpB,OACA,YACqB;EACrB,MAAM,WAAW,MAAM,KAAK,MAAM;EAClC,MAAM,YAAY,SAAS,KAAK;AAChC,MAAI,CAAC,UACH,OAAM,IAAI,MAAM,YAAY,QAAQ;AAEtC,MAAI,EAAE,WAAW,UAAU,QACzB,QAAO;AAET,SAAO,UAAU,OAAO;AACxB,WAAS,KAAK,SAAS;AACvB,QAAM,KAAK,KAAK,SAAS;AACzB,SAAO;;CAGT,YAAY,OAAO,UAA6D;EAC9E,MAAM,WAAW,MAAM,KAAK,MAAM;EAClC,MAAM,YAAY,SAAS,KAAK;AAChC,MAAI,CAAC,UACH;AAEF,SAAO,SAAS,KAAK;AACrB,QAAM,KAAK,KAAK,SAAS;AACzB,SAAO;;CAGT,iBAAyB,gBAAsC;AAC7D,MAAI,CAAC,eAAe,OAAO,gBAAgB,YAAY,MAAM,QAAQ,YAAY,CAC/E,OAAM,IAAI,MAAM,uBAAuB;EAEzC,MAAM,YAAY;AAClB,MAAI,UAAU,kBAAkB,EAC9B,OAAM,IAAI,MAAM,oCAAoC;EAEtD,MAAM,OAAO,UAAU;AACvB,MAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,KAAK,CAC1D,OAAM,IAAI,MAAM,uBAAuB;AAEzC,SAAO;GACL,eAAe;GACT;GACP"}
|
|
@@ -32,4 +32,5 @@ type AppRegistry = {
|
|
|
32
32
|
apps: Record<string, AppRegistryAppRecord>;
|
|
33
33
|
};
|
|
34
34
|
//#endregion
|
|
35
|
-
export { AppInstallSourceKind, AppRegistry, AppRegistryAppRecord, AppRegistryInstalledVersion };
|
|
35
|
+
export { AppInstallSourceKind, AppRegistry, AppRegistryAppRecord, AppRegistryInstalledVersion };
|
|
36
|
+
//# sourceMappingURL=app-registry.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-registry.types.d.ts","names":[],"sources":["../../src/registry/app-registry.types.ts"],"mappings":";;;;;;KAKY,oBAAA;AAAA,KAEA,2BAAA;EACV,OAAA;EACA,gBAAA;EACA,UAAA,EAAY,oBAAA;EACZ,SAAA;EACA,WAAA;EACA,gBAAA,GAAmB,mBAAA;EACnB,WAAA,EAAa,cAAA;EACb,WAAA;EACA,SAAA;EACA,MAAA;EACA,SAAA,GAAY,YAAA;AAAA;AAAA,KAGF,oBAAA;EACV,KAAA;EACA,IAAA;EACA,WAAA;EACA,aAAA;EACA,aAAA;EACA,iBAAA,EAAmB,MAAA,SAAe,2BAAA;EAClC,MAAA,EAAQ,mBAAA;AAAA;AAAA,KAGE,WAAA;EACV,aAAA;EACA,IAAA,EAAM,MAAA,SAAe,oBAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-remote-registry-client.service.d.ts","names":[],"sources":["../../src/registry/app-remote-registry-client.service.ts"],"mappings":";;;;cAUa,8BAAA;EAAA,iBAEQ,aAAA;cAAA,aAAA,GAAe,wBAAA;EAGlC,OAAA,GAAiB,MAAA;IACf,KAAA;IACA,OAAA;IACA,WAAA;EAAA,MACE,OAAA,CAAQ,2BAAA;EAqCZ,cAAA,GAAwB,MAAA;IACtB,UAAA,EAAY,2BAAA;IACZ,eAAA;EAAA,MACE,OAAA;IACF,UAAA;EAAA;EAAA,QA0BM,aAAA;EAAA,QAsCA,YAAA;EAAA,QAwDA,cAAA;EAAA,QAkBA,kBAAA;EAAA,QAOA,kBAAA;EAAA,QAUA,uBAAA;EAAA,QAIA,oBAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-remote-registry-client.service.js","names":[],"sources":["../../src/registry/app-remote-registry-client.service.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppRegistryConfigService } from \"./app-registry-config.service.js\";\nimport type {\n AppRemoteRegistryDocument,\n AppRemoteRegistryResolution,\n AppRemoteRegistryVersion,\n} from \"./app-remote-registry.types.js\";\n\nexport class AppRemoteRegistryClientService {\n constructor(\n private readonly configService: AppRegistryConfigService = new AppRegistryConfigService(),\n ) {}\n\n resolve = async (params: {\n appId: string;\n version?: string;\n registryUrl?: string;\n }): Promise<AppRemoteRegistryResolution> => {\n const { appId, version: requestedVersion, registryUrl: overrideRegistryUrl } = params;\n const registryUrl = overrideRegistryUrl\n ? this.normalizeRegistryUrl(overrideRegistryUrl)\n : (await this.configService.getSnapshot()).currentUrl;\n const metadataUrl = new URL(encodeURIComponent(appId), registryUrl).toString();\n const response = await fetch(metadataUrl);\n if (!response.ok) {\n throw new Error(\n `无法从 registry 拉取 ${appId} metadata:${response.status} ${response.statusText}`,\n );\n }\n const document = this.parseDocument(await response.json(), appId);\n const version = requestedVersion ?? document[\"dist-tags\"].latest;\n const versionRecord = document.versions[version];\n if (!versionRecord) {\n throw new Error(`registry ${registryUrl} 未提供 ${appId}@${version}。`);\n }\n return {\n registryUrl,\n metadataUrl,\n appId,\n version,\n description: versionRecord.description ?? document.description,\n publisher: versionRecord.publisher,\n permissions: versionRecord.permissions,\n distributionMode: versionRecord.dist.kind === \"source\" ? \"source\" : \"bundle\",\n bundleUrl: new URL(\n versionRecord.dist.artifact ??\n versionRecord.dist.source ??\n versionRecord.dist.bundle,\n metadataUrl,\n ).toString(),\n sha256: versionRecord.dist.sha256,\n };\n };\n\n downloadBundle = async (params: {\n resolution: AppRemoteRegistryResolution;\n targetDirectory: string;\n }): Promise<{\n bundlePath: string;\n }> => {\n const { resolution, targetDirectory } = params;\n const response = await fetch(resolution.bundleUrl);\n if (!response.ok) {\n throw new Error(\n `无法下载 bundle:${resolution.bundleUrl} (${response.status} ${response.statusText})`,\n );\n }\n const bundleBytes = Buffer.from(await response.arrayBuffer());\n const actualSha256 = createHash(\"sha256\").update(bundleBytes).digest(\"hex\");\n if (actualSha256 !== resolution.sha256) {\n throw new Error(\n `bundle checksum 校验失败:期望 ${resolution.sha256},实际 ${actualSha256}`,\n );\n }\n const bundlePath = path.join(\n targetDirectory,\n `${this.normalizeBundleFileName(resolution.appId)}-${resolution.version}.napp`,\n );\n await writeFile(bundlePath, bundleBytes);\n return {\n bundlePath,\n };\n };\n\n private parseDocument = (\n rawDocument: unknown,\n appId: string,\n ): AppRemoteRegistryDocument => {\n if (!rawDocument || typeof rawDocument !== \"object\" || Array.isArray(rawDocument)) {\n throw new Error(`registry ${appId} metadata 必须是对象。`);\n }\n const candidate = rawDocument as Record<string, unknown>;\n const name = this.readRequiredString(candidate.name, \"name\");\n if (name !== appId) {\n throw new Error(`registry 返回的包名与请求不一致:请求 ${appId},收到 ${name}`);\n }\n const distTags = candidate[\"dist-tags\"];\n if (!distTags || typeof distTags !== \"object\" || Array.isArray(distTags)) {\n throw new Error(\"registry metadata 缺少 dist-tags。\");\n }\n const versions = candidate.versions;\n if (!versions || typeof versions !== \"object\" || Array.isArray(versions)) {\n throw new Error(\"registry metadata 缺少 versions。\");\n }\n return {\n name,\n description: this.readOptionalString(candidate.description, \"description\"),\n \"dist-tags\": {\n latest: this.readRequiredString(\n (distTags as Record<string, unknown>).latest,\n \"dist-tags.latest\",\n ),\n },\n versions: Object.fromEntries(\n Object.entries(versions).map(([version, rawVersion]) => [\n version,\n this.parseVersion(rawVersion, name, version),\n ]),\n ),\n };\n };\n\n private parseVersion = (\n rawVersion: unknown,\n appId: string,\n version: string,\n ): AppRemoteRegistryVersion => {\n if (!rawVersion || typeof rawVersion !== \"object\" || Array.isArray(rawVersion)) {\n throw new Error(`registry metadata 的 versions.${version} 必须是对象。`);\n }\n const candidate = rawVersion as Record<string, unknown>;\n const name = this.readRequiredString(candidate.name, `versions.${version}.name`);\n const parsedVersion = this.readRequiredString(\n candidate.version,\n `versions.${version}.version`,\n );\n if (name !== appId || parsedVersion !== version) {\n throw new Error(`registry metadata 的 versions.${version} 与 app id/version 不一致。`);\n }\n const dist = candidate.dist;\n if (!dist || typeof dist !== \"object\" || Array.isArray(dist)) {\n throw new Error(`registry metadata 的 versions.${version}.dist 必须是对象。`);\n }\n const distCandidate = dist as Record<string, unknown>;\n return {\n name,\n version: parsedVersion,\n description: this.readOptionalString(\n candidate.description,\n `versions.${version}.description`,\n ),\n publisher: this.parsePublisher(candidate.publisher, version),\n permissions: candidate.permissions as AppRemoteRegistryVersion[\"permissions\"],\n dist: {\n kind:\n distCandidate.kind === \"source\" || distCandidate.kind === \"bundle\"\n ? distCandidate.kind\n : undefined,\n artifact: this.readOptionalString(\n distCandidate.artifact,\n `versions.${version}.dist.artifact`,\n ),\n bundle: this.readRequiredString(\n distCandidate.bundle,\n `versions.${version}.dist.bundle`,\n ),\n source: this.readOptionalString(\n distCandidate.source,\n `versions.${version}.dist.source`,\n ),\n sha256: this.readRequiredString(\n distCandidate.sha256,\n `versions.${version}.dist.sha256`,\n ),\n },\n };\n };\n\n private parsePublisher = (\n rawPublisher: unknown,\n version: string,\n ): AppRemoteRegistryVersion[\"publisher\"] => {\n if (rawPublisher === undefined) {\n return undefined;\n }\n if (!rawPublisher || typeof rawPublisher !== \"object\" || Array.isArray(rawPublisher)) {\n throw new Error(`versions.${version}.publisher 必须是对象。`);\n }\n const candidate = rawPublisher as Record<string, unknown>;\n return {\n id: this.readRequiredString(candidate.id, `versions.${version}.publisher.id`),\n name: this.readRequiredString(candidate.name, `versions.${version}.publisher.name`),\n url: this.readOptionalString(candidate.url, `versions.${version}.publisher.url`),\n };\n };\n\n private readRequiredString = (value: unknown, fieldName: string): string => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${fieldName} 必须是非空字符串。`);\n }\n return value.trim();\n };\n\n private readOptionalString = (\n value: unknown,\n fieldName: string,\n ): string | undefined => {\n if (value === undefined) {\n return undefined;\n }\n return this.readRequiredString(value, fieldName);\n };\n\n private normalizeBundleFileName = (appId: string): string => {\n return appId.replace(/[^a-zA-Z0-9._-]+/g, \"-\");\n };\n\n private normalizeRegistryUrl = (registryUrl: string): string => {\n let normalized: URL;\n try {\n normalized = new URL(registryUrl);\n } catch {\n throw new Error(`非法 registry URL:${registryUrl}`);\n }\n if (normalized.protocol !== \"http:\" && normalized.protocol !== \"https:\") {\n throw new Error(`registry URL 只支持 http/https:${registryUrl}`);\n }\n const text = normalized.toString();\n return text.endsWith(\"/\") ? text : `${text}/`;\n };\n}\n"],"mappings":"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"}
|
|
@@ -54,4 +54,5 @@ type AppRemoteRegistryResolution = {
|
|
|
54
54
|
sha256: string;
|
|
55
55
|
};
|
|
56
56
|
//#endregion
|
|
57
|
-
export { AppPublisher, AppRegistryConfig, AppRegistryConfigSnapshot, AppRemoteRegistryDocument, AppRemoteRegistryResolution, AppRemoteRegistryVersion, DEFAULT_APP_REGISTRY_URL };
|
|
57
|
+
export { AppPublisher, AppRegistryConfig, AppRegistryConfigSnapshot, AppRemoteRegistryDocument, AppRemoteRegistryResolution, AppRemoteRegistryVersion, DEFAULT_APP_REGISTRY_URL };
|
|
58
|
+
//# sourceMappingURL=app-remote-registry.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-remote-registry.types.d.ts","names":[],"sources":["../../src/registry/app-remote-registry.types.ts"],"mappings":";;;;cAGa,wBAAA;AAAA,KAGD,YAAA;EACV,EAAA;EACA,IAAA;EACA,GAAA;AAAA;AAAA,KAGU,iBAAA;EACV,aAAA;EACA,QAAA;IACE,GAAA;EAAA;AAAA;AAAA,KAIQ,yBAAA;EACV,UAAA;EACA,UAAA;EACA,MAAA;AAAA;AAAA,KAGU,wBAAA;EACV,IAAA;EACA,OAAA;EACA,WAAA;EACA,SAAA,GAAY,YAAA;EACZ,WAAA,GAAc,cAAA;EACd,IAAA;IACE,IAAA,GAAO,mBAAA;IACP,QAAA;IACA,MAAA;IACA,MAAA;IACA,MAAA;EAAA;AAAA;AAAA,KAIQ,yBAAA;EACV,IAAA;EACA,WAAA;EACA,WAAA;IACE,MAAA;EAAA;EAEF,QAAA,EAAU,MAAA,SAAe,wBAAA;AAAA;AAAA,KAGf,2BAAA;EACV,WAAA;EACA,WAAA;EACA,KAAA;EACA,OAAA;EACA,WAAA;EACA,SAAA,GAAY,YAAA;EACZ,WAAA,GAAc,cAAA;EACd,gBAAA,EAAkB,mBAAA;EAClB,SAAA;EACA,MAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-remote-registry.types.js","names":[],"sources":["../../src/registry/app-remote-registry.types.ts"],"sourcesContent":["import type { AppPermissions } from \"../manifest/app-manifest.types.js\";\nimport type { AppDistributionMode } from \"../bundle/app-bundle.types.js\";\n\nexport const DEFAULT_APP_REGISTRY_URL =\n \"https://apps-registry.nextclaw.io/api/v1/apps/registry/\";\n\nexport type AppPublisher = {\n id: string;\n name: string;\n url?: string;\n};\n\nexport type AppRegistryConfig = {\n schemaVersion: 1;\n registry: {\n url: string;\n };\n};\n\nexport type AppRegistryConfigSnapshot = {\n defaultUrl: string;\n currentUrl: string;\n source: \"default\" | \"config\" | \"env\";\n};\n\nexport type AppRemoteRegistryVersion = {\n name: string;\n version: string;\n description?: string;\n publisher?: AppPublisher;\n permissions?: AppPermissions;\n dist: {\n kind?: AppDistributionMode;\n artifact?: string;\n bundle: string;\n source?: string;\n sha256: string;\n };\n};\n\nexport type AppRemoteRegistryDocument = {\n name: string;\n description?: string;\n \"dist-tags\": {\n latest: string;\n };\n versions: Record<string, AppRemoteRegistryVersion>;\n};\n\nexport type AppRemoteRegistryResolution = {\n registryUrl: string;\n metadataUrl: string;\n appId: string;\n version: string;\n description?: string;\n publisher?: AppPublisher;\n permissions?: AppPermissions;\n distributionMode: AppDistributionMode;\n bundleUrl: string;\n sha256: string;\n};\n"],"mappings":";AAGA,MAAa,2BACX"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-build.service.d.ts","names":[],"sources":["../../src/runtime/app-build.service.ts"],"mappings":";;;;KAKY,cAAA;EACV,YAAA;EACA,QAAA;EACA,aAAA;EACA,qBAAA;EACA,KAAA;EACA,aAAA;AAAA;AAAA,cAGW,eAAA;EAAA,iBAEQ,eAAA;EAAA,iBACA,gBAAA;cADA,eAAA,GAAiB,kBAAA,EACjB,gBAAA,GAAkB,0BAAA;EAGrC,KAAA,GAAe,MAAA;IACb,YAAA;IACA,OAAA;EAAA,MACE,OAAA,CAAQ,cAAA;EAAA,QAwCJ,UAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-build.service.js","names":[],"sources":["../../src/runtime/app-build.service.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppManifestService } from \"../manifest/app-manifest.service.js\";\nimport { AppRuntimeToolchainService } from \"./app-runtime-toolchain.service.js\";\n\nexport type AppBuildResult = {\n appDirectory: string;\n mainKind: string;\n mainEntryPath: string;\n installedDependencies: boolean;\n built: boolean;\n skippedReason?: string;\n};\n\nexport class AppBuildService {\n constructor(\n private readonly manifestService: AppManifestService = new AppManifestService(),\n private readonly toolchainService: AppRuntimeToolchainService = new AppRuntimeToolchainService(),\n ) {}\n\n build = async (params: {\n appDirectory: string;\n install: boolean;\n }): Promise<AppBuildResult> => {\n const appDirectory = path.resolve(params.appDirectory);\n const bundle = await this.manifestService.load(appDirectory);\n if (bundle.manifest.main.kind !== \"wasi-http-component\") {\n return {\n appDirectory,\n mainKind: bundle.manifest.main.kind,\n mainEntryPath: bundle.mainEntryPath,\n installedDependencies: false,\n built: false,\n skippedReason: \"main.kind=wasm 应用不需要 TS/WASI HTTP 构建。\",\n };\n }\n\n await this.toolchainService.assertReadyForWasiHttpBuild();\n const mainDirectory = path.join(appDirectory, \"main\");\n await access(path.join(mainDirectory, \"package.json\"));\n const shouldInstall = params.install || !(await this.pathExists(path.join(mainDirectory, \"node_modules\")));\n if (shouldInstall) {\n await this.toolchainService.runCommand({\n command: \"npm\",\n args: [\"install\"],\n cwd: mainDirectory,\n });\n }\n await this.toolchainService.runCommand({\n command: \"npm\",\n args: [\"run\", \"build\"],\n cwd: mainDirectory,\n });\n await access(bundle.mainEntryPath);\n return {\n appDirectory,\n mainKind: bundle.manifest.main.kind,\n mainEntryPath: bundle.mainEntryPath,\n installedDependencies: shouldInstall,\n built: true,\n };\n };\n\n private pathExists = async (targetPath: string): Promise<boolean> => {\n try {\n await access(targetPath);\n return true;\n } catch {\n return false;\n }\n };\n}\n"],"mappings":";;;;;AAcA,IAAa,kBAAb,MAA6B;CAC3B,YACE,kBAAuD,IAAI,oBAAoB,EAC/E,mBAAgE,IAAI,4BAA4B,EAChG;AAFiB,OAAA,kBAAA;AACA,OAAA,mBAAA;;CAGnB,QAAQ,OAAO,WAGgB;EAC7B,MAAM,eAAe,KAAK,QAAQ,OAAO,aAAa;EACtD,MAAM,SAAS,MAAM,KAAK,gBAAgB,KAAK,aAAa;AAC5D,MAAI,OAAO,SAAS,KAAK,SAAS,sBAChC,QAAO;GACL;GACA,UAAU,OAAO,SAAS,KAAK;GAC/B,eAAe,OAAO;GACtB,uBAAuB;GACvB,OAAO;GACP,eAAe;GAChB;AAGH,QAAM,KAAK,iBAAiB,6BAA6B;EACzD,MAAM,gBAAgB,KAAK,KAAK,cAAc,OAAO;AACrD,QAAM,OAAO,KAAK,KAAK,eAAe,eAAe,CAAC;EACtD,MAAM,gBAAgB,OAAO,WAAW,CAAE,MAAM,KAAK,WAAW,KAAK,KAAK,eAAe,eAAe,CAAC;AACzG,MAAI,cACF,OAAM,KAAK,iBAAiB,WAAW;GACrC,SAAS;GACT,MAAM,CAAC,UAAU;GACjB,KAAK;GACN,CAAC;AAEJ,QAAM,KAAK,iBAAiB,WAAW;GACrC,SAAS;GACT,MAAM,CAAC,OAAO,QAAQ;GACtB,KAAK;GACN,CAAC;AACF,QAAM,OAAO,OAAO,cAAc;AAClC,SAAO;GACL;GACA,UAAU,OAAO,SAAS,KAAK;GAC/B,eAAe,OAAO;GACtB,uBAAuB;GACvB,OAAO;GACR;;CAGH,aAAqB,OAAO,eAAyC;AACnE,MAAI;AACF,SAAM,OAAO,WAAW;AACxB,UAAO;UACD;AACN,UAAO"}
|
|
@@ -27,4 +27,5 @@ declare class AppRuntimeToolchainService {
|
|
|
27
27
|
private collectProcess;
|
|
28
28
|
}
|
|
29
29
|
//#endregion
|
|
30
|
-
export { AppRuntimeCommandResult, AppRuntimeDoctorResult, AppRuntimeToolStatus, AppRuntimeToolchainService };
|
|
30
|
+
export { AppRuntimeCommandResult, AppRuntimeDoctorResult, AppRuntimeToolStatus, AppRuntimeToolchainService };
|
|
31
|
+
//# sourceMappingURL=app-runtime-toolchain.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-runtime-toolchain.service.d.ts","names":[],"sources":["../../src/runtime/app-runtime-toolchain.service.ts"],"mappings":";KAGY,oBAAA;EACV,IAAA;EACA,OAAA;EACA,EAAA;EACA,OAAA;EACA,WAAA;EACA,KAAA;AAAA;AAAA,KAGU,sBAAA;EACV,EAAA;EACA,KAAA,EAAO,oBAAA;AAAA;AAAA,KAGG,uBAAA;EACV,MAAA;EACA,MAAA;AAAA;AAAA,cA+BW,0BAAA;EACX,MAAA,QAAmB,OAAA,CAAQ,sBAAA;EAQ3B,2BAAA,QAAwC,OAAA;EAcxC,UAAA,GAAoB,MAAA;IAClB,OAAA;IACA,IAAA;IACA,GAAA;EAAA,MACE,OAAA,CAAQ,uBAAA;EAAA,QASJ,SAAA;EAAA,QA0BA,cAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-runtime-toolchain.service.js","names":[],"sources":["../../src/runtime/app-runtime-toolchain.service.ts"],"sourcesContent":["import { spawn, type ChildProcessByStdio } from \"node:child_process\";\nimport type { Readable } from \"node:stream\";\n\nexport type AppRuntimeToolStatus = {\n name: string;\n command: string;\n ok: boolean;\n version?: string;\n installHint: string;\n error?: string;\n};\n\nexport type AppRuntimeDoctorResult = {\n ok: boolean;\n tools: AppRuntimeToolStatus[];\n};\n\nexport type AppRuntimeCommandResult = {\n stdout: string;\n stderr: string;\n};\n\ntype RequiredTool = {\n name: string;\n command: string;\n args: string[];\n installHint: string;\n};\n\nconst REQUIRED_TOOLS: RequiredTool[] = [\n {\n name: \"npm\",\n command: \"npm\",\n args: [\"--version\"],\n installHint: \"请安装 Node.js,或使用 NextClaw 桌面版内置运行环境。\",\n },\n {\n name: \"wasmtime\",\n command: \"wasmtime\",\n args: [\"--version\"],\n installHint: \"请安装 Wasmtime,或使用后续内置 Wasmtime 的 NextClaw 发行包。\",\n },\n {\n name: \"wkg\",\n command: \"wkg\",\n args: [\"--version\"],\n installHint: \"请安装 Bytecode Alliance wkg;有 Rust/Cargo 时可执行:cargo install wkg --locked。\",\n },\n];\n\nexport class AppRuntimeToolchainService {\n doctor = async (): Promise<AppRuntimeDoctorResult> => {\n const tools = await Promise.all(REQUIRED_TOOLS.map((tool) => this.checkTool(tool)));\n return {\n ok: tools.every((tool) => tool.ok),\n tools,\n };\n };\n\n assertReadyForWasiHttpBuild = async (): Promise<void> => {\n const result = await this.doctor();\n const missing = result.tools.filter((tool) => !tool.ok);\n if (missing.length === 0) {\n return;\n }\n throw new Error(\n [\n \"NApp WASI HTTP 开发环境尚未就绪。\",\n ...missing.map((tool) => `- 缺少 ${tool.name}: ${tool.installHint}`),\n ].join(\"\\n\"),\n );\n };\n\n runCommand = async (params: {\n command: string;\n args: string[];\n cwd: string;\n }): Promise<AppRuntimeCommandResult> => {\n const { command, args, cwd } = params;\n const child = spawn(command, args, {\n cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n return await this.collectProcess(child, command);\n };\n\n private checkTool = async (tool: RequiredTool): Promise<AppRuntimeToolStatus> => {\n try {\n const result = await this.runCommand({\n command: tool.command,\n args: tool.args,\n cwd: process.cwd(),\n });\n const version = (result.stdout || result.stderr).trim().split(/\\r?\\n/)[0]?.trim();\n return {\n name: tool.name,\n command: tool.command,\n ok: true,\n version,\n installHint: tool.installHint,\n };\n } catch (error) {\n return {\n name: tool.name,\n command: tool.command,\n ok: false,\n installHint: tool.installHint,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n };\n\n private collectProcess = async (\n child: ChildProcessByStdio<null, Readable, Readable>,\n command: string,\n ): Promise<AppRuntimeCommandResult> => {\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n child.stdout.on(\"data\", (chunk: Buffer) => {\n stdoutChunks.push(chunk);\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n stderrChunks.push(chunk);\n });\n const exitCode = await new Promise<number | null>((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"exit\", resolve);\n });\n const stdout = Buffer.concat(stdoutChunks).toString(\"utf-8\");\n const stderr = Buffer.concat(stderrChunks).toString(\"utf-8\");\n if (exitCode !== 0) {\n throw new Error(`${command} exited with ${exitCode ?? \"unknown\"}\\n${stderr || stdout}`);\n }\n return {\n stdout,\n stderr,\n };\n };\n}\n"],"mappings":";;AA6BA,MAAM,iBAAiC;CACrC;EACE,MAAM;EACN,SAAS;EACT,MAAM,CAAC,YAAY;EACnB,aAAa;EACd;CACD;EACE,MAAM;EACN,SAAS;EACT,MAAM,CAAC,YAAY;EACnB,aAAa;EACd;CACD;EACE,MAAM;EACN,SAAS;EACT,MAAM,CAAC,YAAY;EACnB,aAAa;EACd;CACF;AAED,IAAa,6BAAb,MAAwC;CACtC,SAAS,YAA6C;EACpD,MAAM,QAAQ,MAAM,QAAQ,IAAI,eAAe,KAAK,SAAS,KAAK,UAAU,KAAK,CAAC,CAAC;AACnF,SAAO;GACL,IAAI,MAAM,OAAO,SAAS,KAAK,GAAG;GAClC;GACD;;CAGH,8BAA8B,YAA2B;EAEvD,MAAM,WADS,MAAM,KAAK,QAAQ,EACX,MAAM,QAAQ,SAAS,CAAC,KAAK,GAAG;AACvD,MAAI,QAAQ,WAAW,EACrB;AAEF,QAAM,IAAI,MACR,CACE,4BACA,GAAG,QAAQ,KAAK,SAAS,QAAQ,KAAK,KAAK,IAAI,KAAK,cAAc,CACnE,CAAC,KAAK,KAAK,CACb;;CAGH,aAAa,OAAO,WAIoB;EACtC,MAAM,EAAE,SAAS,MAAM,QAAQ;EAC/B,MAAM,QAAQ,MAAM,SAAS,MAAM;GACjC;GACA,OAAO;IAAC;IAAU;IAAQ;IAAO;GAClC,CAAC;AACF,SAAO,MAAM,KAAK,eAAe,OAAO,QAAQ;;CAGlD,YAAoB,OAAO,SAAsD;AAC/E,MAAI;GACF,MAAM,SAAS,MAAM,KAAK,WAAW;IACnC,SAAS,KAAK;IACd,MAAM,KAAK;IACX,KAAK,QAAQ,KAAK;IACnB,CAAC;GACF,MAAM,WAAW,OAAO,UAAU,OAAO,QAAQ,MAAM,CAAC,MAAM,QAAQ,CAAC,IAAI,MAAM;AACjF,UAAO;IACL,MAAM,KAAK;IACX,SAAS,KAAK;IACd,IAAI;IACJ;IACA,aAAa,KAAK;IACnB;WACM,OAAO;AACd,UAAO;IACL,MAAM,KAAK;IACX,SAAS,KAAK;IACd,IAAI;IACJ,aAAa,KAAK;IAClB,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAC9D;;;CAIL,iBAAyB,OACvB,OACA,YACqC;EACrC,MAAM,eAAyB,EAAE;EACjC,MAAM,eAAyB,EAAE;AACjC,QAAM,OAAO,GAAG,SAAS,UAAkB;AACzC,gBAAa,KAAK,MAAM;IACxB;AACF,QAAM,OAAO,GAAG,SAAS,UAAkB;AACzC,gBAAa,KAAK,MAAM;IACxB;EACF,MAAM,WAAW,MAAM,IAAI,SAAwB,SAAS,WAAW;AACrE,SAAM,KAAK,SAAS,OAAO;AAC3B,SAAM,KAAK,QAAQ,QAAQ;IAC3B;EACF,MAAM,SAAS,OAAO,OAAO,aAAa,CAAC,SAAS,QAAQ;EAC5D,MAAM,SAAS,OAAO,OAAO,aAAa,CAAC,SAAS,QAAQ;AAC5D,MAAI,aAAa,EACf,OAAM,IAAI,MAAM,GAAG,QAAQ,eAAe,YAAY,UAAU,IAAI,UAAU,SAAS;AAEzF,SAAO;GACL;GACA;GACD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main-runner.service.d.ts","names":[],"sources":["../../src/runtime/main-runner.service.ts"],"mappings":";;;uBAEsB,iBAAA;EAAA,SACX,kBAAA,CAAmB,OAAA,EAAS,cAAA,GAAiB,OAAA,CAAQ,aAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main-runner.service.js","names":[],"sources":["../../src/runtime/main-runner.service.ts"],"sourcesContent":["import type { MainRunRequest, MainRunResult } from \"./main-runner.types.js\";\n\nexport abstract class MainRunnerService {\n abstract runDocumentSummary(request: MainRunRequest): Promise<MainRunResult>;\n}\n"],"mappings":";AAEA,IAAsB,oBAAtB,MAAwC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main-runner.types.d.ts","names":[],"sources":["../../src/runtime/main-runner.types.ts"],"mappings":";;;KAEY,wBAAA;EACV,aAAA;EACA,SAAA;AAAA;AAAA,KAGU,cAAA;EACV,MAAA,EAAQ,iBAAA;EACR,KAAA,EAAO,wBAAA;AAAA;AAAA,KAGG,aAAA;EACV,UAAA;EACA,MAAA;AAAA"}
|
|
@@ -9,4 +9,5 @@ declare class WasmMainRunnerService extends MainRunnerService {
|
|
|
9
9
|
runDocumentSummary: (request: MainRunRequest) => Promise<MainRunResult>;
|
|
10
10
|
}
|
|
11
11
|
//#endregion
|
|
12
|
-
export { WasmMainRunnerService };
|
|
12
|
+
export { WasmMainRunnerService };
|
|
13
|
+
//# sourceMappingURL=wasm-main-runner.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasm-main-runner.service.d.ts","names":[],"sources":["../../src/runtime/wasm-main-runner.service.ts"],"mappings":";;;;;cAIa,qBAAA,SAA8B,iBAAA;EAAA,iBAEtB,aAAA;cAAA,aAAA,GAAe,wBAAA;EAKlC,kBAAA,GAA4B,OAAA,EAAS,cAAA,KAAiB,OAAA,CAAQ,aAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasm-main-runner.service.js","names":[],"sources":["../../src/runtime/wasm-main-runner.service.ts"],"sourcesContent":["import { MainRunnerService } from \"./main-runner.service.js\";\nimport type { MainRunRequest, MainRunResult } from \"./main-runner.types.js\";\nimport { WasmSidecarClientService } from \"../sidecar/wasm-sidecar-client.service.js\";\n\nexport class WasmMainRunnerService extends MainRunnerService {\n constructor(\n private readonly sidecarClient: WasmSidecarClientService = new WasmSidecarClientService(),\n ) {\n super();\n }\n\n runDocumentSummary = async (request: MainRunRequest): Promise<MainRunResult> => {\n if (request.bundle.manifest.main.kind !== \"wasm\") {\n throw new Error(\"runDocumentSummary 只支持 main.kind=wasm。\");\n }\n const output = await this.sidecarClient.runExport({\n wasmPath: request.bundle.mainEntryPath,\n exportName: request.bundle.manifest.main.export,\n args: [request.input.documentCount, request.input.textBytes],\n });\n return {\n exportName: request.bundle.manifest.main.export,\n output,\n };\n };\n}\n"],"mappings":";;;AAIA,IAAa,wBAAb,cAA2C,kBAAkB;CAC3D,YACE,gBAA2D,IAAI,0BAA0B,EACzF;AACA,SAAO;AAFU,OAAA,gBAAA;;CAKnB,qBAAqB,OAAO,YAAoD;AAC9E,MAAI,QAAQ,OAAO,SAAS,KAAK,SAAS,OACxC,OAAM,IAAI,MAAM,yCAAyC;EAE3D,MAAM,SAAS,MAAM,KAAK,cAAc,UAAU;GAChD,UAAU,QAAQ,OAAO;GACzB,YAAY,QAAQ,OAAO,SAAS,KAAK;GACzC,MAAM,CAAC,QAAQ,MAAM,eAAe,QAAQ,MAAM,UAAU;GAC7D,CAAC;AACF,SAAO;GACL,YAAY,QAAQ,OAAO,SAAS,KAAK;GACzC;GACD"}
|
|
@@ -21,4 +21,5 @@ declare class WasmtimeWasiHttpComponentService {
|
|
|
21
21
|
private waitUntilReady;
|
|
22
22
|
}
|
|
23
23
|
//#endregion
|
|
24
|
-
export { WasmtimeWasiHttpComponentHandle, WasmtimeWasiHttpComponentService, WasmtimeWasiHttpComponentStartOptions };
|
|
24
|
+
export { WasmtimeWasiHttpComponentHandle, WasmtimeWasiHttpComponentService, WasmtimeWasiHttpComponentStartOptions };
|
|
25
|
+
//# sourceMappingURL=wasmtime-wasi-http-component.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasmtime-wasi-http-component.service.d.ts","names":[],"sources":["../../src/runtime/wasmtime-wasi-http-component.service.ts"],"mappings":";;;KAMY,qCAAA;EACV,QAAA;EACA,aAAA;AAAA;AAAA,KAGU,+BAAA;EACV,GAAA;EACA,IAAA;AAAA;AAAA,cAGW,gCAAA;EAAA,QACH,OAAA;EAAA,QACA,MAAA;EAER,KAAA,GACE,OAAA,EAAS,qCAAA,KACR,OAAA,CAAQ,+BAAA;EAqCX,IAAA,QAAiB,OAAA;EAqBjB,aAAA,GACE,OAAA,EAAS,eAAA,EACT,QAAA,EAAU,cAAA,KACT,OAAA;EAAA,QAiCK,QAAA;EAAA,QAQA,kBAAA;EAAA,QAIA,iBAAA;EAAA,QAyBA,cAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasmtime-wasi-http-component.service.js","names":[],"sources":["../../src/runtime/wasmtime-wasi-http-component.service.ts"],"sourcesContent":["import { spawn, type ChildProcessByStdio } from \"node:child_process\";\nimport { mkdir } from \"node:fs/promises\";\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport path from \"node:path\";\nimport type { Readable } from \"node:stream\";\n\nexport type WasmtimeWasiHttpComponentStartOptions = {\n wasmPath: string;\n dataDirectory: string;\n};\n\nexport type WasmtimeWasiHttpComponentHandle = {\n url: string;\n port: number;\n};\n\nexport class WasmtimeWasiHttpComponentService {\n private process?: ChildProcessByStdio<null, Readable, Readable>;\n private handle?: WasmtimeWasiHttpComponentHandle;\n\n start = async (\n options: WasmtimeWasiHttpComponentStartOptions,\n ): Promise<WasmtimeWasiHttpComponentHandle> => {\n if (this.process) {\n throw new Error(\"WASI HTTP component 已经启动。\");\n }\n const dataDirectory = path.resolve(options.dataDirectory);\n await mkdir(dataDirectory, { recursive: true });\n const port = await this.findAvailablePort();\n const child = spawn(\n \"wasmtime\",\n [\n \"serve\",\n \"-S\",\n \"cli=y\",\n \"-S\",\n \"http=y\",\n \"-S\",\n \"inherit-network=y\",\n \"--addr\",\n `127.0.0.1:${port}`,\n \"--dir\",\n `${dataDirectory}::/data`,\n options.wasmPath,\n ],\n {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n },\n );\n this.process = child;\n const handle = {\n url: `http://127.0.0.1:${port}`,\n port,\n };\n this.handle = handle;\n await this.waitUntilReady(handle.url, child);\n return handle;\n };\n\n stop = async (): Promise<void> => {\n const child = this.process;\n if (!child) {\n return;\n }\n await new Promise<void>((resolve) => {\n child.once(\"exit\", () => {\n resolve();\n });\n child.kill(\"SIGTERM\");\n setTimeout(() => {\n if (!child.killed) {\n child.kill(\"SIGKILL\");\n }\n resolve();\n }, 1_000).unref();\n });\n this.process = undefined;\n this.handle = undefined;\n };\n\n handleRequest = async (\n request: IncomingMessage,\n response: ServerResponse,\n ): Promise<boolean> => {\n const handle = this.handle;\n if (!handle) {\n return false;\n }\n const requestUrl = new URL(request.url ?? \"/\", handle.url);\n if (!requestUrl.pathname.startsWith(\"/api/\")) {\n return false;\n }\n const headers = new Headers();\n for (const [key, value] of Object.entries(request.headers)) {\n if (value === undefined || key.toLowerCase() === \"host\") {\n continue;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(key, item);\n }\n continue;\n }\n headers.set(key, value);\n }\n const body = await this.readBody(request);\n const upstreamResponse = await fetch(requestUrl, {\n method: request.method,\n headers,\n body: this.methodSupportsBody(request.method) ? body : undefined,\n });\n response.writeHead(upstreamResponse.status, Object.fromEntries(upstreamResponse.headers));\n response.end(Buffer.from(await upstreamResponse.arrayBuffer()));\n return true;\n };\n\n private readBody = async (request: IncomingMessage): Promise<Buffer> => {\n const chunks: Buffer[] = [];\n for await (const chunk of request) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks);\n };\n\n private methodSupportsBody = (method: string | undefined): boolean => {\n return method !== undefined && method !== \"GET\" && method !== \"HEAD\";\n };\n\n private findAvailablePort = async (): Promise<number> => {\n const server = createServer();\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(0, \"127.0.0.1\", () => {\n server.off(\"error\", reject);\n resolve();\n });\n });\n const address = server.address();\n await new Promise<void>((resolve, reject) => {\n server.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n if (!address || typeof address === \"string\") {\n throw new Error(\"无法分配 WASI HTTP component 端口。\");\n }\n return address.port;\n };\n\n private waitUntilReady = async (\n url: string,\n child: ChildProcessByStdio<null, Readable, Readable>,\n ): Promise<void> => {\n const errors: string[] = [];\n child.stderr.on(\"data\", (chunk: Buffer) => {\n errors.push(chunk.toString(\"utf-8\"));\n });\n for (let attempt = 0; attempt < 50; attempt += 1) {\n if (child.exitCode !== null) {\n throw new Error(\n `WASI HTTP component 启动失败:${errors.join(\"\").trim() || `exit ${child.exitCode}`}`,\n );\n }\n try {\n await fetch(url);\n return;\n } catch {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n throw new Error(`WASI HTTP component 启动超时:${errors.join(\"\").trim()}`);\n };\n}\n"],"mappings":"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"}
|
|
@@ -29,4 +29,5 @@ declare class AppScaffoldService {
|
|
|
29
29
|
private buildIconSvg;
|
|
30
30
|
}
|
|
31
31
|
//#endregion
|
|
32
|
-
export { AppScaffoldResult, AppScaffoldService, AppScaffoldTemplate };
|
|
32
|
+
export { AppScaffoldResult, AppScaffoldService, AppScaffoldTemplate };
|
|
33
|
+
//# sourceMappingURL=app-scaffold.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-scaffold.service.d.ts","names":[],"sources":["../../src/scaffold/app-scaffold.service.ts"],"mappings":";;;;KAKY,mBAAA;AAAA,KAEA,iBAAA;EACV,YAAA;EACA,YAAA;EACA,QAAA,EAAU,mBAAA;AAAA;AAAA,cAGC,kBAAA;EAAA,iBAEQ,qBAAA;EAAA,iBACA,yBAAA;cADA,qBAAA,GAAuB,gCAAA,EACvB,yBAAA,GAA2B,oCAAA;EAG9C,QAAA,GACE,eAAA,UACA,OAAA;IACE,QAAA,GAAW,mBAAA;EAAA,MAEZ,OAAA,CAAQ,iBAAA;EAAA,QAyEH,kBAAA;EAAA,QAaA,wBAAA;EAAA,QASA,UAAA;EAAA,QAKA,YAAA;EAAA,QAKA,aAAA;EAAA,QAYA,aAAA;EAAA,QA4BA,cAAA;EAAA,QAUA,WAAA;EAAA,QAyHA,wBAAA;EAAA,QAqBA,WAAA;EAAA,QAwBA,iBAAA;EAAA,QAqDA,YAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-scaffold.service.js","names":[],"sources":["../../src/scaffold/app-scaffold.service.ts"],"sourcesContent":["import { access, mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppTsHttpLiteScaffoldTemplateService } from \"./app-ts-http-lite-scaffold-template.service.js\";\nimport { AppTsHttpScaffoldTemplateService } from \"./app-ts-http-scaffold-template.service.js\";\n\nexport type AppScaffoldTemplate = \"starter\" | \"ts-http\" | \"ts-http-lite\";\n\nexport type AppScaffoldResult = {\n appDirectory: string;\n manifestPath: string;\n template: AppScaffoldTemplate;\n};\n\nexport class AppScaffoldService {\n constructor(\n private readonly tsHttpTemplateService: AppTsHttpScaffoldTemplateService = new AppTsHttpScaffoldTemplateService(),\n private readonly tsHttpLiteTemplateService: AppTsHttpLiteScaffoldTemplateService = new AppTsHttpLiteScaffoldTemplateService(),\n ) {}\n\n scaffold = async (\n targetDirectory: string,\n options?: {\n template?: AppScaffoldTemplate;\n },\n ): Promise<AppScaffoldResult> => {\n const appDirectory = path.resolve(targetDirectory);\n const template = options?.template ?? \"starter\";\n await this.assertTargetDoesNotExist(appDirectory);\n await mkdir(path.join(appDirectory, \"main\"), { recursive: true });\n await mkdir(path.join(appDirectory, \"ui\"), { recursive: true });\n await mkdir(path.join(appDirectory, \"assets\"), { recursive: true });\n\n const appName = this.buildAppName(appDirectory);\n const appId = this.buildAppId(appDirectory);\n const manifestPath = path.join(appDirectory, \"manifest.json\");\n if (template === \"ts-http\") {\n await this.writeTemplateFiles(\n appDirectory,\n this.tsHttpTemplateService.buildFiles({\n appId,\n appName,\n }),\n );\n return {\n appDirectory,\n manifestPath,\n template,\n };\n }\n if (template === \"ts-http-lite\") {\n await this.writeTemplateFiles(\n appDirectory,\n this.tsHttpLiteTemplateService.buildFiles({\n appId,\n appName,\n }),\n );\n return {\n appDirectory,\n manifestPath,\n template,\n };\n }\n\n await Promise.all([\n writeFile(\n manifestPath,\n `${JSON.stringify(this.buildManifest(appId, appName), null, 2)}\\n`,\n \"utf-8\",\n ),\n writeFile(\n path.join(appDirectory, \"marketplace.json\"),\n `${JSON.stringify(this.buildMarketplaceMetadata(appName), null, 2)}\\n`,\n \"utf-8\",\n ),\n writeFile(path.join(appDirectory, \"README.md\"), this.buildReadme(appName, appId), \"utf-8\"),\n writeFile(\n path.join(appDirectory, \"main\", \"app.wasm\"),\n Buffer.from(APP_WASM_BASE64, \"base64\"),\n ),\n writeFile(path.join(appDirectory, \"main\", \"app.wat\"), this.buildWatSource(), \"utf-8\"),\n writeFile(path.join(appDirectory, \"ui\", \"index.html\"), this.buildUiHtml(appName), \"utf-8\"),\n writeFile(\n path.join(appDirectory, \"ui\", \"app.controller.js\"),\n this.buildUiController(),\n \"utf-8\",\n ),\n writeFile(path.join(appDirectory, \"assets\", \"icon.svg\"), this.buildIconSvg(), \"utf-8\"),\n ]);\n\n return {\n appDirectory,\n manifestPath,\n template,\n };\n };\n\n private writeTemplateFiles = async (\n appDirectory: string,\n files: Array<{ relativePath: string; content: string | Buffer }>,\n ): Promise<void> => {\n await Promise.all(\n files.map(async (file) => {\n const filePath = path.join(appDirectory, file.relativePath);\n await mkdir(path.dirname(filePath), { recursive: true });\n await writeFile(filePath, file.content);\n }),\n );\n };\n\n private assertTargetDoesNotExist = async (targetDirectory: string): Promise<void> => {\n try {\n await access(targetDirectory);\n } catch {\n return;\n }\n throw new Error(`目标目录已存在:${targetDirectory}`);\n };\n\n private buildAppId = (targetDirectory: string): string => {\n const slug = this.normalizeSlug(path.basename(targetDirectory));\n return `nextclaw.${slug}`;\n };\n\n private buildAppName = (targetDirectory: string): string => {\n const parts = this.normalizeSlug(path.basename(targetDirectory)).split(\"-\");\n return parts.map((part) => `${part[0]?.toUpperCase() ?? \"\"}${part.slice(1)}`).join(\" \");\n };\n\n private normalizeSlug = (value: string): string => {\n const normalized = value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n if (!normalized) {\n throw new Error(\"应用目录名无法生成合法的应用标识。\");\n }\n return normalized;\n };\n\n private buildManifest = (appId: string, appName: string) => {\n return {\n schemaVersion: 1,\n id: appId,\n name: appName,\n version: \"0.1.0\",\n description: \"A minimal NextClaw micro app scaffold created by napp.\",\n icon: \"assets/icon.svg\",\n main: {\n kind: \"wasm\",\n entry: \"main/app.wasm\",\n export: \"summarize_notes\",\n action: \"runStarterDemo\",\n },\n ui: {\n entry: \"ui/index.html\",\n },\n permissions: {\n storage: {\n namespace: appId.replace(/\\./g, \"-\"),\n },\n capabilities: {\n hostBridge: true,\n },\n },\n };\n };\n\n private buildWatSource = (): string => {\n return `(module\n (func (export \"summarize_notes\") (param i32 i32) (result i32)\n local.get 0\n local.get 1\n i32.add\n i32.const 200\n i32.add))\\n`;\n };\n\n private buildUiHtml = (appName: string): string => {\n return `<!doctype html>\n<html lang=\"zh-CN\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${appName}</title>\n <style>\n :root {\n color-scheme: light;\n font-family: \"IBM Plex Sans\", \"Helvetica Neue\", sans-serif;\n background:\n radial-gradient(circle at top left, rgba(255, 206, 134, 0.24), transparent 30%),\n linear-gradient(165deg, #f7f1e7 0%, #fcfcfe 48%, #e1ebf5 100%);\n color: #11233a;\n }\n\n body {\n margin: 0;\n min-height: 100vh;\n }\n\n main {\n box-sizing: border-box;\n max-width: 760px;\n margin: 0 auto;\n padding: 48px 20px 72px;\n }\n\n .panel {\n background: rgba(255, 255, 255, 0.78);\n border: 1px solid rgba(17, 35, 58, 0.08);\n border-radius: 24px;\n box-shadow: 0 18px 42px rgba(17, 35, 58, 0.12);\n padding: 28px;\n backdrop-filter: blur(18px);\n }\n\n h1 {\n margin: 0 0 12px;\n font-size: clamp(30px, 6vw, 54px);\n line-height: 1;\n }\n\n p {\n line-height: 1.6;\n }\n\n button {\n border: 0;\n border-radius: 999px;\n background: #11233a;\n color: #fff;\n padding: 12px 18px;\n font-size: 15px;\n cursor: pointer;\n }\n\n dl {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 12px;\n margin: 24px 0 0;\n }\n\n dt {\n font-size: 12px;\n color: #556881;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n }\n\n dd {\n margin: 6px 0 0;\n font-size: 24px;\n font-weight: 600;\n }\n\n pre {\n margin-top: 20px;\n padding: 14px;\n border-radius: 16px;\n background: rgba(17, 35, 58, 0.07);\n overflow: auto;\n }\n </style>\n </head>\n <body>\n <main>\n <section class=\"panel\">\n <p>NextClaw Micro App</p>\n <h1 id=\"title\">Loading...</h1>\n <p id=\"description\">A starter scaffold created by napp create.</p>\n <button id=\"run-button\" type=\"button\">Run Starter Demo</button>\n <dl>\n <div>\n <dt>Action</dt>\n <dd id=\"action\">-</dd>\n </div>\n <div>\n <dt>Documents</dt>\n <dd id=\"document-count\">-</dd>\n </div>\n <div>\n <dt>Text Bytes</dt>\n <dd id=\"text-bytes\">-</dd>\n </div>\n <div>\n <dt>Wasm Score</dt>\n <dd id=\"wasm-score\">-</dd>\n </div>\n </dl>\n <pre id=\"details\"></pre>\n </section>\n </main>\n <script type=\"module\" src=\"./app.controller.js\"></script>\n </body>\n</html>\n`;\n };\n\n private buildMarketplaceMetadata = (appName: string) => {\n return {\n slug: this.normalizeSlug(appName),\n summary: `A minimal ${appName} app scaffold created by napp.`,\n summaryI18n: {\n en: `A minimal ${appName} app scaffold created by napp.`,\n zh: `一个由 napp 创建的最小 ${appName} 应用骨架。`,\n },\n description: `A minimal NextClaw app scaffold for ${appName}, ready for local run and marketplace publish.`,\n descriptionI18n: {\n en: `A minimal NextClaw app scaffold for ${appName}, ready for local run and marketplace publish.`,\n zh: `一个面向 ${appName} 的最小 NextClaw 应用骨架,可直接本地运行并发布到 marketplace。`,\n },\n author: \"NextClaw\",\n tags: [\"starter\", \"official\", \"local\"],\n sourceRepo: \"https://github.com/Peiiii/nextclaw\",\n homepage: \"https://nextclaw.io\",\n featured: false,\n };\n };\n\n private buildReadme = (appName: string, appId: string): string => {\n return `# ${appName}\n\nThis starter app was created by \\`napp create\\`.\n\n## Local workflow\n\n\\`\\`\\`bash\nnapp inspect .\nnapp run .\n\\`\\`\\`\n\n## Publish workflow\n\n\\`\\`\\`bash\nnapp publish .\n\\`\\`\\`\n\n## App ID\n\n\\`${appId}\\`\n`;\n };\n\n private buildUiController = (): string => {\n return `const titleNode = document.getElementById(\"title\");\nconst descriptionNode = document.getElementById(\"description\");\nconst actionNode = document.getElementById(\"action\");\nconst documentCountNode = document.getElementById(\"document-count\");\nconst textBytesNode = document.getElementById(\"text-bytes\");\nconst wasmScoreNode = document.getElementById(\"wasm-score\");\nconst detailsNode = document.getElementById(\"details\");\nconst runButton = document.getElementById(\"run-button\");\n\nconst setDetails = (value) => {\n detailsNode.textContent = JSON.stringify(value, null, 2);\n};\n\nconst loadManifest = async () => {\n const response = await fetch(\"/__napp/manifest\");\n const payload = await response.json();\n titleNode.textContent = payload.manifest.name;\n descriptionNode.textContent = payload.manifest.description;\n actionNode.textContent = payload.manifest.main.action;\n setDetails(payload);\n};\n\nconst runDemo = async () => {\n runButton.disabled = true;\n try {\n const response = await fetch(\"/__napp/run\", {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n action: actionNode.textContent,\n }),\n });\n const payload = await response.json();\n documentCountNode.textContent = String(payload.result.input.documentCount);\n textBytesNode.textContent = String(payload.result.input.textBytes);\n wasmScoreNode.textContent = String(payload.result.output.output);\n setDetails(payload);\n } finally {\n runButton.disabled = false;\n }\n};\n\nrunButton.addEventListener(\"click\", () => {\n void runDemo();\n});\n\nawait loadManifest();\n`;\n };\n\n private buildIconSvg = (): string => {\n return `<svg width=\"160\" height=\"160\" viewBox=\"0 0 160 160\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect width=\"160\" height=\"160\" rx=\"40\" fill=\"#11233A\" />\n <path d=\"M42 45H118V58H74V115H58V58H42V45Z\" fill=\"#F8C97E\" />\n <path d=\"M92 74H118V87H92V74Z\" fill=\"#FFFFFF\" fill-opacity=\"0.92\" />\n <path d=\"M92 95H118V108H92V95Z\" fill=\"#FFFFFF\" fill-opacity=\"0.72\" />\n</svg>\n`;\n };\n}\n\nconst APP_WASM_BASE64 =\n \"AGFzbQEAAAABBwFgAn9/AX8DAgEABxMBD3N1bW1hcml6ZV9ub3RlcwAACg0BCwAgACABakHIAWoL\";\n"],"mappings":"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"}
|
|
@@ -15,4 +15,5 @@ declare class AppTsHttpLiteScaffoldTemplateService {
|
|
|
15
15
|
private normalizeSlug;
|
|
16
16
|
}
|
|
17
17
|
//#endregion
|
|
18
|
-
export { AppTsHttpLiteScaffoldTemplateService };
|
|
18
|
+
export { AppTsHttpLiteScaffoldTemplateService };
|
|
19
|
+
//# sourceMappingURL=app-ts-http-lite-scaffold-template.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-ts-http-lite-scaffold-template.service.d.ts","names":[],"sources":["../../src/scaffold/app-ts-http-lite-scaffold-template.service.ts"],"mappings":";;;cAGa,oCAAA;EAAA,iBAEQ,uBAAA;cAAA,uBAAA,GAAyB,gCAAA;EAG5C,UAAA,GAAc,MAAA;IACZ,KAAA;IACA,OAAA;EAAA,MACE,eAAA;EAAA,QA8BI,oBAAA;EAAA,QA0BA,wBAAA;EAAA,QAqBA,WAAA;EAAA,QAuDA,oBAAA;EAAA,QAkIA,aAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-ts-http-lite-scaffold-template.service.js","names":[],"sources":["../../src/scaffold/app-ts-http-lite-scaffold-template.service.ts"],"sourcesContent":["import type { AppScaffoldFile } from \"./app-ts-http-scaffold-template.service.js\";\nimport { AppTsHttpScaffoldTemplateService } from \"./app-ts-http-scaffold-template.service.js\";\n\nexport class AppTsHttpLiteScaffoldTemplateService {\n constructor(\n private readonly standardTemplateService: AppTsHttpScaffoldTemplateService = new AppTsHttpScaffoldTemplateService(),\n ) {}\n\n buildFiles = (params: {\n appId: string;\n appName: string;\n }): AppScaffoldFile[] => {\n const standardFiles = this.standardTemplateService.buildFiles(params);\n return standardFiles.map((file) => {\n switch (file.relativePath) {\n case \"marketplace.json\":\n return {\n relativePath: file.relativePath,\n content: `${JSON.stringify(this.buildMarketplaceMetadata(params.appName), null, 2)}\\n`,\n };\n case \"README.md\":\n return {\n relativePath: file.relativePath,\n content: this.buildReadme(params.appName, params.appId),\n };\n case \"main/package.json\":\n return {\n relativePath: file.relativePath,\n content: `${JSON.stringify(this.buildMainPackageJson(params.appId), null, 2)}\\n`,\n };\n case \"main/src/component.ts\":\n return {\n relativePath: file.relativePath,\n content: this.buildComponentSource(),\n };\n default:\n return file;\n }\n });\n };\n\n private buildMainPackageJson = (appId: string) => {\n return {\n name: `${appId.replace(/\\./g, \"-\")}-main`,\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n scripts: {\n build: \"npm run prepare-wit && npm run guest-types && npm run typecheck && npm run bundle && npm run componentize\",\n \"prepare-wit\": \"wkg wit fetch\",\n \"guest-types\": \"jco guest-types wit --world-name napp-http --out-dir generated/types\",\n typecheck: \"tsc --noEmit\",\n bundle: \"rolldown -c\",\n componentize: \"jco componentize -w wit -n napp-http -o app.wasm dist/component.js\",\n },\n dependencies: {\n \"@bytecodealliance/jco-std\": \"^0.1.3\",\n },\n devDependencies: {\n \"@bytecodealliance/componentize-js\": \"^0.20.0\",\n \"@bytecodealliance/jco\": \"^1.19.0\",\n rolldown: \"^1.0.0-rc.17\",\n typescript: \"^5.6.3\",\n },\n };\n };\n\n private buildMarketplaceMetadata = (appName: string) => {\n return {\n slug: this.normalizeSlug(appName),\n summary: `A lightweight TypeScript WASI HTTP ${appName} scaffold created by napp.`,\n summaryI18n: {\n en: `A lightweight TypeScript WASI HTTP ${appName} scaffold created by napp.`,\n zh: `一个由 napp 创建的轻量 TypeScript WASI HTTP ${appName} 应用骨架。`,\n },\n description: `A lightweight TypeScript NApp scaffold for ${appName}, designed to keep the WASI HTTP backend smaller than the default Hono-based template.`,\n descriptionI18n: {\n en: `A lightweight TypeScript NApp scaffold for ${appName}, designed to keep the WASI HTTP backend smaller than the default Hono-based template.`,\n zh: `一个面向 ${appName} 的轻量 TypeScript NApp 应用骨架,用更薄的 WASI HTTP handler 替代默认的 Hono 模板,优先追求更小包体。`,\n },\n author: \"NextClaw\",\n tags: [\"starter\", \"typescript\", \"wasi-http\", \"official\", \"lite\"],\n sourceRepo: \"https://github.com/Peiiii/nextclaw\",\n homepage: \"https://nextclaw.io\",\n featured: false,\n };\n };\n\n private buildReadme = (appName: string, appId: string): string => {\n return `# ${appName}\n\nThis app was created by \\`napp create --template ts-http-lite\\`.\n\nIt keeps the existing NApp directory contract, while making \\`main/app.wasm\\` a lighter WASI HTTP component without the default Hono routing layer.\n\n## App ID\n\n\\`${appId}\\`\n\n## Build the TypeScript backend\n\nRecommended:\n\n\\`\\`\\`bash\nnapp build . --install\n\\`\\`\\`\n\nManual equivalent:\n\n\\`\\`\\`bash\ncd main\nnpm install\nnpm run build\n\\`\\`\\`\n\nThe build writes the backend component to:\n\n\\`\\`\\`text\nmain/app.wasm\n\\`\\`\\`\n\n## Local workflow\n\n\\`\\`\\`bash\nnapp inspect .\nnapp validate-publish .\nnapp run . --data ./data\n\\`\\`\\`\n\nThe runtime mounts the host data directory to guest \\`/data\\`. This example stores todos in:\n\n\\`\\`\\`text\n./data/todos.json\n\\`\\`\\`\n\nThe frontend still uses ordinary same-origin HTTP:\n\n\\`\\`\\`js\nawait fetch(\"/api/todos\");\n\\`\\`\\`\n`;\n };\n\n private buildComponentSource = (): string => {\n return `import { fire, incomingHandler } from \"@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server\";\nimport { getDirectories } from \"wasi:filesystem/preopens@0.2.2\";\n\ntype Todo = {\n id: string;\n title: string;\n completed: boolean;\n};\n\ntype TodoInput = {\n title?: string;\n};\n\nconst TODOS_FILE = \"todos.json\";\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\nconst app = {\n fetch: (request: Request): Promise<Response> => routeRequest(request),\n};\n\nfire(app as never);\n\nexport { incomingHandler };\n\nasync function routeRequest(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const pathname = url.pathname;\n\n if (request.method === \"GET\" && pathname === \"/api/todos\") {\n return json(loadTodos());\n }\n\n if (request.method === \"POST\" && pathname === \"/api/todos\") {\n const input = await request.json() as TodoInput;\n if (!input.title?.trim()) {\n return json({ error: \"title is required\" }, 400);\n }\n const todos = loadTodos();\n todos.push({\n id: crypto.randomUUID(),\n title: input.title.trim(),\n completed: false,\n });\n saveTodos(todos);\n return json(todos);\n }\n\n if (request.method === \"PATCH\" && pathname.startsWith(\"/api/todos/\")) {\n const todoId = pathname.slice(\"/api/todos/\".length);\n const input = await request.json() as Partial<Todo>;\n const todos = loadTodos();\n const todo = todos.find((entry) => entry.id === todoId);\n if (!todo) {\n return json({ error: \"todo not found\" }, 404);\n }\n if (typeof input.title === \"string\") {\n todo.title = input.title;\n }\n if (typeof input.completed === \"boolean\") {\n todo.completed = input.completed;\n }\n saveTodos(todos);\n return json(todos);\n }\n\n if (request.method === \"DELETE\" && pathname.startsWith(\"/api/todos/\")) {\n const todoId = pathname.slice(\"/api/todos/\".length);\n const nextTodos = loadTodos().filter((entry) => entry.id !== todoId);\n saveTodos(nextTodos);\n return json(nextTodos);\n }\n\n return text(\"Not found\", 404);\n}\n\nfunction json(value: unknown, status = 200): Response {\n return new Response(JSON.stringify(value), {\n status,\n headers: {\n \"content-type\": \"application/json; charset=utf-8\",\n },\n });\n}\n\nfunction text(value: string, status = 200): Response {\n return new Response(value, { status });\n}\n\nfunction loadTodos(): Todo[] {\n const file = openTodosFile({ read: true });\n if (!file) {\n return [];\n }\n const [bytes] = file.read(BigInt(1024 * 1024), BigInt(0));\n if (bytes.length === 0) {\n return [];\n }\n return JSON.parse(decoder.decode(bytes)) as Todo[];\n}\n\nfunction saveTodos(todos: Todo[]): void {\n const file = getDataDirectory().openAt(\n { symlinkFollow: true },\n TODOS_FILE,\n { create: true, truncate: true },\n { write: true },\n );\n file.write(encoder.encode(JSON.stringify(todos, null, 2)), BigInt(0));\n}\n\nfunction openTodosFile(flags: { read?: boolean; write?: boolean }) {\n try {\n return getDataDirectory().openAt({ symlinkFollow: true }, TODOS_FILE, {}, flags);\n } catch {\n return undefined;\n }\n}\n\nfunction getDataDirectory() {\n for (const [descriptor, guestPath] of getDirectories()) {\n if (guestPath === \"/data\") {\n return descriptor;\n }\n }\n throw new Error(\"Missing /data preopen. NApp runtime must mount the app data directory at /data.\");\n}\n`;\n };\n\n private normalizeSlug = (value: string): string => {\n return value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n };\n}\n"],"mappings":";;AAGA,IAAa,uCAAb,MAAkD;CAChD,YACE,0BAA6E,IAAI,kCAAkC,EACnH;AADiB,OAAA,0BAAA;;CAGnB,cAAc,WAGW;AAEvB,SADsB,KAAK,wBAAwB,WAAW,OAAO,CAChD,KAAK,SAAS;AACjC,WAAQ,KAAK,cAAb;IACE,KAAK,mBACH,QAAO;KACL,cAAc,KAAK;KACnB,SAAS,GAAG,KAAK,UAAU,KAAK,yBAAyB,OAAO,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpF;IACH,KAAK,YACH,QAAO;KACL,cAAc,KAAK;KACnB,SAAS,KAAK,YAAY,OAAO,SAAS,OAAO,MAAM;KACxD;IACH,KAAK,oBACH,QAAO;KACL,cAAc,KAAK;KACnB,SAAS,GAAG,KAAK,UAAU,KAAK,qBAAqB,OAAO,MAAM,EAAE,MAAM,EAAE,CAAC;KAC9E;IACH,KAAK,wBACH,QAAO;KACL,cAAc,KAAK;KACnB,SAAS,KAAK,sBAAsB;KACrC;IACH,QACE,QAAO;;IAEX;;CAGJ,wBAAgC,UAAkB;AAChD,SAAO;GACL,MAAM,GAAG,MAAM,QAAQ,OAAO,IAAI,CAAC;GACnC,SAAS;GACT,SAAS;GACT,MAAM;GACN,SAAS;IACP,OAAO;IACP,eAAe;IACf,eAAe;IACf,WAAW;IACX,QAAQ;IACR,cAAc;IACf;GACD,cAAc,EACZ,6BAA6B,UAC9B;GACD,iBAAiB;IACf,qCAAqC;IACrC,yBAAyB;IACzB,UAAU;IACV,YAAY;IACb;GACF;;CAGH,4BAAoC,YAAoB;AACtD,SAAO;GACL,MAAM,KAAK,cAAc,QAAQ;GACjC,SAAS,sCAAsC,QAAQ;GACvD,aAAa;IACX,IAAI,sCAAsC,QAAQ;IAClD,IAAI,uCAAuC,QAAQ;IACpD;GACD,aAAa,8CAA8C,QAAQ;GACnE,iBAAiB;IACf,IAAI,8CAA8C,QAAQ;IAC1D,IAAI,QAAQ,QAAQ;IACrB;GACD,QAAQ;GACR,MAAM;IAAC;IAAW;IAAc;IAAa;IAAY;IAAO;GAChE,YAAY;GACZ,UAAU;GACV,UAAU;GACX;;CAGH,eAAuB,SAAiB,UAA0B;AAChE,SAAO,KAAK,QAAQ;;;;;;;;IAQpB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8CR,6BAA6C;AAC3C,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiIT,iBAAyB,UAA0B;AACjD,SAAO,MACJ,MAAM,CACN,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,YAAY,GAAG"}
|
|
@@ -22,4 +22,5 @@ declare class AppTsHttpScaffoldTemplateService {
|
|
|
22
22
|
private normalizeSlug;
|
|
23
23
|
}
|
|
24
24
|
//#endregion
|
|
25
|
-
export { AppScaffoldFile, AppTsHttpScaffoldTemplateService };
|
|
25
|
+
export { AppScaffoldFile, AppTsHttpScaffoldTemplateService };
|
|
26
|
+
//# sourceMappingURL=app-ts-http-scaffold-template.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-ts-http-scaffold-template.service.d.ts","names":[],"sources":["../../src/scaffold/app-ts-http-scaffold-template.service.ts"],"mappings":";KAAY,eAAA;EACV,YAAA;EACA,OAAA,WAAkB,MAAA;AAAA;AAAA,cAGP,gCAAA;EACX,UAAA,GAAc,MAAA;IACZ,KAAA;IACA,OAAA;EAAA,MACE,eAAA;EAAA,QAsDI,aAAA;EAAA,QAkBA,oBAAA;EAAA,QA2BA,aAAA;EAAA,QAeA,wBAAA;EAAA,QAqBA,WAAA;EAAA,QA4DA,mBAAA;EAAA,QAYA,aAAA;EAAA,QAYA,oBAAA;EAAA,QA+GA,WAAA;EAAA,QA4FA,aAAA;EAAA,QAgEA,YAAA;EAAA,QAUA,aAAA;AAAA"}
|
|
@@ -494,3 +494,5 @@ void loadTodos();
|
|
|
494
494
|
const APP_WASM_PLACEHOLDER_BASE64 = "AGFzbQEAAAABBwFgAn9/AX8DAgEABxMBD3N1bW1hcml6ZV9ub3RlcwAACg0BCwAgACABakHIAWoL";
|
|
495
495
|
//#endregion
|
|
496
496
|
export { AppTsHttpScaffoldTemplateService };
|
|
497
|
+
|
|
498
|
+
//# sourceMappingURL=app-ts-http-scaffold-template.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-ts-http-scaffold-template.service.js","names":[],"sources":["../../src/scaffold/app-ts-http-scaffold-template.service.ts"],"sourcesContent":["export type AppScaffoldFile = {\n relativePath: string;\n content: string | Buffer;\n};\n\nexport class AppTsHttpScaffoldTemplateService {\n buildFiles = (params: {\n appId: string;\n appName: string;\n }): AppScaffoldFile[] => {\n const { appId, appName } = params;\n return [\n {\n relativePath: \"manifest.json\",\n content: `${JSON.stringify(this.buildManifest(appId, appName), null, 2)}\\n`,\n },\n {\n relativePath: \"marketplace.json\",\n content: `${JSON.stringify(this.buildMarketplaceMetadata(appName), null, 2)}\\n`,\n },\n {\n relativePath: \"README.md\",\n content: this.buildReadme(appName, appId),\n },\n {\n relativePath: \"main/package.json\",\n content: `${JSON.stringify(this.buildMainPackageJson(appId), null, 2)}\\n`,\n },\n {\n relativePath: \"main/tsconfig.json\",\n content: `${JSON.stringify(this.buildTsconfig(), null, 2)}\\n`,\n },\n {\n relativePath: \"main/rolldown.config.mjs\",\n content: this.buildRolldownConfig(),\n },\n {\n relativePath: \"main/wit/world.wit\",\n content: this.buildWitWorld(),\n },\n {\n relativePath: \"main/src/component.ts\",\n content: this.buildComponentSource(),\n },\n {\n relativePath: \"main/app.wasm\",\n content: Buffer.from(APP_WASM_PLACEHOLDER_BASE64, \"base64\"),\n },\n {\n relativePath: \"ui/index.html\",\n content: this.buildUiHtml(appName),\n },\n {\n relativePath: \"ui/app.js\",\n content: this.buildUiScript(),\n },\n {\n relativePath: \"assets/icon.svg\",\n content: this.buildIconSvg(),\n },\n ];\n };\n\n private buildManifest = (appId: string, appName: string) => {\n return {\n schemaVersion: 1,\n id: appId,\n name: appName,\n version: \"0.1.0\",\n description: \"A TypeScript WASI HTTP NApp scaffold created by napp.\",\n icon: \"assets/icon.svg\",\n main: {\n kind: \"wasi-http-component\",\n entry: \"main/app.wasm\",\n },\n ui: {\n entry: \"ui/index.html\",\n },\n };\n };\n\n private buildMainPackageJson = (appId: string) => {\n return {\n name: `${appId.replace(/\\./g, \"-\")}-main`,\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n scripts: {\n build: \"npm run prepare-wit && npm run guest-types && npm run typecheck && npm run bundle && npm run componentize\",\n \"prepare-wit\": \"wkg wit fetch\",\n \"guest-types\": \"jco guest-types wit --world-name napp-http --out-dir generated/types\",\n typecheck: \"tsc --noEmit\",\n bundle: \"rolldown -c\",\n componentize: \"jco componentize -w wit -n napp-http -o app.wasm dist/component.js\",\n },\n dependencies: {\n \"@bytecodealliance/jco-std\": \"^0.1.3\",\n hono: \"^4.10.0\",\n },\n devDependencies: {\n \"@bytecodealliance/componentize-js\": \"^0.20.0\",\n \"@bytecodealliance/jco\": \"^1.19.0\",\n rolldown: \"^1.0.0-rc.17\",\n typescript: \"^5.6.3\",\n },\n };\n };\n\n private buildTsconfig = () => {\n return {\n compilerOptions: {\n target: \"ES2022\",\n module: \"ESNext\",\n moduleResolution: \"Bundler\",\n strict: true,\n skipLibCheck: true,\n lib: [\"ES2022\", \"DOM\"],\n types: [\"./generated/types/interfaces/wasi-filesystem-preopens\", \"./generated/types/interfaces/wasi-filesystem-types\"],\n },\n include: [\"src/**/*.ts\"],\n };\n };\n\n private buildMarketplaceMetadata = (appName: string) => {\n return {\n slug: this.normalizeSlug(appName),\n summary: `A TypeScript WASI HTTP ${appName} scaffold created by napp.`,\n summaryI18n: {\n en: `A TypeScript WASI HTTP ${appName} scaffold created by napp.`,\n zh: `一个由 napp 创建的 TypeScript WASI HTTP ${appName} 应用骨架。`,\n },\n description: `A TypeScript NApp scaffold for ${appName}, designed for ordinary frontend-to-backend HTTP development on WASI components.`,\n descriptionI18n: {\n en: `A TypeScript NApp scaffold for ${appName}, designed for ordinary frontend-to-backend HTTP development on WASI components.`,\n zh: `一个面向 ${appName} 的 TypeScript NApp 应用骨架,用 WASI component 承载普通前后端 HTTP 开发心智。`,\n },\n author: \"NextClaw\",\n tags: [\"starter\", \"typescript\", \"wasi-http\", \"official\"],\n sourceRepo: \"https://github.com/Peiiii/nextclaw\",\n homepage: \"https://nextclaw.io\",\n featured: false,\n };\n };\n\n private buildReadme = (appName: string, appId: string): string => {\n return `# ${appName}\n\nThis app was created by \\`napp create --template ts-http\\`.\n\nIt keeps the existing NApp directory contract, while making \\`main/app.wasm\\` a WASI HTTP component.\n\n## App ID\n\n\\`${appId}\\`\n\n## Build the TypeScript backend\n\nRecommended:\n\n\\`\\`\\`bash\nnapp build . --install\n\\`\\`\\`\n\nManual equivalent:\n\n\\`\\`\\`bash\ncd main\nnpm install\nnpm run build\n\\`\\`\\`\n\nThe build writes the backend component to:\n\n\\`\\`\\`text\nmain/app.wasm\n\\`\\`\\`\n\nThe build uses the official Bytecode Alliance toolchain and expects \\`wkg\\` to be available for fetching WIT dependencies:\n\n\\`\\`\\`bash\ncargo install wkg\n\\`\\`\\`\n\n## Local workflow\n\n\\`\\`\\`bash\nnapp inspect .\nnapp run . --data ./data\n\\`\\`\\`\n\nThe runtime mounts the host data directory to guest \\`/data\\`. This example stores todos in:\n\n\\`\\`\\`text\n./data/todos.json\n\\`\\`\\`\n\nThe frontend can call the backend as ordinary same-origin HTTP:\n\n\\`\\`\\`js\nawait fetch(\"/api/todos\");\n\\`\\`\\`\n`;\n };\n\n private buildRolldownConfig = (): string => {\n return `export default {\n input: \"src/component.ts\",\n output: {\n file: \"dist/component.js\",\n format: \"esm\"\n },\n external: [/^wasi:/]\n};\n`;\n };\n\n private buildWitWorld = (): string => {\n return `package nextclaw:napp;\n\nworld napp-http {\n import wasi:filesystem/types@0.2.2;\n import wasi:filesystem/preopens@0.2.2;\n\n export wasi:http/incoming-handler@0.2.6;\n}\n`;\n };\n\n private buildComponentSource = (): string => {\n return `import { Hono } from \"hono\";\nimport { fire } from \"@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server\";\nimport { getDirectories } from \"wasi:filesystem/preopens@0.2.2\";\n\ntype Todo = {\n id: string;\n title: string;\n completed: boolean;\n};\n\ntype TodoInput = {\n title?: string;\n};\n\nconst TODOS_FILE = \"todos.json\";\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\nconst app = new Hono();\n\napp.get(\"/api/todos\", (context) => {\n return context.json(loadTodos());\n});\n\napp.post(\"/api/todos\", async (context) => {\n const input = await context.req.json<TodoInput>();\n if (!input.title?.trim()) {\n return context.json({ error: \"title is required\" }, 400);\n }\n\n const todos = loadTodos();\n todos.push({\n id: crypto.randomUUID(),\n title: input.title.trim(),\n completed: false,\n });\n saveTodos(todos);\n return context.json(todos);\n});\n\napp.patch(\"/api/todos/:id\", async (context) => {\n const input = await context.req.json<Partial<Todo>>();\n const todos = loadTodos();\n const todo = todos.find((entry) => entry.id === context.req.param(\"id\"));\n if (!todo) {\n return context.json({ error: \"todo not found\" }, 404);\n }\n\n if (typeof input.title === \"string\") {\n todo.title = input.title;\n }\n if (typeof input.completed === \"boolean\") {\n todo.completed = input.completed;\n }\n saveTodos(todos);\n return context.json(todos);\n});\n\napp.delete(\"/api/todos/:id\", (context) => {\n const nextTodos = loadTodos().filter((entry) => entry.id !== context.req.param(\"id\"));\n saveTodos(nextTodos);\n return context.json(nextTodos);\n});\n\napp.notFound((context) => context.text(\"Not found\", 404));\n\nfire(app);\n\nexport { incomingHandler } from \"@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server\";\n\nfunction loadTodos(): Todo[] {\n const file = openTodosFile({ read: true });\n if (!file) {\n return [];\n }\n const [bytes] = file.read(BigInt(1024 * 1024), BigInt(0));\n if (bytes.length === 0) {\n return [];\n }\n return JSON.parse(decoder.decode(bytes)) as Todo[];\n}\n\nfunction saveTodos(todos: Todo[]): void {\n const file = getDataDirectory().openAt(\n { symlinkFollow: true },\n TODOS_FILE,\n { create: true, truncate: true },\n { write: true },\n );\n file.write(encoder.encode(JSON.stringify(todos, null, 2)), BigInt(0));\n}\n\nfunction openTodosFile(flags: { read?: boolean; write?: boolean }) {\n try {\n return getDataDirectory().openAt({ symlinkFollow: true }, TODOS_FILE, {}, flags);\n } catch {\n return undefined;\n }\n}\n\nfunction getDataDirectory() {\n for (const [descriptor, guestPath] of getDirectories()) {\n if (guestPath === \"/data\") {\n return descriptor;\n }\n }\n throw new Error(\"Missing /data preopen. NApp runtime must mount the app data directory at /data.\");\n}\n`;\n };\n\n private buildUiHtml = (appName: string): string => {\n return `<!doctype html>\n<html lang=\"zh-CN\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${appName}</title>\n <style>\n :root {\n color-scheme: light;\n font-family: \"IBM Plex Sans\", \"Helvetica Neue\", sans-serif;\n background: linear-gradient(135deg, #f4efe6 0%, #eff6f9 50%, #f8fafc 100%);\n color: #13212f;\n }\n\n body {\n margin: 0;\n min-height: 100vh;\n }\n\n main {\n box-sizing: border-box;\n max-width: 720px;\n margin: 0 auto;\n padding: 48px 20px;\n }\n\n .panel {\n border: 1px solid rgba(19, 33, 47, 0.1);\n border-radius: 24px;\n background: rgba(255, 255, 255, 0.82);\n box-shadow: 0 24px 70px rgba(19, 33, 47, 0.14);\n padding: 28px;\n }\n\n h1 {\n margin: 0 0 10px;\n font-size: clamp(32px, 7vw, 58px);\n letter-spacing: -0.06em;\n }\n\n form {\n display: flex;\n gap: 10px;\n margin: 24px 0;\n }\n\n input {\n flex: 1;\n border: 1px solid rgba(19, 33, 47, 0.18);\n border-radius: 999px;\n padding: 12px 14px;\n font-size: 15px;\n }\n\n button {\n border: 0;\n border-radius: 999px;\n background: #13212f;\n color: white;\n padding: 12px 18px;\n cursor: pointer;\n }\n\n li {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 0;\n border-top: 1px solid rgba(19, 33, 47, 0.1);\n }\n </style>\n </head>\n <body>\n <main>\n <section class=\"panel\">\n <p>TypeScript WASI HTTP NApp</p>\n <h1>${appName}</h1>\n <form id=\"todo-form\">\n <input id=\"todo-title\" type=\"text\" placeholder=\"Add a todo\" />\n <button type=\"submit\">Add</button>\n </form>\n <ul id=\"todos\"></ul>\n </section>\n </main>\n <script type=\"module\" src=\"./app.js\"></script>\n </body>\n</html>\n`;\n };\n\n private buildUiScript = (): string => {\n return `const form = document.getElementById(\"todo-form\");\nconst input = document.getElementById(\"todo-title\");\nconst list = document.getElementById(\"todos\");\n\nasync function loadTodos() {\n const response = await fetch(\"/api/todos\");\n render(await response.json());\n}\n\nasync function addTodo(title) {\n const response = await fetch(\"/api/todos\", {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\"\n },\n body: JSON.stringify({ title })\n });\n render(await response.json());\n}\n\nasync function toggleTodo(todo) {\n const response = await fetch(\\`/api/todos/\\${todo.id}\\`, {\n method: \"PATCH\",\n headers: {\n \"content-type\": \"application/json\"\n },\n body: JSON.stringify({ completed: !todo.completed })\n });\n render(await response.json());\n}\n\nfunction render(todos) {\n list.replaceChildren(\n ...todos.map((todo) => {\n const item = document.createElement(\"li\");\n const label = document.createElement(\"span\");\n label.textContent = todo.completed ? \\`✓ \\${todo.title}\\` : todo.title;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = todo.completed ? \"Reopen\" : \"Done\";\n button.addEventListener(\"click\", () => {\n void toggleTodo(todo);\n });\n item.append(label, button);\n return item;\n })\n );\n}\n\nform.addEventListener(\"submit\", (event) => {\n event.preventDefault();\n const title = input.value.trim();\n if (!title) {\n return;\n }\n input.value = \"\";\n void addTodo(title);\n});\n\nvoid loadTodos();\n`;\n };\n\n private buildIconSvg = (): string => {\n return `<svg width=\"160\" height=\"160\" viewBox=\"0 0 160 160\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect width=\"160\" height=\"160\" rx=\"38\" fill=\"#13212F\" />\n <path d=\"M44 49H116V63H44V49Z\" fill=\"#F2C879\" />\n <path d=\"M44 76H116V90H44V76Z\" fill=\"#F7F4EC\" />\n <path d=\"M44 103H92V117H44V103Z\" fill=\"#8FD3C7\" />\n</svg>\n`;\n };\n\n private normalizeSlug = (value: string): string => {\n return value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n };\n}\n\nconst APP_WASM_PLACEHOLDER_BASE64 =\n \"AGFzbQEAAAABBwFgAn9/AX8DAgEABxMBD3N1bW1hcml6ZV9ub3RlcwAACg0BCwAgACABakHIAWoL\";\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasm-sidecar-client.service.d.ts","names":[],"sources":["../../src/sidecar/wasm-sidecar-client.service.ts"],"mappings":";cAaa,wBAAA;EACX,SAAA,GAAmB,MAAA;IACjB,QAAA;IACA,UAAA;IACA,IAAA;EAAA,MACE,OAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasm-sidecar-client.service.js","names":[],"sources":["../../src/sidecar/wasm-sidecar-client.service.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\n\ntype WasmRuntime = {\n instantiate(\n wasmBytes: Buffer,\n imports: Record<string, unknown>,\n ): Promise<{\n instance: {\n exports: Record<string, unknown>;\n };\n }>;\n};\n\nexport class WasmSidecarClientService {\n runExport = async (params: {\n wasmPath: string;\n exportName: string;\n args: number[];\n }): Promise<number> => {\n const { wasmPath, exportName, args } = params;\n const wasmBytes = await readFile(wasmPath);\n const wasmRuntime = (globalThis as typeof globalThis & { WebAssembly?: WasmRuntime })\n .WebAssembly;\n if (!wasmRuntime) {\n throw new Error(\"当前 Node 运行时不支持 WebAssembly。\");\n }\n const instance = await wasmRuntime.instantiate(wasmBytes, {});\n const exported = instance.instance.exports[exportName];\n if (typeof exported !== \"function\") {\n throw new Error(`Wasm 导出不存在或不可调用:${exportName}`);\n }\n const result = exported(...args);\n if (typeof result !== \"number\") {\n throw new Error(`Wasm 导出 ${exportName} 必须返回 number。`);\n }\n return result;\n };\n}\n"],"mappings":";;AAaA,IAAa,2BAAb,MAAsC;CACpC,YAAY,OAAO,WAII;EACrB,MAAM,EAAE,UAAU,YAAY,SAAS;EACvC,MAAM,YAAY,MAAM,SAAS,SAAS;EAC1C,MAAM,cAAe,WAClB;AACH,MAAI,CAAC,YACH,OAAM,IAAI,MAAM,8BAA8B;EAGhD,MAAM,YADW,MAAM,YAAY,YAAY,WAAW,EAAE,CAAC,EACnC,SAAS,QAAQ;AAC3C,MAAI,OAAO,aAAa,WACtB,OAAM,IAAI,MAAM,mBAAmB,aAAa;EAElD,MAAM,SAAS,SAAS,GAAG,KAAK;AAChC,MAAI,OAAO,WAAW,SACpB,OAAM,IAAI,MAAM,WAAW,WAAW,eAAe;AAEvD,SAAO"}
|