@1aboveio/skills 0.19.2 → 0.20.1
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/README.md +9 -4
- package/package.json +1 -1
- package/runtime/skills/distribution/generated/recipes.json +54 -48
- package/runtime/skills/distribution/scripts/bundles.mjs +113 -7
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +4 -6
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +3 -36
- package/skills/cicd-pipeline/mergify/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +14 -83
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +4 -6
- package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +3 -36
- package/skills/engineering/smoke/SKILL.md +1 -1
- package/skills/payment/payment-analysis/SKILL.md +13 -7
- package/skills/payment/payment-analysis/references/chargebacks.md +43 -18
- package/skills/payment/payment-analysis/references/event-layers.md +4 -0
- package/skills/payment/payment-analysis/references/metrics.md +9 -7
- package/skills/payment/payment-analysis/references/report-template.md +3 -3
- package/skills/payment/payment-analysis/references/terminology.md +6 -1
- package/skills/payment/payment-analysis/references/visualization.md +1 -1
- package/skills/payment/tld-detection/SKILL.md +163 -0
- package/skills/payment/tld-detection/agents/openai.yaml +9 -0
- package/skills/payment/tld-detection/evals/evals.json +107 -0
- package/skills/payment/tld-detection/references/clean.md +51 -0
- package/skills/payment/tld-detection/references/clustering.md +219 -0
- package/skills/payment/tld-detection/references/decision.md +85 -0
- package/skills/payment/tld-detection/references/report-template.md +169 -0
- package/skills/payment/tld-detection/references/terminology.md +69 -0
- package/skills/payment/tld-detection/references/workflow.md +78 -0
- package/skills/payment/tld-detection/scripts/detect_ladders.py +390 -0
- package/skills/payment/tld-detection/tests/test_detect_ladders.py +132 -0
- package/skills/engineering/resolve-issues/SKILL.md +0 -168
- package/skills/engineering/resolve-issues/agents/openai.yaml +0 -9
- package/skills/engineering/resolve-issues/generated/workflow-repair-policy.json +0 -499
- package/skills/engineering/resolve-issues/references/breaker.md +0 -82
- package/skills/engineering/resolve-issues/references/deliverables.md +0 -27
- package/skills/engineering/resolve-issues/references/delivery.md +0 -108
- package/skills/engineering/resolve-issues/references/evidence-lane.md +0 -21
- package/skills/engineering/resolve-issues/references/exact-head-ci.md +0 -287
- package/skills/engineering/resolve-issues/references/fan-out.md +0 -33
- package/skills/engineering/resolve-issues/references/finalization.md +0 -68
- package/skills/engineering/resolve-issues/references/guarantees.md +0 -10
- package/skills/engineering/resolve-issues/references/high-risk.md +0 -29
- package/skills/engineering/resolve-issues/references/incidents/848/README.md +0 -156
- package/skills/engineering/resolve-issues/references/intake.md +0 -86
- package/skills/engineering/resolve-issues/references/integration-gate.md +0 -53
- package/skills/engineering/resolve-issues/references/interference.md +0 -87
- package/skills/engineering/resolve-issues/references/loop.md +0 -134
- package/skills/engineering/resolve-issues/references/model-catalog.md +0 -9
- package/skills/engineering/resolve-issues/references/postmortem.md +0 -27
- package/skills/engineering/resolve-issues/references/pre-flight-model-slots.md +0 -41
- package/skills/engineering/resolve-issues/references/pre-flight-recording-and-checkout.md +0 -48
- package/skills/engineering/resolve-issues/references/pre-flight.md +0 -41
- package/skills/engineering/resolve-issues/references/regression-checklist.md +0 -26
- package/skills/engineering/resolve-issues/references/run-state.md +0 -288
- package/skills/engineering/resolve-issues/references/sandboxed-testing.md +0 -48
- package/skills/engineering/resolve-issues/references/spawn-contract.md +0 -96
- package/skills/engineering/resolve-issues/references/terminal-evidence-journal.md +0 -40
- package/skills/engineering/resolve-issues/references/why.md +0 -653
- package/skills/engineering/resolve-issues/schemas/fix-round.schema.json +0 -49
- package/skills/engineering/resolve-issues/scripts/combine-and-verify.mjs +0 -721
- package/skills/engineering/resolve-issues/scripts/component-candidate.mjs +0 -962
- package/skills/engineering/resolve-issues/scripts/contract-revision.mjs +0 -220
- package/skills/engineering/resolve-issues/scripts/detect-delivery-mode.mjs +0 -420
- package/skills/engineering/resolve-issues/scripts/detect-target-branch.mjs +0 -256
- package/skills/engineering/resolve-issues/scripts/detect-workspace-mode.mjs +0 -168
- package/skills/engineering/resolve-issues/scripts/discover-models.mjs +0 -9
- package/skills/engineering/resolve-issues/scripts/doctrine.mjs +0 -62
- package/skills/engineering/resolve-issues/scripts/evidence-lifecycle-contract.mjs +0 -191
- package/skills/engineering/resolve-issues/scripts/exact-head-ci.mjs +0 -413
- package/skills/engineering/resolve-issues/scripts/exact-head-github-provider.mjs +0 -332
- package/skills/engineering/resolve-issues/scripts/finalize.mjs +0 -488
- package/skills/engineering/resolve-issues/scripts/fix-rounds.mjs +0 -3307
- package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-circular-1001.json +0 -16
- package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-valid-sequencing.json +0 -51
- package/skills/engineering/resolve-issues/scripts/fixtures/fmm-express-830-component-candidate.json +0 -17
- package/skills/engineering/resolve-issues/scripts/fixtures/head-check-set-1081.json +0 -166
- package/skills/engineering/resolve-issues/scripts/gate-value-series.mjs +0 -92
- package/skills/engineering/resolve-issues/scripts/guide-index.mjs +0 -73
- package/skills/engineering/resolve-issues/scripts/head-check-set.mjs +0 -159
- package/skills/engineering/resolve-issues/scripts/interference.mjs +0 -427
- package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +0 -9
- package/skills/engineering/resolve-issues/scripts/next-operations.mjs +0 -419
- package/skills/engineering/resolve-issues/scripts/postmortem.mjs +0 -909
- package/skills/engineering/resolve-issues/scripts/preflight-questions.mjs +0 -345
- package/skills/engineering/resolve-issues/scripts/reconcile-contained-unit-prs.mjs +0 -415
- package/skills/engineering/resolve-issues/scripts/release-state-contract.mjs +0 -697
- package/skills/engineering/resolve-issues/scripts/report.mjs +0 -494
- package/skills/engineering/resolve-issues/scripts/required-check-plan.mjs +0 -172
- package/skills/engineering/resolve-issues/scripts/round-metadata.mjs +0 -79
- package/skills/engineering/resolve-issues/scripts/run-state-review6-cases.mjs +0 -334
- package/skills/engineering/resolve-issues/scripts/run-state.mjs +0 -4792
- package/skills/engineering/resolve-issues/scripts/sandbox-selftest.mjs +0 -395
- package/skills/engineering/resolve-issues/scripts/spawn-contract.mjs +0 -290
- package/skills/engineering/resolve-issues/scripts/terminal-dispositions.mjs +0 -170
- package/skills/engineering/resolve-issues/scripts/terminal-evidence-journal.mjs +0 -293
- package/skills/engineering/resolve-issues/scripts/unit-kind.mjs +0 -197
- package/skills/engineering/resolve-issues/scripts/unit-lifecycle.mjs +0 -127
- package/skills/engineering/resolve-issues/scripts/watch-delivery.mjs +0 -893
- package/skills/engineering/resolve-issues/scripts/workspaces.mjs +0 -829
- package/skills/engineering/resolve-issues/workflows/independent-review.workflow.js +0 -290
- package/skills/engineering/resolve-issues/workflows/prior-art-scan.workflow.js +0 -80
- package/skills/engineering/resolve-issues/workflows/workflow-smoke.mjs +0 -102
- package/skills/engineering/resolve-release/LICENSE +0 -3
- package/skills/engineering/resolve-release/SKILL.md +0 -113
- package/skills/engineering/resolve-release/agents/openai.yaml +0 -9
- package/skills/engineering/resolve-release/references/assembly.md +0 -137
- package/skills/engineering/resolve-release/references/auto-when-green.md +0 -56
- package/skills/engineering/resolve-release/references/candidate.md +0 -167
- package/skills/engineering/resolve-release/references/exposure.md +0 -178
- package/skills/engineering/resolve-release/references/handoff.md +0 -24
- package/skills/engineering/resolve-release/references/postmortem.md +0 -230
- package/skills/engineering/resolve-release/references/preflight.md +0 -208
- package/skills/engineering/resolve-release/references/principles.md +0 -94
- package/skills/engineering/resolve-release/references/regression-checklist.md +0 -36
- package/skills/engineering/resolve-release/references/related-skills.md +0 -13
- package/skills/engineering/resolve-release/references/routing.md +0 -149
- package/skills/engineering/resolve-release/references/verified-sha-github-flow.md +0 -285
- package/skills/engineering/resolve-release/references/versioning.md +0 -202
- package/skills/engineering/resolve-release/references/why.md +0 -53
- package/skills/engineering/resolve-release/scripts/adapter-completion-artifact.mjs +0 -389
- package/skills/engineering/resolve-release/scripts/build-changes.mjs +0 -209
- package/skills/engineering/resolve-release/scripts/candidate-hygiene.mjs +0 -407
- package/skills/engineering/resolve-release/scripts/candidate-identity.mjs +0 -904
- package/skills/engineering/resolve-release/scripts/candidate-traffic.mjs +0 -81
- package/skills/engineering/resolve-release/scripts/checked-adapter-loader.mjs +0 -612
- package/skills/engineering/resolve-release/scripts/close-attempt.mjs +0 -135
- package/skills/engineering/resolve-release/scripts/closeout-release.mjs +0 -161
- package/skills/engineering/resolve-release/scripts/doctrine.mjs +0 -106
- package/skills/engineering/resolve-release/scripts/durable-processing.mjs +0 -522
- package/skills/engineering/resolve-release/scripts/ensure-target-green.mjs +0 -659
- package/skills/engineering/resolve-release/scripts/evidence-bundle.mjs +0 -1014
- package/skills/engineering/resolve-release/scripts/finalize-release.mjs +0 -526
- package/skills/engineering/resolve-release/scripts/fixtures/durable-processing-adapter.mjs +0 -169
- package/skills/engineering/resolve-release/scripts/green-gate.mjs +0 -599
- package/skills/engineering/resolve-release/scripts/isolated-adapter-evaluator.mjs +0 -752
- package/skills/engineering/resolve-release/scripts/metadata-pr-status.mjs +0 -56
- package/skills/engineering/resolve-release/scripts/metadata-sync.mjs +0 -1538
- package/skills/engineering/resolve-release/scripts/postmortem.mjs +0 -381
- package/skills/engineering/resolve-release/scripts/preflight-probes.mjs +0 -510
- package/skills/engineering/resolve-release/scripts/production-endpoints.mjs +0 -326
- package/skills/engineering/resolve-release/scripts/rc-circuit-breaker.mjs +0 -272
- package/skills/engineering/resolve-release/scripts/report.mjs +0 -417
- package/skills/engineering/resolve-release/scripts/reprobe-credentials.mjs +0 -114
- package/skills/engineering/resolve-release/scripts/revalidate-candidate.mjs +0 -238
- package/skills/engineering/resolve-release/scripts/review-packet.mjs +0 -503
- package/skills/engineering/resolve-release/scripts/rollback-floor.mjs +0 -263
- package/skills/engineering/resolve-release/scripts/version-assert.mjs +0 -339
- package/skills/engineering/resolve-release/scripts/version-postmortem.mjs +0 -485
- package/skills/engineering/resolve-release/scripts/version.mjs +0 -1199
- package/skills/engineering/resolve-release/scripts/watch-candidate-delivery.mjs +0 -449
- package/skills/engineering/resolve-release/vendor/ACORN-LICENSE +0 -21
- package/skills/engineering/resolve-release/vendor/README.md +0 -60
- package/skills/engineering/resolve-release/vendor/acorn.mjs +0 -6233
- package/skills/engineering/resolve-release/vendor/quickjs/core/LICENSE +0 -21
- package/skills/engineering/resolve-release/vendor/quickjs/core/README.md +0 -341
- package/skills/engineering/resolve-release/vendor/quickjs/core/README.template.md +0 -70
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs +0 -7
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.mts +0 -2033
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.ts +0 -2033
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js +0 -7
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/package.json +0 -49
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/LICENSE +0 -21
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/README.md +0 -5
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.mts +0 -549
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.ts +0 -549
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/package.json +0 -36
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/LICENSE +0 -47
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/README.md +0 -82
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.d.ts +0 -11
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.mjs +0 -22
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cjs +0 -21
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.cjs +0 -21
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.d.ts +0 -11
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.d.ts +0 -11
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.mjs +0 -25
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.wasm +0 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.mts +0 -85
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.ts +0 -85
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.mts +0 -20
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.ts +0 -20
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/package.json +0 -61
- /package/skills/{engineering/resolve-issues → payment/tld-detection}/LICENSE +0 -0
|
@@ -1,752 +0,0 @@
|
|
|
1
|
-
import crypto from 'node:crypto'
|
|
2
|
-
import fs from 'node:fs'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import {
|
|
5
|
-
newQuickJSWASMModuleFromVariant,
|
|
6
|
-
shouldInterruptAfterDeadline,
|
|
7
|
-
} from '../vendor/quickjs/core/dist/index.mjs'
|
|
8
|
-
import QUICKJS_RELEASE_SYNC from '../vendor/quickjs/release-sync/dist/index.mjs'
|
|
9
|
-
|
|
10
|
-
export const ISOLATED_ADAPTER_RUNTIME = Object.freeze({
|
|
11
|
-
name: 'quickjs-emscripten',
|
|
12
|
-
version: '0.32.0',
|
|
13
|
-
variant: '@jitl/quickjs-wasmfile-release-sync',
|
|
14
|
-
})
|
|
15
|
-
export const ISOLATED_ADAPTER_LIMITS = Object.freeze({
|
|
16
|
-
executionMs: 250,
|
|
17
|
-
memoryBytes: 32 * 1024 * 1024,
|
|
18
|
-
stackBytes: 256 * 1024,
|
|
19
|
-
payloadBytes: 256 * 1024,
|
|
20
|
-
resultBytes: 1024 * 1024,
|
|
21
|
-
fileBytes: 1024 * 1024,
|
|
22
|
-
fileCount: 32,
|
|
23
|
-
pendingJobs: 1024,
|
|
24
|
-
serializationDepth: 32,
|
|
25
|
-
serializationNodes: 10_000,
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
const ADAPTER_EXPORT = 'DURABLE_PROCESSING_ADAPTER'
|
|
29
|
-
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype'])
|
|
30
|
-
const JSON_SERIALIZER_SOURCE = `
|
|
31
|
-
(() => {
|
|
32
|
-
const objectPrototype = Object.prototype
|
|
33
|
-
const arrayPrototype = Array.prototype
|
|
34
|
-
const getPrototypeOf = Object.getPrototypeOf
|
|
35
|
-
const getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors
|
|
36
|
-
const ownKeys = Reflect.ownKeys
|
|
37
|
-
const isArray = Array.isArray
|
|
38
|
-
const numberIsFinite = Number.isFinite
|
|
39
|
-
const numberIsSafeInteger = Number.isSafeInteger
|
|
40
|
-
const jsonStringify = JSON.stringify
|
|
41
|
-
const objectCreate = Object.create
|
|
42
|
-
const setConstructor = Set
|
|
43
|
-
const dangerous = new setConstructor(['__proto__', 'constructor', 'prototype'])
|
|
44
|
-
|
|
45
|
-
return (value, maxDepth, maxNodes, safeIntegers) => {
|
|
46
|
-
const seen = new setConstructor()
|
|
47
|
-
let nodes = 0
|
|
48
|
-
const visit = (input, depth) => {
|
|
49
|
-
nodes += 1
|
|
50
|
-
if (nodes > maxNodes || depth > maxDepth) throw new Error('e:adapter-result-too-large')
|
|
51
|
-
if (input === null || typeof input === 'string' || typeof input === 'boolean') return input
|
|
52
|
-
if (typeof input === 'number') {
|
|
53
|
-
if (!numberIsFinite(input) || (safeIntegers && !numberIsSafeInteger(input))) {
|
|
54
|
-
throw new Error('e:adapter-result-not-serializable')
|
|
55
|
-
}
|
|
56
|
-
return input
|
|
57
|
-
}
|
|
58
|
-
if (typeof input !== 'object') throw new Error('e:adapter-result-not-serializable')
|
|
59
|
-
if (seen.has(input)) throw new Error('e:adapter-result-not-serializable')
|
|
60
|
-
seen.add(input)
|
|
61
|
-
const prototype = getPrototypeOf(input)
|
|
62
|
-
const array = isArray(input)
|
|
63
|
-
if (array ? prototype !== arrayPrototype : prototype !== objectPrototype && prototype !== null) {
|
|
64
|
-
throw new Error('e:adapter-result-not-plain')
|
|
65
|
-
}
|
|
66
|
-
const descriptors = getOwnPropertyDescriptors(input)
|
|
67
|
-
const keys = ownKeys(input)
|
|
68
|
-
for (const key of keys) {
|
|
69
|
-
if (typeof key !== 'string' || dangerous.has(key)) {
|
|
70
|
-
throw new Error('e:adapter-result-not-plain')
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const output = array ? [] : objectCreate(null)
|
|
74
|
-
for (const key of keys) {
|
|
75
|
-
const descriptor = descriptors[key]
|
|
76
|
-
if (array && key === 'length') continue
|
|
77
|
-
if (!descriptor.enumerable || !('value' in descriptor)) throw new Error('e:adapter-result-not-plain')
|
|
78
|
-
output[key] = visit(descriptor.value, depth + 1)
|
|
79
|
-
}
|
|
80
|
-
seen.delete(input)
|
|
81
|
-
return output
|
|
82
|
-
}
|
|
83
|
-
return jsonStringify(visit(value, 0))
|
|
84
|
-
}
|
|
85
|
-
})()
|
|
86
|
-
`
|
|
87
|
-
|
|
88
|
-
const ADAPTER_INSPECTOR_SOURCE = `
|
|
89
|
-
(() => {
|
|
90
|
-
const objectPrototype = Object.prototype
|
|
91
|
-
const getPrototypeOf = Object.getPrototypeOf
|
|
92
|
-
const getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors
|
|
93
|
-
const ownKeys = Reflect.ownKeys
|
|
94
|
-
const objectCreate = Object.create
|
|
95
|
-
const setConstructor = Set
|
|
96
|
-
const dangerous = new setConstructor(["__proto__", "constructor", "prototype"])
|
|
97
|
-
return (adapter) => {
|
|
98
|
-
const prototype = getPrototypeOf(adapter)
|
|
99
|
-
if (prototype !== objectPrototype && prototype !== null) throw new Error("e:adapter-export-invalid")
|
|
100
|
-
const descriptors = getOwnPropertyDescriptors(adapter)
|
|
101
|
-
const keys = ownKeys(adapter)
|
|
102
|
-
for (const key of keys) {
|
|
103
|
-
if (typeof key !== "string" || dangerous.has(key)) throw new Error("e:adapter-export-invalid")
|
|
104
|
-
}
|
|
105
|
-
const metadata = objectCreate(null)
|
|
106
|
-
let executable = false
|
|
107
|
-
for (const key of keys) {
|
|
108
|
-
const descriptor = descriptors[key]
|
|
109
|
-
if (!descriptor.enumerable || !("value" in descriptor)) throw new Error("e:adapter-export-invalid")
|
|
110
|
-
if (key === "execute") {
|
|
111
|
-
executable = typeof descriptor.value === "function"
|
|
112
|
-
} else {
|
|
113
|
-
metadata[key] = descriptor.value
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return { metadata, executable }
|
|
117
|
-
}
|
|
118
|
-
})()
|
|
119
|
-
`
|
|
120
|
-
|
|
121
|
-
const INVOKER_SOURCE = `
|
|
122
|
-
(() => {
|
|
123
|
-
const jsonParse = JSON.parse
|
|
124
|
-
const objectFreeze = Object.freeze
|
|
125
|
-
const objectHasOwn = Object.hasOwn
|
|
126
|
-
return async (adapter, requestText, configText, snapshotsText) => {
|
|
127
|
-
const request = jsonParse(requestText)
|
|
128
|
-
const config = jsonParse(configText)
|
|
129
|
-
const snapshots = jsonParse(snapshotsText)
|
|
130
|
-
const writes = []
|
|
131
|
-
const host = objectFreeze({
|
|
132
|
-
readTextFile(target) {
|
|
133
|
-
if (typeof target !== "string" || !objectHasOwn(snapshots, target)) {
|
|
134
|
-
throw new Error("e:adapter-file-authority-forbidden")
|
|
135
|
-
}
|
|
136
|
-
for (let index = writes.length - 1; index >= 0; index -= 1) {
|
|
137
|
-
if (writes[index].target === target) return writes[index].contents
|
|
138
|
-
}
|
|
139
|
-
return snapshots[target]
|
|
140
|
-
},
|
|
141
|
-
writeTextFile(target, contents) {
|
|
142
|
-
if (typeof target !== "string" || !objectHasOwn(snapshots, target)) {
|
|
143
|
-
throw new Error("e:adapter-file-authority-forbidden")
|
|
144
|
-
}
|
|
145
|
-
if (typeof contents !== "string") throw new Error("e:adapter-file-write-invalid")
|
|
146
|
-
writes[writes.length] = { target, contents }
|
|
147
|
-
},
|
|
148
|
-
})
|
|
149
|
-
const output = await adapter.execute(request, config, host)
|
|
150
|
-
return { output, writes }
|
|
151
|
-
}
|
|
152
|
-
})()
|
|
153
|
-
`
|
|
154
|
-
|
|
155
|
-
let quickJSPromise
|
|
156
|
-
|
|
157
|
-
function fail(code) {
|
|
158
|
-
const error = new Error(code)
|
|
159
|
-
error.code = code
|
|
160
|
-
throw error
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function transactionError(code, fields = {}) {
|
|
164
|
-
const error = new Error(code)
|
|
165
|
-
error.code = code
|
|
166
|
-
Object.assign(error, fields)
|
|
167
|
-
return error
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function byteLength(value) {
|
|
171
|
-
return Buffer.byteLength(value, 'utf8')
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function boundedJson(value, maxBytes, code) {
|
|
175
|
-
let text
|
|
176
|
-
try {
|
|
177
|
-
text = JSON.stringify(value)
|
|
178
|
-
} catch {
|
|
179
|
-
fail(code)
|
|
180
|
-
}
|
|
181
|
-
if (text === undefined || byteLength(text) > maxBytes) fail(code)
|
|
182
|
-
return text
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function canonicalJson(value) {
|
|
186
|
-
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`
|
|
187
|
-
if (value != null && typeof value === 'object') {
|
|
188
|
-
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(',')}}`
|
|
189
|
-
}
|
|
190
|
-
return JSON.stringify(value)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function detachedPlain(value, code = 'e:adapter-result-not-serializable') {
|
|
194
|
-
try {
|
|
195
|
-
return JSON.parse(JSON.stringify(value))
|
|
196
|
-
} catch {
|
|
197
|
-
fail(code)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
async function quickJS() {
|
|
202
|
-
quickJSPromise ??= newQuickJSWASMModuleFromVariant(QUICKJS_RELEASE_SYNC)
|
|
203
|
-
return quickJSPromise
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function dispose(value) {
|
|
207
|
-
if (value?.alive) value.dispose()
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function disposeAll(...values) {
|
|
211
|
-
let firstError
|
|
212
|
-
for (const value of values) {
|
|
213
|
-
try {
|
|
214
|
-
dispose(value)
|
|
215
|
-
} catch (error) {
|
|
216
|
-
firstError ??= error
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
if (firstError != null) throw firstError
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function guestError(vm, handle) {
|
|
223
|
-
let dumped
|
|
224
|
-
try {
|
|
225
|
-
dumped = vm.dump(handle)
|
|
226
|
-
} catch {
|
|
227
|
-
return 'e:adapter-execution-failed'
|
|
228
|
-
}
|
|
229
|
-
const message = typeof dumped === 'object' && dumped != null ? dumped.message : String(dumped)
|
|
230
|
-
if (message === 'interrupted') return 'e:adapter-execution-timeout'
|
|
231
|
-
if (message === 'stack overflow') return 'e:adapter-stack-limit'
|
|
232
|
-
if (message === 'out of memory') return 'e:adapter-memory-limit'
|
|
233
|
-
if (typeof message === 'string' && /^e:adapter-[a-z0-9-]+$/.test(message)) return message
|
|
234
|
-
return 'e:adapter-execution-failed'
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function unwrap(vm, result) {
|
|
238
|
-
if (result.error) {
|
|
239
|
-
const code = guestError(vm, result.error)
|
|
240
|
-
dispose(result.error)
|
|
241
|
-
fail(code)
|
|
242
|
-
}
|
|
243
|
-
return result.value
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function settle(vm, runtime, handle) {
|
|
247
|
-
for (let jobs = 0; jobs <= ISOLATED_ADAPTER_LIMITS.pendingJobs; jobs += 1) {
|
|
248
|
-
const state = vm.getPromiseState(handle)
|
|
249
|
-
if (state.type === 'fulfilled') {
|
|
250
|
-
return state.notAPromise ? handle.dup() : state.value
|
|
251
|
-
}
|
|
252
|
-
if (state.type === 'rejected') {
|
|
253
|
-
const code = guestError(vm, state.error)
|
|
254
|
-
dispose(state.error)
|
|
255
|
-
fail(code)
|
|
256
|
-
}
|
|
257
|
-
if (jobs === ISOLATED_ADAPTER_LIMITS.pendingJobs || !runtime.hasPendingJob()) {
|
|
258
|
-
fail('e:adapter-execution-timeout')
|
|
259
|
-
}
|
|
260
|
-
const executed = runtime.executePendingJobs(1)
|
|
261
|
-
if (executed.error) {
|
|
262
|
-
const code = guestError(vm, executed.error)
|
|
263
|
-
dispose(executed.error)
|
|
264
|
-
fail(code)
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
fail('e:adapter-execution-timeout')
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function ownPropertyNames(vm, handle) {
|
|
271
|
-
const result = vm.getOwnPropertyNames(handle, { strings: true, symbols: true, onlyEnumerable: true })
|
|
272
|
-
const handles = unwrap(vm, result)
|
|
273
|
-
try {
|
|
274
|
-
return handles.map((item) => item.consume(vm.dump))
|
|
275
|
-
} finally {
|
|
276
|
-
handles.dispose()
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function serializeGuestValue(vm, serializer, value, maxBytes) {
|
|
281
|
-
const maxDepth = vm.newNumber(ISOLATED_ADAPTER_LIMITS.serializationDepth)
|
|
282
|
-
const maxNodes = vm.newNumber(ISOLATED_ADAPTER_LIMITS.serializationNodes)
|
|
283
|
-
let serialized
|
|
284
|
-
try {
|
|
285
|
-
serialized = unwrap(vm, vm.callFunction(
|
|
286
|
-
serializer, vm.undefined, value, maxDepth, maxNodes, vm.false,
|
|
287
|
-
))
|
|
288
|
-
if (vm.typeof(serialized) !== 'string') fail('e:adapter-result-not-serializable')
|
|
289
|
-
const text = vm.getString(serialized)
|
|
290
|
-
if (byteLength(text) > maxBytes) fail('e:adapter-result-too-large')
|
|
291
|
-
try {
|
|
292
|
-
return JSON.parse(text)
|
|
293
|
-
} catch {
|
|
294
|
-
fail('e:adapter-result-not-serializable')
|
|
295
|
-
}
|
|
296
|
-
} finally {
|
|
297
|
-
disposeAll(serialized, maxNodes, maxDepth)
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function extractMetadata(vm, serializer, inspector, moduleHandle) {
|
|
302
|
-
const names = ownPropertyNames(vm, moduleHandle)
|
|
303
|
-
if (names.length !== 1 || names[0] !== ADAPTER_EXPORT) fail('e:adapter-export-invalid')
|
|
304
|
-
const adapter = vm.getProp(moduleHandle, ADAPTER_EXPORT)
|
|
305
|
-
let inspected
|
|
306
|
-
try {
|
|
307
|
-
if (vm.typeof(adapter) !== 'object') fail('e:adapter-export-invalid')
|
|
308
|
-
inspected = unwrap(vm, vm.callFunction(inspector, vm.undefined, adapter))
|
|
309
|
-
const metadata = vm.getProp(inspected, 'metadata')
|
|
310
|
-
const executable = vm.getProp(inspected, 'executable')
|
|
311
|
-
try {
|
|
312
|
-
return {
|
|
313
|
-
metadata: serializeGuestValue(vm, serializer, metadata, ISOLATED_ADAPTER_LIMITS.resultBytes),
|
|
314
|
-
executable: vm.dump(executable) === true,
|
|
315
|
-
}
|
|
316
|
-
} finally {
|
|
317
|
-
disposeAll(executable, metadata)
|
|
318
|
-
}
|
|
319
|
-
} finally {
|
|
320
|
-
disposeAll(inspected, adapter)
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function createRuntime(QuickJS) {
|
|
325
|
-
const runtime = QuickJS.newRuntime({
|
|
326
|
-
memoryLimitBytes: ISOLATED_ADAPTER_LIMITS.memoryBytes,
|
|
327
|
-
maxStackSizeBytes: ISOLATED_ADAPTER_LIMITS.stackBytes,
|
|
328
|
-
})
|
|
329
|
-
runtime.setInterruptHandler(shouldInterruptAfterDeadline(
|
|
330
|
-
Date.now() + ISOLATED_ADAPTER_LIMITS.executionMs,
|
|
331
|
-
))
|
|
332
|
-
return runtime
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
function installGuestConsole(vm) {
|
|
336
|
-
const result = vm.evalCode(`
|
|
337
|
-
globalThis.console = Object.create(null)
|
|
338
|
-
for (const name of ['log', 'info', 'warn', 'error', 'debug']) console[name] = () => undefined
|
|
339
|
-
`, 'checked-adapter-console.js', { type: 'global', strict: true })
|
|
340
|
-
unwrap(vm, result).dispose()
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function loadGuestFunction(vm, source, filename) {
|
|
344
|
-
return unwrap(vm, vm.evalCode(source, filename, { type: 'global', strict: true }))
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function evaluateModule(vm, runtime, source, contentHash) {
|
|
348
|
-
const evaluated = unwrap(vm, vm.evalCode(source, `sha256-${contentHash}.mjs`, { type: 'module' }))
|
|
349
|
-
try {
|
|
350
|
-
return settle(vm, runtime, evaluated)
|
|
351
|
-
} finally {
|
|
352
|
-
evaluated.dispose()
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
async function withGuest(source, contentHash, run) {
|
|
357
|
-
const QuickJS = await quickJS()
|
|
358
|
-
const runtime = createRuntime(QuickJS)
|
|
359
|
-
const vm = runtime.newContext()
|
|
360
|
-
let serializer
|
|
361
|
-
let inspector
|
|
362
|
-
let invoker
|
|
363
|
-
let moduleHandle
|
|
364
|
-
try {
|
|
365
|
-
serializer = loadGuestFunction(vm, JSON_SERIALIZER_SOURCE, 'checked-adapter-serializer.js')
|
|
366
|
-
inspector = loadGuestFunction(vm, ADAPTER_INSPECTOR_SOURCE, 'checked-adapter-inspector.js')
|
|
367
|
-
invoker = loadGuestFunction(vm, INVOKER_SOURCE, 'checked-adapter-invoker.js')
|
|
368
|
-
installGuestConsole(vm)
|
|
369
|
-
moduleHandle = evaluateModule(vm, runtime, source, contentHash)
|
|
370
|
-
return run({ vm, runtime, serializer, inspector, invoker, moduleHandle })
|
|
371
|
-
} catch (error) {
|
|
372
|
-
if (error?.code) throw error
|
|
373
|
-
fail('e:adapter-execution-failed')
|
|
374
|
-
} finally {
|
|
375
|
-
disposeAll(moduleHandle, invoker, inspector, serializer, vm, runtime)
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function collectConfiguredFiles(config) {
|
|
380
|
-
const files = new Set()
|
|
381
|
-
const pending = [config]
|
|
382
|
-
const seen = new Set()
|
|
383
|
-
while (pending.length > 0) {
|
|
384
|
-
const value = pending.pop()
|
|
385
|
-
if (typeof value === 'string') {
|
|
386
|
-
if (path.isAbsolute(value)) files.add(path.resolve(value))
|
|
387
|
-
continue
|
|
388
|
-
}
|
|
389
|
-
if (value == null || typeof value !== 'object' || seen.has(value)) continue
|
|
390
|
-
seen.add(value)
|
|
391
|
-
pending.push(...(Array.isArray(value) ? value : Object.values(value)))
|
|
392
|
-
}
|
|
393
|
-
if (files.size > ISOLATED_ADAPTER_LIMITS.fileCount) fail('e:adapter-file-count-limit')
|
|
394
|
-
return [...files]
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
function snapshotConfiguredFiles(config) {
|
|
398
|
-
const snapshots = Object.create(null)
|
|
399
|
-
const identities = new Map()
|
|
400
|
-
for (const target of collectConfiguredFiles(config)) {
|
|
401
|
-
let stat
|
|
402
|
-
let contents
|
|
403
|
-
try {
|
|
404
|
-
const pathStat = fs.lstatSync(target, { bigint: true })
|
|
405
|
-
stat = fs.statSync(target, { bigint: true })
|
|
406
|
-
if (!pathStat.isFile() || !stat.isFile()
|
|
407
|
-
|| pathStat.dev !== stat.dev || pathStat.ino !== stat.ino
|
|
408
|
-
|| stat.size > BigInt(ISOLATED_ADAPTER_LIMITS.fileBytes)) {
|
|
409
|
-
fail('e:adapter-file-limit')
|
|
410
|
-
}
|
|
411
|
-
contents = fs.readFileSync(target, 'utf8')
|
|
412
|
-
} catch (error) {
|
|
413
|
-
if (error?.code?.startsWith?.('e:')) throw error
|
|
414
|
-
fail('e:adapter-file-unreadable')
|
|
415
|
-
}
|
|
416
|
-
if (byteLength(contents) > ISOLATED_ADAPTER_LIMITS.fileBytes) fail('e:adapter-file-limit')
|
|
417
|
-
snapshots[target] = contents
|
|
418
|
-
identities.set(target, {
|
|
419
|
-
dev: stat.dev, ino: stat.ino, size: stat.size, mode: stat.mode,
|
|
420
|
-
originalContents: Buffer.from(contents, 'utf8'),
|
|
421
|
-
mtimeNs: stat.mtimeNs, ctimeNs: stat.ctimeNs,
|
|
422
|
-
})
|
|
423
|
-
}
|
|
424
|
-
return { snapshots, identities }
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
function sameFileIdentity(left, right) {
|
|
428
|
-
return left.dev === right.dev && left.ino === right.ino && left.size === right.size
|
|
429
|
-
&& left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
let commitFaultInjector
|
|
433
|
-
|
|
434
|
-
// Host-test seam only. This callback is never installed in, or callable from, the guest runtime.
|
|
435
|
-
export function setAdapterCommitFaultInjectorForTests(injector) {
|
|
436
|
-
if (injector != null && typeof injector !== 'function') throw new TypeError('injector must be a function')
|
|
437
|
-
commitFaultInjector = injector
|
|
438
|
-
return () => {
|
|
439
|
-
if (commitFaultInjector === injector) commitFaultInjector = undefined
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function injectCommitFault(operation, target, index) {
|
|
444
|
-
commitFaultInjector?.(Object.freeze({ operation, target, index }))
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const UNSUPPORTED_DIRECTORY_SYNC = new Set(['EBADF', 'EINVAL', 'EISDIR', 'ENOTSUP', 'EOPNOTSUPP', 'EPERM'])
|
|
448
|
-
|
|
449
|
-
function syncDirectory(directory) {
|
|
450
|
-
let descriptor
|
|
451
|
-
try {
|
|
452
|
-
descriptor = fs.openSync(directory, fs.constants.O_RDONLY)
|
|
453
|
-
fs.fsyncSync(descriptor)
|
|
454
|
-
} catch (error) {
|
|
455
|
-
if (!UNSUPPORTED_DIRECTORY_SYNC.has(error?.code)) throw error
|
|
456
|
-
} finally {
|
|
457
|
-
if (descriptor != null) fs.closeSync(descriptor)
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
function syncDirectories(directories) {
|
|
462
|
-
for (const directory of [...directories].sort()) syncDirectory(directory)
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function artifactPath(target, nonce, index, kind) {
|
|
466
|
-
const digest = crypto.createHash('sha256').update(target).digest('hex').slice(0, 16)
|
|
467
|
-
return path.join(path.dirname(target), `.adapter-write-${digest}-${nonce}-${index}-${kind}`)
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function stageExclusive(file, contents, mode) {
|
|
471
|
-
let descriptor
|
|
472
|
-
let created = false
|
|
473
|
-
let failure
|
|
474
|
-
try {
|
|
475
|
-
descriptor = fs.openSync(
|
|
476
|
-
file,
|
|
477
|
-
fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL,
|
|
478
|
-
Number(mode & 0o7777n),
|
|
479
|
-
)
|
|
480
|
-
created = true
|
|
481
|
-
fs.writeFileSync(descriptor, contents)
|
|
482
|
-
fs.fchmodSync(descriptor, Number(mode & 0o7777n))
|
|
483
|
-
fs.fsyncSync(descriptor)
|
|
484
|
-
} catch (error) {
|
|
485
|
-
failure = error
|
|
486
|
-
}
|
|
487
|
-
if (descriptor != null) {
|
|
488
|
-
try {
|
|
489
|
-
fs.closeSync(descriptor)
|
|
490
|
-
} catch (error) {
|
|
491
|
-
failure ??= error
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
if (failure != null) {
|
|
495
|
-
if (!created) throw failure
|
|
496
|
-
try {
|
|
497
|
-
fs.unlinkSync(file)
|
|
498
|
-
} catch {
|
|
499
|
-
throw transactionError('e:adapter-write-cleanup-failed', {
|
|
500
|
-
affectedTargets: [file],
|
|
501
|
-
ambiguousEffects: false,
|
|
502
|
-
})
|
|
503
|
-
}
|
|
504
|
-
throw failure
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
function removeArtifacts(transactions) {
|
|
509
|
-
const failed = []
|
|
510
|
-
for (const transaction of transactions) {
|
|
511
|
-
for (const [artifact, owned] of [
|
|
512
|
-
[transaction.finalStage, transaction.finalOwned],
|
|
513
|
-
[transaction.originalStage, transaction.originalOwned],
|
|
514
|
-
]) {
|
|
515
|
-
if (!owned) continue
|
|
516
|
-
if (artifact == null) continue
|
|
517
|
-
try {
|
|
518
|
-
fs.unlinkSync(artifact)
|
|
519
|
-
} catch (error) {
|
|
520
|
-
if (error?.code !== 'ENOENT') failed.push(artifact)
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
return failed
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
function createCommit(writes, identities) {
|
|
528
|
-
let committed = false
|
|
529
|
-
return () => {
|
|
530
|
-
if (committed) fail('e:adapter-write-already-committed')
|
|
531
|
-
if (writes.length === 0) {
|
|
532
|
-
committed = true
|
|
533
|
-
return
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const ordered = [...writes].sort((left, right) =>
|
|
537
|
-
left.target < right.target ? -1 : left.target > right.target ? 1 : 0)
|
|
538
|
-
const nonce = crypto.randomBytes(12).toString('hex')
|
|
539
|
-
const transactions = []
|
|
540
|
-
const directories = new Set()
|
|
541
|
-
const replaced = []
|
|
542
|
-
let failure
|
|
543
|
-
|
|
544
|
-
try {
|
|
545
|
-
for (const [index, write] of ordered.entries()) {
|
|
546
|
-
const expected = identities.get(write.target)
|
|
547
|
-
if (expected == null) fail('e:adapter-file-authority-forbidden')
|
|
548
|
-
const transaction = {
|
|
549
|
-
...write,
|
|
550
|
-
expected,
|
|
551
|
-
index,
|
|
552
|
-
finalStage: artifactPath(write.target, nonce, index, 'final'),
|
|
553
|
-
originalStage: artifactPath(write.target, nonce, index, 'original'),
|
|
554
|
-
finalOwned: false,
|
|
555
|
-
originalOwned: false,
|
|
556
|
-
}
|
|
557
|
-
transactions.push(transaction)
|
|
558
|
-
directories.add(path.dirname(write.target))
|
|
559
|
-
injectCommitFault('stage-final', write.target, index)
|
|
560
|
-
stageExclusive(transaction.finalStage, write.contents, expected.mode)
|
|
561
|
-
transaction.finalOwned = true
|
|
562
|
-
injectCommitFault('stage-original', write.target, index)
|
|
563
|
-
stageExclusive(transaction.originalStage, expected.originalContents, expected.mode)
|
|
564
|
-
transaction.originalOwned = true
|
|
565
|
-
}
|
|
566
|
-
syncDirectories(directories)
|
|
567
|
-
|
|
568
|
-
// Final pre-mutation boundary: the complete identity pass directly precedes replacement.
|
|
569
|
-
for (const transaction of transactions) {
|
|
570
|
-
injectCommitFault('validate-identity', transaction.target, transaction.index)
|
|
571
|
-
let actual
|
|
572
|
-
try {
|
|
573
|
-
const pathStat = fs.lstatSync(transaction.target, { bigint: true })
|
|
574
|
-
actual = fs.statSync(transaction.target, { bigint: true })
|
|
575
|
-
if (!pathStat.isFile() || pathStat.dev !== actual.dev || pathStat.ino !== actual.ino) {
|
|
576
|
-
fail('e:adapter-file-changed')
|
|
577
|
-
}
|
|
578
|
-
} catch {
|
|
579
|
-
fail('e:adapter-file-changed')
|
|
580
|
-
}
|
|
581
|
-
if (!actual.isFile() || !sameFileIdentity(transaction.expected, actual)) {
|
|
582
|
-
fail('e:adapter-file-changed')
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
for (const transaction of transactions) {
|
|
587
|
-
injectCommitFault('replace', transaction.target, transaction.index)
|
|
588
|
-
fs.renameSync(transaction.finalStage, transaction.target)
|
|
589
|
-
transaction.finalOwned = false
|
|
590
|
-
replaced.push(transaction)
|
|
591
|
-
}
|
|
592
|
-
injectCommitFault('complete-replacements', ordered.at(-1).target, ordered.length - 1)
|
|
593
|
-
syncDirectories(directories)
|
|
594
|
-
committed = true
|
|
595
|
-
} catch (error) {
|
|
596
|
-
failure = error
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
if (failure != null && replaced.length > 0) {
|
|
600
|
-
const rollbackFailed = []
|
|
601
|
-
for (const transaction of [...replaced].reverse()) {
|
|
602
|
-
try {
|
|
603
|
-
injectCommitFault('rollback', transaction.target, transaction.index)
|
|
604
|
-
fs.renameSync(transaction.originalStage, transaction.target)
|
|
605
|
-
transaction.originalOwned = false
|
|
606
|
-
} catch {
|
|
607
|
-
rollbackFailed.push(transaction.target)
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
try {
|
|
611
|
-
syncDirectories(directories)
|
|
612
|
-
} catch {
|
|
613
|
-
for (const transaction of replaced) {
|
|
614
|
-
if (!rollbackFailed.includes(transaction.target)) rollbackFailed.push(transaction.target)
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
const cleanupFailed = removeArtifacts(transactions)
|
|
618
|
-
try {
|
|
619
|
-
syncDirectories(directories)
|
|
620
|
-
} catch {
|
|
621
|
-
// The target replacement result above already determines rollback ambiguity.
|
|
622
|
-
}
|
|
623
|
-
if (rollbackFailed.length > 0) {
|
|
624
|
-
throw transactionError('e:adapter-write-rollback-failed', {
|
|
625
|
-
affectedTargets: rollbackFailed.sort(),
|
|
626
|
-
ambiguousEffects: true,
|
|
627
|
-
cleanupFailed,
|
|
628
|
-
})
|
|
629
|
-
}
|
|
630
|
-
if (cleanupFailed.length > 0) {
|
|
631
|
-
throw transactionError('e:adapter-write-cleanup-failed', {
|
|
632
|
-
affectedTargets: cleanupFailed.sort(),
|
|
633
|
-
ambiguousEffects: false,
|
|
634
|
-
})
|
|
635
|
-
}
|
|
636
|
-
throw transactionError('e:adapter-write-failed', {
|
|
637
|
-
affectedTargets: ordered.map(({ target }) => target),
|
|
638
|
-
})
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
const cleanupFailed = removeArtifacts(transactions)
|
|
642
|
-
let cleanupSyncFailed = false
|
|
643
|
-
try {
|
|
644
|
-
syncDirectories(directories)
|
|
645
|
-
} catch {
|
|
646
|
-
cleanupSyncFailed = true
|
|
647
|
-
}
|
|
648
|
-
if (cleanupFailed.length > 0 || cleanupSyncFailed) {
|
|
649
|
-
throw transactionError('e:adapter-write-cleanup-failed', {
|
|
650
|
-
affectedTargets: cleanupFailed.length > 0
|
|
651
|
-
? cleanupFailed.sort()
|
|
652
|
-
: ordered.map(({ target }) => target),
|
|
653
|
-
committedEffects: committed,
|
|
654
|
-
})
|
|
655
|
-
}
|
|
656
|
-
if (failure != null) {
|
|
657
|
-
if (failure?.code === 'e:adapter-file-changed'
|
|
658
|
-
|| failure?.code === 'e:adapter-write-cleanup-failed'
|
|
659
|
-
|| failure?.code === 'e:adapter-file-authority-forbidden') throw failure
|
|
660
|
-
throw transactionError('e:adapter-write-failed', {
|
|
661
|
-
affectedTargets: ordered.map(({ target }) => target),
|
|
662
|
-
})
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
function validateWrites(value, identities) {
|
|
668
|
-
if (!Array.isArray(value) || value.length > ISOLATED_ADAPTER_LIMITS.fileCount) {
|
|
669
|
-
fail('e:adapter-file-count-limit')
|
|
670
|
-
}
|
|
671
|
-
const finalByTarget = new Map()
|
|
672
|
-
for (const write of value) {
|
|
673
|
-
if (write == null || typeof write !== 'object' || Array.isArray(write)
|
|
674
|
-
|| Object.keys(write).length !== 2 || typeof write.target !== 'string'
|
|
675
|
-
|| typeof write.contents !== 'string' || !identities.has(path.resolve(write.target))) {
|
|
676
|
-
fail('e:adapter-file-authority-forbidden')
|
|
677
|
-
}
|
|
678
|
-
if (byteLength(write.contents) > ISOLATED_ADAPTER_LIMITS.fileBytes) fail('e:adapter-file-limit')
|
|
679
|
-
const target = path.resolve(write.target)
|
|
680
|
-
finalByTarget.set(target, Object.freeze({ target, contents: write.contents }))
|
|
681
|
-
}
|
|
682
|
-
return [...finalByTarget.values()]
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
export async function inspectIsolatedAdapter({ source, bytes, contentHash }) {
|
|
686
|
-
if (typeof source !== 'string' || !Buffer.isBuffer(bytes)
|
|
687
|
-
|| crypto.createHash('sha256').update(bytes).digest('hex') !== contentHash
|
|
688
|
-
|| Buffer.compare(bytes, Buffer.from(source, 'utf8')) !== 0) {
|
|
689
|
-
fail('e:adapter-content-identity-mismatch')
|
|
690
|
-
}
|
|
691
|
-
const boundBytes = Buffer.from(bytes)
|
|
692
|
-
return withGuest(source, contentHash, ({ vm, serializer, inspector, moduleHandle }) => {
|
|
693
|
-
const { metadata, executable } = extractMetadata(vm, serializer, inspector, moduleHandle)
|
|
694
|
-
const boundMetadata = detachedPlain(metadata)
|
|
695
|
-
const execute = executable
|
|
696
|
-
? async (request, config) => executeIsolatedAdapter({
|
|
697
|
-
source, bytes: boundBytes, contentHash, expectedMetadata: boundMetadata, request, config,
|
|
698
|
-
})
|
|
699
|
-
: undefined
|
|
700
|
-
const adapter = Object.freeze({ ...boundMetadata, ...(execute ? { execute } : {}) })
|
|
701
|
-
return Object.freeze({ module: Object.freeze({ [ADAPTER_EXPORT]: adapter }) })
|
|
702
|
-
})
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
export async function executeIsolatedAdapter({ source, bytes, contentHash, expectedMetadata, request, config }) {
|
|
706
|
-
if (crypto.createHash('sha256').update(bytes).digest('hex') !== contentHash
|
|
707
|
-
|| Buffer.compare(bytes, Buffer.from(source, 'utf8')) !== 0) {
|
|
708
|
-
fail('e:adapter-content-identity-mismatch')
|
|
709
|
-
}
|
|
710
|
-
if (expectedMetadata == null || typeof expectedMetadata !== 'object') {
|
|
711
|
-
fail('e:adapter-export-invalid')
|
|
712
|
-
}
|
|
713
|
-
const requestText = boundedJson(request, ISOLATED_ADAPTER_LIMITS.payloadBytes, 'e:adapter-payload-too-large')
|
|
714
|
-
const configText = boundedJson(config, ISOLATED_ADAPTER_LIMITS.payloadBytes, 'e:adapter-payload-too-large')
|
|
715
|
-
const { snapshots, identities } = snapshotConfiguredFiles(config)
|
|
716
|
-
const snapshotsText = boundedJson(snapshots, ISOLATED_ADAPTER_LIMITS.fileBytes * ISOLATED_ADAPTER_LIMITS.fileCount,
|
|
717
|
-
'e:adapter-file-limit')
|
|
718
|
-
return withGuest(source, contentHash, ({ vm, runtime, serializer, inspector, invoker, moduleHandle }) => {
|
|
719
|
-
const current = extractMetadata(vm, serializer, inspector, moduleHandle)
|
|
720
|
-
if (!current.executable || canonicalJson(current.metadata) !== canonicalJson(expectedMetadata)) {
|
|
721
|
-
fail('e:adapter-export-changed')
|
|
722
|
-
}
|
|
723
|
-
const adapter = vm.getProp(moduleHandle, ADAPTER_EXPORT)
|
|
724
|
-
const requestHandle = vm.newString(requestText)
|
|
725
|
-
const configHandle = vm.newString(configText)
|
|
726
|
-
const snapshotsHandle = vm.newString(snapshotsText)
|
|
727
|
-
let called
|
|
728
|
-
try {
|
|
729
|
-
called = unwrap(vm, vm.callFunction(
|
|
730
|
-
invoker, vm.undefined, adapter, requestHandle, configHandle, snapshotsHandle,
|
|
731
|
-
))
|
|
732
|
-
} finally {
|
|
733
|
-
disposeAll(snapshotsHandle, configHandle, requestHandle, adapter)
|
|
734
|
-
}
|
|
735
|
-
let settled
|
|
736
|
-
try {
|
|
737
|
-
settled = settle(vm, runtime, called)
|
|
738
|
-
const envelope = serializeGuestValue(vm, serializer, settled, ISOLATED_ADAPTER_LIMITS.resultBytes)
|
|
739
|
-
if (envelope == null || typeof envelope !== 'object' || Array.isArray(envelope)
|
|
740
|
-
|| !Object.hasOwn(envelope, 'output') || !Object.hasOwn(envelope, 'writes')) {
|
|
741
|
-
fail('e:adapter-result-not-serializable')
|
|
742
|
-
}
|
|
743
|
-
const output = detachedPlain(envelope.output)
|
|
744
|
-
boundedJson(output, ISOLATED_ADAPTER_LIMITS.resultBytes, 'e:adapter-result-too-large')
|
|
745
|
-
const writes = validateWrites(envelope.writes, identities)
|
|
746
|
-
return Object.freeze({ output, commit: createCommit(writes, identities) })
|
|
747
|
-
} finally {
|
|
748
|
-
dispose(settled)
|
|
749
|
-
called.dispose()
|
|
750
|
-
}
|
|
751
|
-
})
|
|
752
|
-
}
|