refactor(stage-tamagotchi): better formatting and naming
This commit is contained in:
+14
-14
@@ -1,10 +1,10 @@
|
||||
import type { StaticAssetService } from '../../../http-server/static-assets'
|
||||
import type { StaticAssetSession } from '../../../http-server/static-assets/types'
|
||||
import type { PluginAssetCookie, PluginAssetCookieAdapter } from './index'
|
||||
import type { ExtensionAssetCookie, ExtensionAssetCookieAdapter } from './index'
|
||||
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { createPluginAssetService } from './index'
|
||||
import { createExtensionAssetService } from './index'
|
||||
|
||||
const mockState = vi.hoisted(() => ({
|
||||
createStaticAssetService: vi.fn(),
|
||||
@@ -45,8 +45,8 @@ function createFakeServer(options: {
|
||||
}
|
||||
|
||||
function createFakeCookieAdapter() {
|
||||
const setCookies: PluginAssetCookie[] = []
|
||||
const removedCookies: PluginAssetCookie[] = []
|
||||
const setCookies: ExtensionAssetCookie[] = []
|
||||
const removedCookies: ExtensionAssetCookie[] = []
|
||||
|
||||
return {
|
||||
adapter: {
|
||||
@@ -56,13 +56,13 @@ function createFakeCookieAdapter() {
|
||||
removeCookie: vi.fn(async (cookie) => {
|
||||
removedCookies.push(cookie)
|
||||
}),
|
||||
} satisfies PluginAssetCookieAdapter,
|
||||
} satisfies ExtensionAssetCookieAdapter,
|
||||
removedCookies,
|
||||
setCookies,
|
||||
}
|
||||
}
|
||||
|
||||
describe('createPluginAssetService', () => {
|
||||
describe('createExtensionAssetService', () => {
|
||||
beforeEach(() => {
|
||||
mockState.createStaticAssetService.mockReset()
|
||||
})
|
||||
@@ -75,7 +75,7 @@ describe('createPluginAssetService', () => {
|
||||
const { adapter, setCookies } = createFakeCookieAdapter()
|
||||
mockState.createStaticAssetService.mockReturnValue(server)
|
||||
|
||||
const service = createPluginAssetService({
|
||||
const service = createExtensionAssetService({
|
||||
getManifestEntryByName: () => new Map(),
|
||||
cookieAdapter: adapter,
|
||||
})
|
||||
@@ -122,7 +122,7 @@ describe('createPluginAssetService', () => {
|
||||
const { adapter, setCookies } = createFakeCookieAdapter()
|
||||
mockState.createStaticAssetService.mockReturnValue(server)
|
||||
|
||||
const service = createPluginAssetService({
|
||||
const service = createExtensionAssetService({
|
||||
getManifestEntryByName: () => new Map(),
|
||||
cookieAdapter: adapter,
|
||||
})
|
||||
@@ -134,7 +134,7 @@ describe('createPluginAssetService', () => {
|
||||
routeAssetPath: 'assets/app.js',
|
||||
pathPrefix: 'assets/',
|
||||
ttlMs: 60_000,
|
||||
})).rejects.toThrow('Plugin asset server base URL is unavailable')
|
||||
})).rejects.toThrow('Extension asset server base URL is unavailable')
|
||||
|
||||
expect(server.revokeSession).toHaveBeenCalledWith('asset-session-2')
|
||||
expect(adapter.setCookie).not.toHaveBeenCalled()
|
||||
@@ -148,7 +148,7 @@ describe('createPluginAssetService', () => {
|
||||
})
|
||||
const { adapter } = createFakeCookieAdapter()
|
||||
mockState.createStaticAssetService.mockReturnValue(server)
|
||||
const service = createPluginAssetService({
|
||||
const service = createExtensionAssetService({
|
||||
getManifestEntryByName: () => new Map(),
|
||||
cookieAdapter: adapter,
|
||||
})
|
||||
@@ -160,7 +160,7 @@ describe('createPluginAssetService', () => {
|
||||
routeAssetPath: '../secret.txt',
|
||||
pathPrefix: '',
|
||||
ttlMs: 60_000,
|
||||
})).rejects.toThrow('Plugin asset session routeAssetPath must be a safe plugin asset path')
|
||||
})).rejects.toThrow('Extension asset session routeAssetPath must be a safe extension asset path')
|
||||
|
||||
expect(server.revokeSession).toHaveBeenCalledWith('asset-session-3')
|
||||
expect(adapter.setCookie).not.toHaveBeenCalled()
|
||||
@@ -195,14 +195,14 @@ describe('createPluginAssetService', () => {
|
||||
const { adapter, removedCookies } = createFakeCookieAdapter()
|
||||
mockState.createStaticAssetService.mockReturnValue(server)
|
||||
|
||||
const service = createPluginAssetService({
|
||||
const service = createExtensionAssetService({
|
||||
getManifestEntryByName: () => new Map(),
|
||||
cookieAdapter: adapter,
|
||||
})
|
||||
|
||||
await service.revokeSession('direct-asset-session')
|
||||
await service.revokeByOwnerSessionId('owner-session-1')
|
||||
await service.revokeByPluginId('airi-plugin-game-chess')
|
||||
await service.revokeByExtensionId('airi-plugin-game-chess')
|
||||
await service.revokeAll()
|
||||
|
||||
expect(server.revokeSession).toHaveBeenCalledWith('direct-asset-session')
|
||||
@@ -251,7 +251,7 @@ describe('createPluginAssetService', () => {
|
||||
const { adapter, removedCookies } = createFakeCookieAdapter()
|
||||
mockState.createStaticAssetService.mockReturnValue(server)
|
||||
|
||||
const service = createPluginAssetService({
|
||||
const service = createExtensionAssetService({
|
||||
getManifestEntryByName: () => new Map(),
|
||||
cookieAdapter: adapter,
|
||||
})
|
||||
|
||||
+43
-49
@@ -6,11 +6,11 @@ import { createStaticAssetService } from '../../../http-server/static-assets'
|
||||
import { buildMountedStaticAssetPath } from '../../../http-server/static-assets/paths'
|
||||
|
||||
/**
|
||||
* Describes one plugin asset session creation request.
|
||||
* Describes one extension asset session creation request.
|
||||
*
|
||||
* Use when:
|
||||
* - A extension-owned asset URL must be mounted behind the local loopback server with cookie auth
|
||||
* - Snapshot builders need a transport-agnostic way to authorize one plugin asset route before iframe load
|
||||
* - An extension-owned asset URL must be mounted behind the local loopback server with cookie auth
|
||||
* - Snapshot builders need a transport-agnostic way to authorize one extension asset route before iframe load
|
||||
*
|
||||
* Expects:
|
||||
* - `pluginId` matches a manifest entry registered in the asset host
|
||||
@@ -20,10 +20,10 @@ import { buildMountedStaticAssetPath } from '../../../http-server/static-assets/
|
||||
* Returns:
|
||||
* - N/A
|
||||
*/
|
||||
export interface PluginAssetSessionInput {
|
||||
/** Plugin id that owns the static asset root. */
|
||||
export interface ExtensionAssetSessionInput {
|
||||
/** Extension/plugin manifest id that owns the static asset root. */
|
||||
pluginId: string
|
||||
/** Plugin version expected by the server-side session validator. */
|
||||
/** Extension/plugin version expected by the server-side session validator. */
|
||||
version: string
|
||||
/** Parent extension session id used for owner-scoped revocation. */
|
||||
ownerSessionId: string
|
||||
@@ -36,7 +36,7 @@ export interface PluginAssetSessionInput {
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the cookie material Electron must apply before loading a plugin asset URL.
|
||||
* Describes the cookie material Electron must apply before loading an extension asset URL.
|
||||
*
|
||||
* Use when:
|
||||
* - Main process bridges server-side asset sessions into Electron's cookie jar
|
||||
@@ -49,7 +49,7 @@ export interface PluginAssetSessionInput {
|
||||
* Returns:
|
||||
* - N/A
|
||||
*/
|
||||
export interface PluginAssetCookie {
|
||||
export interface ExtensionAssetCookie {
|
||||
/** Cookie name generated for the asset session. */
|
||||
name: string
|
||||
/** Opaque cookie value required by the static asset route. */
|
||||
@@ -63,7 +63,7 @@ export interface PluginAssetCookie {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies and removes plugin asset cookies from the Electron host.
|
||||
* Applies and removes extension asset cookies from the Electron host.
|
||||
*
|
||||
* Use when:
|
||||
* - Asset sessions must exist in Electron's cookie jar before an iframe navigates to its URL
|
||||
@@ -76,13 +76,13 @@ export interface PluginAssetCookie {
|
||||
* Returns:
|
||||
* - N/A
|
||||
*/
|
||||
export interface PluginAssetCookieAdapter {
|
||||
setCookie: (cookie: PluginAssetCookie) => Promise<void>
|
||||
removeCookie: (cookie: PluginAssetCookie) => Promise<void>
|
||||
export interface ExtensionAssetCookieAdapter {
|
||||
setCookie: (cookie: ExtensionAssetCookie) => Promise<void>
|
||||
removeCookie: (cookie: ExtensionAssetCookie) => Promise<void>
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the plugin asset methods needed while building renderer-facing snapshots.
|
||||
* Describes the extension asset methods needed while building renderer-facing snapshots.
|
||||
*
|
||||
* Use when:
|
||||
* - Snapshot builders must request route-scoped asset sessions without depending on HTTP server internals
|
||||
@@ -95,13 +95,13 @@ export interface PluginAssetCookieAdapter {
|
||||
* Returns:
|
||||
* - A mounted asset URL and cookie-backed session metadata
|
||||
*/
|
||||
export interface PluginAssetSnapshotService {
|
||||
export interface ExtensionAssetSnapshotService {
|
||||
getBaseUrl: () => string | undefined
|
||||
createAssetSession: (input: Omit<PluginAssetSessionInput, 'ttlMs'>) => Promise<PluginAssetSession>
|
||||
createAssetSession: (input: Omit<ExtensionAssetSessionInput, 'ttlMs'>) => Promise<ExtensionAssetSession>
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a plugin asset session prepared for renderer iframe navigation.
|
||||
* Describes an extension asset session prepared for renderer iframe navigation.
|
||||
*
|
||||
* Use when:
|
||||
* - A plugin iframe needs a mounted static asset URL and pre-applied cookie state
|
||||
@@ -113,13 +113,13 @@ export interface PluginAssetSnapshotService {
|
||||
* Returns:
|
||||
* - Renderer-facing URL plus server and cookie metadata
|
||||
*/
|
||||
export interface PluginAssetSession {
|
||||
export interface ExtensionAssetSession {
|
||||
/** Absolute mounted asset URL safe to hand to a renderer iframe after cookie setup. */
|
||||
url: string
|
||||
/** Opaque server-side asset session id embedded in mounted asset routes. */
|
||||
assetSessionId: string
|
||||
/** Cookie data that was applied through the host adapter. */
|
||||
cookie: PluginAssetCookie
|
||||
cookie: ExtensionAssetCookie
|
||||
/** Unix timestamp in milliseconds when the cookie-backed asset session expires. */
|
||||
expiresAt: number
|
||||
}
|
||||
@@ -129,24 +129,24 @@ export interface PluginAssetSession {
|
||||
*
|
||||
* Use when:
|
||||
* - Plugin snapshots need mounted asset URLs without depending on the H3 server shape
|
||||
* - Host teardown must revoke plugin asset access independently from widget/gamelet logic
|
||||
* - Host teardown must revoke extension asset access independently from widget/gamelet logic
|
||||
*
|
||||
* Expects:
|
||||
* - Implementations own the underlying transport, cookie, and session lifecycle
|
||||
*
|
||||
* Returns:
|
||||
* - A startable/stoppable asset-hosting service with generic plugin-facing methods
|
||||
* - A startable/stoppable asset-hosting service with generic extension-facing methods
|
||||
*/
|
||||
export interface PluginAssetService extends ServerManager {
|
||||
export interface ExtensionAssetService extends ServerManager {
|
||||
getBaseUrl: () => string | undefined
|
||||
createAssetSession: (input: PluginAssetSessionInput) => Promise<PluginAssetSession>
|
||||
createAssetSession: (input: ExtensionAssetSessionInput) => Promise<ExtensionAssetSession>
|
||||
revokeSession: (assetSessionId: string) => Promise<void>
|
||||
revokeByOwnerSessionId: (ownerSessionId: string) => Promise<void>
|
||||
revokeByPluginId: (pluginId: string) => Promise<void>
|
||||
revokeByExtensionId: (extensionId: string) => Promise<void>
|
||||
revokeAll: () => Promise<void>
|
||||
}
|
||||
|
||||
function createPluginAssetCookie(baseUrl: string, session: StaticAssetSession): PluginAssetCookie {
|
||||
function createExtensionAssetCookie(baseUrl: string, session: StaticAssetSession): ExtensionAssetCookie {
|
||||
return {
|
||||
name: session.cookieName,
|
||||
value: session.cookieValue,
|
||||
@@ -156,36 +156,24 @@ function createPluginAssetCookie(baseUrl: string, session: StaticAssetSession):
|
||||
}
|
||||
}
|
||||
|
||||
function requireBaseUrl(baseUrl: string | undefined) {
|
||||
if (!baseUrl) {
|
||||
throw new Error('Plugin asset server base URL is unavailable; start the asset server before creating asset sessions')
|
||||
}
|
||||
|
||||
return baseUrl
|
||||
}
|
||||
|
||||
async function removeCookies(cookieAdapter: PluginAssetCookieAdapter, baseUrl: string, sessions: readonly StaticAssetSession[]) {
|
||||
await Promise.all(sessions.map(session => cookieAdapter.removeCookie(createPluginAssetCookie(baseUrl, session))))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the plugin asset host service backed by the extension static asset server.
|
||||
* Creates the extension asset host service backed by the extension static asset server.
|
||||
*
|
||||
* Use when:
|
||||
* - The extension host needs to expose mounted asset URLs to renderer snapshots
|
||||
* - Asset session lifecycle should stay inside the plugin domain instead of the HTTP server layer
|
||||
* - Asset session lifecycle should stay inside the extension domain instead of the HTTP server layer
|
||||
*
|
||||
* Expects:
|
||||
* - `getManifestEntryByName` returns the latest extension root/version map
|
||||
* - `cookieAdapter` writes and removes cookies in the Electron host session used by plugin iframes
|
||||
*
|
||||
* Returns:
|
||||
* - A plugin-facing asset host service with generic plugin asset methods
|
||||
* - An extension-facing asset host service with generic extension asset methods
|
||||
*/
|
||||
export function createPluginAssetService(options: {
|
||||
export function createExtensionAssetService(options: {
|
||||
getManifestEntryByName: () => Map<string, StaticAssetManifestEntry>
|
||||
cookieAdapter: PluginAssetCookieAdapter
|
||||
}): PluginAssetService {
|
||||
cookieAdapter: ExtensionAssetCookieAdapter
|
||||
}): ExtensionAssetService {
|
||||
const server = createStaticAssetService({ getManifestEntryByName: options.getManifestEntryByName })
|
||||
let lastBaseUrl: string | undefined
|
||||
|
||||
@@ -201,11 +189,13 @@ export function createPluginAssetService(options: {
|
||||
return
|
||||
}
|
||||
|
||||
await removeCookies(options.cookieAdapter, baseUrl, sessions)
|
||||
await Promise.all(
|
||||
sessions.map(session => options.cookieAdapter.removeCookie(createExtensionAssetCookie(baseUrl, session))),
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
key: 'plugin-assets',
|
||||
key: 'extension-assets',
|
||||
async start() {
|
||||
await server.start()
|
||||
},
|
||||
@@ -226,7 +216,11 @@ export function createPluginAssetService(options: {
|
||||
})
|
||||
|
||||
try {
|
||||
const baseUrl = requireBaseUrl(readBaseUrl())
|
||||
const baseUrl = readBaseUrl()
|
||||
if (!baseUrl) {
|
||||
throw new Error('Extension asset server base URL is unavailable; start the asset server before creating asset sessions')
|
||||
}
|
||||
|
||||
const mountedPath = buildMountedStaticAssetPath({
|
||||
extensionId: input.pluginId,
|
||||
assetSessionId: session.assetSessionId,
|
||||
@@ -234,10 +228,10 @@ export function createPluginAssetService(options: {
|
||||
})
|
||||
|
||||
if (!mountedPath) {
|
||||
throw new RangeError('Plugin asset session routeAssetPath must be a safe plugin asset path')
|
||||
throw new RangeError('Extension asset session routeAssetPath must be a safe extension asset path')
|
||||
}
|
||||
|
||||
const cookie = createPluginAssetCookie(baseUrl, session)
|
||||
const cookie = createExtensionAssetCookie(baseUrl, session)
|
||||
await options.cookieAdapter.setCookie(cookie)
|
||||
|
||||
return {
|
||||
@@ -263,8 +257,8 @@ export function createPluginAssetService(options: {
|
||||
async revokeByOwnerSessionId(ownerSessionId) {
|
||||
await revokeSessions(server.revokeByOwnerSessionId(ownerSessionId))
|
||||
},
|
||||
async revokeByPluginId(pluginId) {
|
||||
await revokeSessions(server.revokeByExtensionId(pluginId))
|
||||
async revokeByExtensionId(extensionId) {
|
||||
await revokeSessions(server.revokeByExtensionId(extensionId))
|
||||
},
|
||||
async revokeAll() {
|
||||
await revokeSessions(server.revokeAll())
|
||||
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
PluginHostDebugSnapshot,
|
||||
PluginHostModuleSummary,
|
||||
} from '../../../../../shared/eventa/plugin/host'
|
||||
import type { PluginAssetSnapshotService } from '../features/static-assets'
|
||||
import type { ExtensionAssetSnapshotService } from '../features/static-assets'
|
||||
import type { ExtensionConfig, ManifestEntry } from '../types'
|
||||
|
||||
import { rewriteWidgetModuleAssetUrl } from '../kits/widget'
|
||||
@@ -15,12 +15,12 @@ import { buildPluginRegistrySnapshot } from './registry'
|
||||
*
|
||||
* Use when:
|
||||
* - Renderer devtools need sessions, kits, modules, and capability state
|
||||
* - Widget iframe asset URLs must be rewritten to mounted plugin asset URLs
|
||||
* - Widget iframe asset URLs must be rewritten to mounted extension asset URLs
|
||||
*
|
||||
* Expects:
|
||||
* - `host` is the initialized extension host instance
|
||||
* - `manifestEntryByName` contains entries for any extension-owned modules being inspected
|
||||
* - `pluginAssetService` owns plugin asset URL/session lifecycle when mounted asset URLs are needed
|
||||
* - `extensionAssetService` owns extension asset URL/session lifecycle when mounted asset URLs are needed
|
||||
*
|
||||
* Returns:
|
||||
* - A full debug snapshot with registry, sessions, kits, modules, and capabilities
|
||||
@@ -32,9 +32,9 @@ export function buildPluginHostDebugSnapshot(options: {
|
||||
config: ExtensionConfig
|
||||
loaded: Set<string>
|
||||
manifestEntryByName: Map<string, ManifestEntry>
|
||||
pluginAssetService?: PluginAssetSnapshotService
|
||||
extensionAssetService?: ExtensionAssetSnapshotService
|
||||
}): Promise<PluginHostDebugSnapshot> {
|
||||
const pluginAssetService = options.pluginAssetService
|
||||
const extensionAssetService = options.extensionAssetService
|
||||
const modules = Promise.all(options.host
|
||||
.listBindings()
|
||||
.map(module =>
|
||||
@@ -42,8 +42,8 @@ export function buildPluginHostDebugSnapshot(options: {
|
||||
module as PluginHostModuleSummary,
|
||||
options.manifestEntryByName,
|
||||
{
|
||||
pluginAssetBaseUrl: pluginAssetService?.getBaseUrl(),
|
||||
...(pluginAssetService
|
||||
pluginAssetBaseUrl: extensionAssetService?.getBaseUrl(),
|
||||
...(extensionAssetService
|
||||
? {
|
||||
createAssetSession: ({ extensionId, version, sessionId, routeAssetPath, sessionPathPrefix }: {
|
||||
extensionId: string
|
||||
@@ -51,7 +51,7 @@ export function buildPluginHostDebugSnapshot(options: {
|
||||
sessionId: string
|
||||
routeAssetPath: string
|
||||
sessionPathPrefix: string
|
||||
}) => pluginAssetService.createAssetSession({
|
||||
}) => extensionAssetService.createAssetSession({
|
||||
pluginId: extensionId,
|
||||
version,
|
||||
ownerSessionId: sessionId,
|
||||
|
||||
@@ -5,9 +5,9 @@ import type {
|
||||
PluginRegistrySnapshot,
|
||||
} from '../../../../../shared/eventa/plugin/host'
|
||||
import type {
|
||||
PluginAssetCookie,
|
||||
PluginAssetSession,
|
||||
PluginAssetSnapshotService,
|
||||
ExtensionAssetCookie,
|
||||
ExtensionAssetSession,
|
||||
ExtensionAssetSnapshotService,
|
||||
} from '../features/static-assets'
|
||||
import type { ExtensionHostService, SetupExtensionHostOptions } from '../types'
|
||||
|
||||
@@ -18,7 +18,7 @@ import { ExtensionHost } from '@proj-airi/plugin-sdk/plugin-host'
|
||||
import { app, session as electronSession } from 'electron'
|
||||
|
||||
import { createExtensionAutoReloadFeature } from '../features/auto-reload'
|
||||
import { createPluginAssetService } from '../features/static-assets'
|
||||
import { createExtensionAssetService } from '../features/static-assets'
|
||||
import { createBuiltInExtensionKitRuntime } from '../kits'
|
||||
import { createExtensionHostConfigStore } from './config'
|
||||
import { buildPluginHostDebugSnapshot } from './debug'
|
||||
@@ -34,7 +34,7 @@ const extensionAssetSessionTtlMs = 30 * 24 * 60 * 60 * 1000
|
||||
|
||||
function createElectronExtensionAssetCookieAdapter() {
|
||||
return {
|
||||
async setCookie(cookie: PluginAssetCookie) {
|
||||
async setCookie(cookie: ExtensionAssetCookie) {
|
||||
await electronSession.defaultSession.cookies.set({
|
||||
url: cookie.url,
|
||||
name: cookie.name,
|
||||
@@ -46,7 +46,7 @@ function createElectronExtensionAssetCookieAdapter() {
|
||||
expirationDate: Math.floor(cookie.expiresAt / 1000),
|
||||
})
|
||||
},
|
||||
async removeCookie(cookie: PluginAssetCookie) {
|
||||
async removeCookie(cookie: ExtensionAssetCookie) {
|
||||
await electronSession.defaultSession.cookies.remove(cookie.url, cookie.name)
|
||||
},
|
||||
}
|
||||
@@ -169,7 +169,7 @@ export interface ExtensionHostServiceInternal extends ExtensionHostService {
|
||||
* - Host debugging needs a fresh runtime snapshot after registry refresh
|
||||
*
|
||||
* Expects:
|
||||
* - The host and plugin asset service are both initialized
|
||||
* - The host and extension asset service are both initialized
|
||||
*
|
||||
* Returns:
|
||||
* - The full debug snapshot exposed through plugin inspection IPC
|
||||
@@ -184,10 +184,10 @@ export interface ExtensionHostServiceInternal extends ExtensionHostService {
|
||||
* - Snapshot consumers need the current loopback asset mount base
|
||||
*
|
||||
* Expects:
|
||||
* - The plugin asset service may be started before this is called
|
||||
* - The extension asset service may be started before this is called
|
||||
*
|
||||
* Returns:
|
||||
* - The current plugin asset base URL, or an empty string when unavailable
|
||||
* - The current extension asset base URL, or an empty string when unavailable
|
||||
*/
|
||||
getAssetBaseUrl: () => string
|
||||
|
||||
@@ -233,10 +233,8 @@ export async function setupExtensionHostServiceInternal(
|
||||
|
||||
// Kit API, Host
|
||||
const builtInKitRuntime = createBuiltInExtensionKitRuntime(options)
|
||||
const host = new ExtensionHost({ runtime: 'electron', contributions: builtInKitRuntime.contributions })
|
||||
builtInKitRuntime.attachHost(host) // reverse dependency injection
|
||||
const host = new ExtensionHost({ runtime: 'electron' })
|
||||
log.withFields({ extensionsRoot }).log('loading extension manifests')
|
||||
// Once kit injected the host, then apply kits
|
||||
builtInKitRuntime.registerHostKits(host)
|
||||
|
||||
// extension registry
|
||||
@@ -249,19 +247,19 @@ export async function setupExtensionHostServiceInternal(
|
||||
}
|
||||
|
||||
// Extension feature: Static Assets serving
|
||||
const pluginAssetService = createPluginAssetService({
|
||||
const extensionAssetService = createExtensionAssetService({
|
||||
getManifestEntryByName: () => extensionRegistry.getManifestEntryByName(),
|
||||
cookieAdapter: createElectronExtensionAssetCookieAdapter(),
|
||||
})
|
||||
await pluginAssetService.start()
|
||||
await extensionAssetService.start()
|
||||
|
||||
const loaded = new Set<string>()
|
||||
const loadedSessionIds = new Map<string, string>()
|
||||
const moduleAssetSessionCache = new Map<string, PluginAssetSession>()
|
||||
const moduleAssetSessionCache = new Map<string, ExtensionAssetSession>()
|
||||
|
||||
const clearModuleAssetSessionCacheByExtensionId = (pluginId: string) => {
|
||||
const clearModuleAssetSessionCacheByExtensionId = (extensionId: string) => {
|
||||
for (const key of moduleAssetSessionCache.keys()) {
|
||||
if (key.startsWith(`${pluginId}:`)) {
|
||||
if (key.startsWith(`${extensionId}:`)) {
|
||||
moduleAssetSessionCache.delete(key)
|
||||
}
|
||||
}
|
||||
@@ -305,7 +303,7 @@ export async function setupExtensionHostServiceInternal(
|
||||
return cachedSession
|
||||
}
|
||||
|
||||
const session = await pluginAssetService.createAssetSession({
|
||||
const session = await extensionAssetService.createAssetSession({
|
||||
pluginId,
|
||||
version,
|
||||
ownerSessionId,
|
||||
@@ -317,8 +315,8 @@ export async function setupExtensionHostServiceInternal(
|
||||
return session
|
||||
}
|
||||
|
||||
const pluginAssetSnapshotService: PluginAssetSnapshotService = {
|
||||
getBaseUrl: pluginAssetService.getBaseUrl,
|
||||
const extensionAssetSnapshotService: ExtensionAssetSnapshotService = {
|
||||
getBaseUrl: extensionAssetService.getBaseUrl,
|
||||
createAssetSession: ({ pluginId, version, ownerSessionId, routeAssetPath, pathPrefix }) => {
|
||||
return createModuleAssetSession({
|
||||
pluginId,
|
||||
@@ -338,7 +336,7 @@ export async function setupExtensionHostServiceInternal(
|
||||
config: getConfig(),
|
||||
loaded,
|
||||
manifestEntryByName: extensionRegistry.getManifestEntryByName(),
|
||||
pluginAssetService: pluginAssetSnapshotService,
|
||||
extensionAssetService: extensionAssetSnapshotService,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -374,7 +372,7 @@ export async function setupExtensionHostServiceInternal(
|
||||
loaded.delete(name)
|
||||
|
||||
clearModuleAssetSessionCacheByOwnerSessionId(sessionId)
|
||||
await pluginAssetService.revokeByOwnerSessionId(sessionId)
|
||||
await extensionAssetService.revokeByOwnerSessionId(sessionId)
|
||||
|
||||
log.log('extension unloaded', { extension: name, sessionId })
|
||||
}
|
||||
@@ -436,6 +434,9 @@ export async function setupExtensionHostServiceInternal(
|
||||
|
||||
return {
|
||||
host,
|
||||
// REVIEW: Tool registry ownership is currently hidden inside the built-in kit runtime even though
|
||||
// the host service also exposes it for IPC listing/invocation. Consider moving registry ownership
|
||||
// to this host service and passing it into kit registration as a dependency.
|
||||
tools: builtInKitRuntime.tools,
|
||||
manifests: extensionRegistry.listManifests(),
|
||||
async list() {
|
||||
@@ -454,7 +455,7 @@ export async function setupExtensionHostServiceInternal(
|
||||
else {
|
||||
enabled.delete(payload.name)
|
||||
clearModuleAssetSessionCacheByExtensionId(payload.name)
|
||||
await pluginAssetService.revokeByPluginId(payload.name)
|
||||
await extensionAssetService.revokeByExtensionId(payload.name)
|
||||
}
|
||||
|
||||
const entry = extensionRegistry.findManifestEntry(payload.name)
|
||||
@@ -514,15 +515,15 @@ export async function setupExtensionHostServiceInternal(
|
||||
return await inspectSnapshot()
|
||||
},
|
||||
getAssetBaseUrl() {
|
||||
return pluginAssetService.getBaseUrl() ?? ''
|
||||
return extensionAssetService.getBaseUrl() ?? ''
|
||||
},
|
||||
async dispose() {
|
||||
autoReloadFeature.dispose()
|
||||
builtInKitRuntime.dispose()
|
||||
|
||||
moduleAssetSessionCache.clear()
|
||||
await pluginAssetService.revokeAll()
|
||||
await pluginAssetService.stop()
|
||||
await extensionAssetService.revokeAll()
|
||||
await extensionAssetService.stop()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,8 @@ import { defineInvoke } from '@moeru/eventa'
|
||||
import { ExtensionHost } from '@proj-airi/plugin-sdk/plugin-host'
|
||||
import { afterEach, beforeEach, describe, expect, expectTypeOf, it, vi } from 'vitest'
|
||||
|
||||
import {
|
||||
electronPluginGetAssetBaseUrl,
|
||||
} from '../../../../shared/eventa/plugin/assets'
|
||||
import {
|
||||
electronPluginUpdateCapability,
|
||||
} from '../../../../shared/eventa/plugin/capabilities'
|
||||
import { electronPluginGetAssetBaseUrl } from '../../../../shared/eventa/plugin/assets'
|
||||
import { electronPluginUpdateCapability } from '../../../../shared/eventa/plugin/capabilities'
|
||||
import {
|
||||
electronPluginInspect,
|
||||
electronPluginList,
|
||||
@@ -34,15 +30,11 @@ import {
|
||||
electronPluginSetEnabled,
|
||||
electronPluginUnload,
|
||||
} from '../../../../shared/eventa/plugin/host'
|
||||
import {
|
||||
electronPluginToolsChanged,
|
||||
} from '../../../../shared/eventa/plugin/tools'
|
||||
import { electronPluginToolsChanged } from '../../../../shared/eventa/plugin/tools'
|
||||
import { setupExtensionHostServiceInternal } from './host'
|
||||
import { loadManifestsFrom } from './host/registry'
|
||||
import { setupExtensionHost as setupExtensionHostService } from './index'
|
||||
import {
|
||||
gameletPluginKitDescriptor,
|
||||
} from './kits/gamelet'
|
||||
import { gameletPluginKitDescriptor } from './kits/gamelet'
|
||||
import { createGameletOrchestrationRuntime } from './kits/gamelet/orchestration'
|
||||
import { widgetPluginKitDescriptor } from './kits/widget'
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@ import { defineInvoke, defineInvokeHandler } from '@moeru/eventa'
|
||||
import { createContext } from '@moeru/eventa/adapters/electron/main'
|
||||
import { app, ipcMain } from 'electron'
|
||||
|
||||
import {
|
||||
electronPluginGetAssetBaseUrl,
|
||||
} from '../../../../shared/eventa/plugin/assets'
|
||||
import { electronPluginGetAssetBaseUrl } from '../../../../shared/eventa/plugin/assets'
|
||||
import {
|
||||
electronPluginUpdateCapability,
|
||||
pluginProtocolListProviders,
|
||||
|
||||
@@ -3,10 +3,7 @@ import type { ManifestEntry } from '../../types'
|
||||
|
||||
import { isPlainObject } from 'es-toolkit'
|
||||
|
||||
import {
|
||||
buildMountedStaticAssetPath,
|
||||
normalizeStaticAssetPath,
|
||||
} from '../../../http-server/static-assets/paths'
|
||||
import { buildMountedStaticAssetPath, normalizeStaticAssetPath } from '../../../http-server/static-assets/paths'
|
||||
|
||||
/**
|
||||
* Describes one widget iframe asset as seen from the mounted `/ui` route.
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import type {
|
||||
ExtensionHost,
|
||||
KitDescriptor,
|
||||
} from '@proj-airi/plugin-sdk/plugin-host'
|
||||
import type { ExtensionHost, KitDescriptor } from '@proj-airi/plugin-sdk/plugin-host'
|
||||
|
||||
export {
|
||||
resolveWidgetAssetRoute,
|
||||
rewriteWidgetModuleAssetUrl,
|
||||
} from './asset-url'
|
||||
export { resolveWidgetAssetRoute, rewriteWidgetModuleAssetUrl } from './asset-url'
|
||||
|
||||
/**
|
||||
* Declares the built-in widget kit exposed by `stage-tamagotchi`.
|
||||
|
||||
Reference in New Issue
Block a user