Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 29 additions & 15 deletions kun/src/extensions/host-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,11 +370,22 @@ export class ExtensionHostProcess {
state: this._state
})
}
return this.peer!.request('extension.invoke', { method, params }, {
signal: options.signal,
timeoutMs: options.timeoutMs ?? this.limits.operationTimeoutMs,
resetTimeoutOnStream: options.resetTimeoutOnStream
})
try {
return await this.peer!.request('extension.invoke', { method, params }, {
signal: options.signal,
timeoutMs: options.timeoutMs ?? this.limits.operationTimeoutMs,
resetTimeoutOnStream: options.resetTimeoutOnStream
})
} catch (error) {
if (
this.exitPromise !== undefined &&
this.child !== undefined &&
(this.child.exitCode !== null || this.child.signalCode !== null)
) {
await this.exitPromise
}
throw error
}
}

async notify(method: string, params: JsonValue): Promise<void> {
Expand Down Expand Up @@ -539,16 +550,19 @@ export class ExtensionHostProcess {
await this.log.write('lifecycle', `exited expected=${expected} code=${code} signal=${signal}`)
.catch(() => undefined)
await this.log.flush().catch(() => undefined)
this.resolveExit?.()
this.resolveExit = undefined
await this.options.onExit?.({
extensionId: this.principal.extensionId,
lifecycleNonce: this.lifecycleNonce,
expected,
code,
signal,
...(this._lastError === undefined ? {} : { error: this._lastError })
})
try {
await this.options.onExit?.({
extensionId: this.principal.extensionId,
lifecycleNonce: this.lifecycleNonce,
expected,
code,
signal,
...(this._lastError === undefined ? {} : { error: this._lastError })
})
} finally {
this.resolveExit?.()
this.resolveExit = undefined
}
}

private send(envelope: RpcEnvelope): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const output = `${JSON.stringify(schema, null, 2)}\n`

if (process.argv.includes('--check')) {
const current = await readFile(outputPath, 'utf8').catch(() => '')
if (current !== output) {
if (current.replace(/\r\n/gu, '\n') !== output) {
console.error('EXT_SCHEMA_STALE: schema/kun-extension.schema.json is not generated from ExtensionManifestSchema')
process.exitCode = 1
}
Expand Down
24 changes: 23 additions & 1 deletion scripts/check-extension-docs.test.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { dirname, join, posix, win32 } from 'node:path'
import test from 'node:test'
import { fileURLToPath } from 'node:url'
import {
Expand All @@ -9,6 +9,7 @@ import {
SDK_SNAPSHOTS_BEGIN,
SDK_SNAPSHOTS_END,
githubHeadingSlug,
isPathWithinRoot,
renderApiExportsRegion,
renderSdkSnapshotsRegion,
validateBilingualPair,
Expand Down Expand Up @@ -110,6 +111,27 @@ test('detects generated API inventory and Changelog public-surface drift', () =>
)[0].includes('drifted'))
})

test('contains public SDK declarations across native and mixed Windows separators', () => {
assert.equal(
isPathWithinRoot(
'D:\\a\\Kun\\Kun\\packages\\extension-api\\src',
'D:/a/Kun/Kun/packages/extension-api/src/accounts.ts',
win32
),
true
)
assert.equal(
isPathWithinRoot(
'D:\\a\\Kun\\Kun\\packages\\extension-api\\src',
'D:/a/Kun/Kun/packages/extension-api/src-escape/accounts.ts',
win32
),
false
)
assert.equal(isPathWithinRoot('/repo/packages/api/src', '/repo/packages/api/src/index.ts', posix), true)
assert.equal(isPathWithinRoot('/repo/packages/api/src', '/repo/packages/other/index.ts', posix), false)
})

function fixture(name) {
return readFile(join(fixtures, name), 'utf8')
}
2 changes: 2 additions & 0 deletions scripts/check-extension-release-gate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ function requirePublishDependencies(document, workflowLabel) {
}

function requireOrderedSourceMarkers(source, label, markers) {
source = source.replace(/\r\n/gu, '\n')
let priorIndex = -1
for (const marker of markers) {
const index = source.indexOf(marker, priorIndex + 1)
Expand All @@ -202,6 +203,7 @@ function requireOrderedSourceMarkers(source, label, markers) {
}

function requireSourceMarkersAfter(source, label, priorMarker, markers) {
source = source.replace(/\r\n/gu, '\n')
const priorIndex = source.indexOf(priorMarker)
check(priorIndex >= 0, `${label} is missing required gate marker: ${priorMarker}`)
for (const marker of markers) {
Expand Down
13 changes: 11 additions & 2 deletions scripts/lib/extension-docs-validation.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createHash } from 'node:crypto'
import { access, readFile, readdir } from 'node:fs/promises'
import { dirname, extname, join, relative, resolve, sep } from 'node:path'
import { dirname, extname, isAbsolute, join, relative, resolve, sep } from 'node:path'
import ts from 'typescript'

export const API_EXPORTS_BEGIN = '<!-- BEGIN GENERATED SDK EXPORTS -->'
Expand Down Expand Up @@ -244,7 +244,7 @@ export async function inspectPublicSdkPackages(root) {
const declaration = symbol.valueDeclaration ?? symbol.declarations?.[0]
if (!declaration) throw new Error(`Public export ${exportSymbol.name} has no declaration`)
const sourceFile = declaration.getSourceFile().fileName
if (!sourceFile.startsWith(`${join(packageRoot, 'src')}${sep}`) && sourceFile !== entryPath) {
if (!isPathWithinRoot(join(packageRoot, 'src'), sourceFile)) {
throw new Error(`Public export ${exportSymbol.name} escapes ${definition.name}: ${sourceFile}`)
}
return {
Expand All @@ -271,6 +271,15 @@ export async function inspectPublicSdkPackages(root) {
return result
}

export function isPathWithinRoot(root, candidate, pathApi = { isAbsolute, relative, sep }) {
const relativePath = pathApi.relative(root, candidate)
return relativePath === '' || (
relativePath !== '..' &&
!relativePath.startsWith(`..${pathApi.sep}`) &&
!pathApi.isAbsolute(relativePath)
)
}

export function renderApiExportsRegion(sdkPackages, locale) {
const summaryLabels = locale === 'zh'
? ['SDK 包', '版本', '公开入口', '公开导出数', '公开 surface SHA-256']
Expand Down
Loading