返回 CodeWhale
isolate.ts
根目录 / crates / tui / extension-host / src / dsh / upstream / loader / src / config / isolate.ts
1 import { Context } from '@deepseek-ai/cordis'
2 import type { Dict } from '@deepseek-ai/cosmokit'
3 import { Entry } from './entry.ts'
4
5 // Codewhale type-only delta: these dynamically allocated symbol slots hold delimiters.
6 type DelimitedContext = Context & Record<symbol, symbol>
7
8 declare module './entry.ts' {
9 interface EntryOptions {
10 intercept?: Dict | null
11 isolate?: Dict<true | string> | null
12 }
13
14 interface Entry {
15 realm: LocalRealm
16 }
17 }
18
19 function swap<T extends {}>(target: T, source?: T | null) {
20 for (const key of Reflect.ownKeys(target)) {
21 Reflect.deleteProperty(target, key)
22 }
23 for (const key of Reflect.ownKeys(source || {})) {
24 Reflect.defineProperty(target, key, Reflect.getOwnPropertyDescriptor(source!, key)!)
25 }
26 }
27
28 /** Symbol realm used to isolate service implementations by entry or label. */
29 export abstract class Realm {
30 protected store: Dict<symbol> = Object.create(null)
31
32 abstract get suffix(): string
33
34 access(key: string, create = false) {
35 if (create) {
36 return this.store[key] ??= Symbol(`${key}${this.suffix}`)
37 } else {
38 return this.store[key] ?? Symbol(`${key}${this.suffix}`)
39 }
40 }
41
42 delete(key: string) {
43 delete this.store[key]
44 }
45
46 get size() {
47 return Object.keys(this.store).length
48 }
49 }
50
51 /** Entry-local isolation realm. */
52 export class LocalRealm extends Realm {
53 constructor(private entry: Entry) {
54 super()
55 }
56
57 get suffix() {
58 return '#' + this.entry.options.id
59 }
60 }
61
62 /** Named isolation realm shared by entries that use the same label. */
63 export class GlobalRealm extends Realm {
64 constructor(public label: string) {
65 super()
66 }
67
68 get suffix() {
69 return '@' + this.label
70 }
71 }
72
73 /** Install loader hooks that apply `intercept` and `isolate` entry options. */
74 export default function isolate(ctx: Context) {
75 const realms: Dict<GlobalRealm> = Object.create(null)
76 const delims: Dict<symbol> = Object.create(null)
77
78 function access(entry: Entry, name: string, create: true): symbol
79 function access(entry: Entry, name: string, create?: boolean): symbol | undefined
80 function access(entry: Entry, name: string, create = false) {
81 let realm: Realm | undefined
82 const label = entry.options.isolate?.[name]
83 if (!label) return
84 if (label === true) {
85 realm = entry.realm ??= new LocalRealm(entry)
86 } else if (create) {
87 realm = realms[label] ??= new GlobalRealm(label)
88 } else {
89 realm = realms[label]
90 }
91 return realm?.access(name, create)
92 }
93
94 ctx.on('loader/entry-init', (entry) => {
95 entry.ctx[Context.intercept] = Object.create(entry.ctx[Context.intercept])
96 entry.ctx[Context.isolate] = Object.create(entry.ctx[Context.isolate])
97 })
98
99 ctx.on('loader/patch-context', (entry, next) => {
100 // step 1: generate new isolate map
101 const newMap: Dict<symbol> = Object.create(entry.parent.ctx[Context.isolate])
102 for (const name of Object.keys(entry.options.isolate ?? {})) {
103 newMap[name] = access(entry, name, true)
104 }
105
106 // step 2: generate service diff
107 const diff: Dict<[symbol, symbol, symbol, symbol]> = Object.create(null)
108 const oldMap = entry.ctx[Context.isolate]
109 for (const name in { ...newMap, ...delims }) {
110 if (newMap[name] === oldMap[name]) continue
111 const delim = delims[name] ??= Symbol(`delim:${name}`)
112 ;(entry.ctx as DelimitedContext)[delim] = Symbol(`${name}#${entry.id}`)
113 for (const symbol of [oldMap[name], newMap[name]]) {
114 const impl = symbol && entry.ctx.reflect.store[symbol]
115 if (!impl) continue
116 if (!impl.fiber) {
117 entry.ctx.logger.warn(new Error(`expected service ${name} to be implemented`))
118 continue
119 }
120 diff[name] = [oldMap[name], newMap[name], (entry.ctx as DelimitedContext)[delim], (impl.fiber.ctx as DelimitedContext)[delim]]
121 if ((entry.ctx as DelimitedContext)[delim] !== (impl.fiber.ctx as DelimitedContext)[delim]) break
122 }
123 }
124
125 // step 3: set prototype for transferred context
126 Object.setPrototypeOf(entry.ctx[Context.isolate], entry.parent.ctx[Context.isolate])
127 Object.setPrototypeOf(entry.ctx[Context.intercept], entry.parent.ctx[Context.intercept])
128 swap(entry.ctx[Context.isolate], newMap)
129 swap(entry.ctx[Context.intercept], entry.options.intercept)
130
131 // step 4: reload fiber
132 next()
133
134 // step 5: replace service impl
135 for (const [symbol1, symbol2, flag1, flag2] of Object.values(diff)) {
136 if (flag1 === flag2 && entry.ctx.reflect.store[symbol1] && !entry.ctx.reflect.store[symbol2]) {
137 entry.ctx.reflect.store[symbol2] = entry.ctx.reflect.store[symbol1]
138 delete entry.ctx.reflect.store[symbol1]
139 }
140 }
141
142 // step 6: reflect notify
143 ctx.reflect.notify(Object.keys(diff), (ctx, name) => {
144 const [symbol1, symbol2, flag1, flag2] = diff[name]
145 const symbol3 = ctx[Context.isolate][name]
146 const flag3 = (ctx as DelimitedContext)[delims[name]]
147 return (symbol1 === symbol3 || symbol2 === symbol3) && (flag1 === flag3) !== (flag1 === flag2)
148 })
149
150 // step 7: clean up delimiters
151 for (const name in delims) {
152 if (!Reflect.ownKeys(newMap).includes(name)) {
153 delete (entry.ctx as DelimitedContext)[delims[name]]
154 }
155 }
156 })
157
158 ctx.on('loader/partial-dispose', (entry, legacy, active) => {
159 for (const [name, label] of Object.entries(legacy.isolate ?? {})) {
160 if (label === true) continue
161 if (active && entry.options.isolate?.[name] === label) continue
162 const realm = realms[label]
163 if (!realm) continue
164
165 // realm garbage collection
166 for (const entry of ctx.loader.entries()) {
167 // has reference to this realm
168 if (entry.options.isolate?.[name] === realm.label) return
169 }
170 realm.delete(name)
171 if (!realm.size) {
172 delete realms[realm.label]
173 }
174 }
175 })
176 }
177
177 lines TYPESCRIPT