Add PHP Analyzer Plugin and Composer Lock Data Handling
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implemented the PhpAnalyzerPlugin to analyze PHP projects. - Created ComposerLockData class to represent data from composer.lock files. - Developed ComposerLockReader to load and parse composer.lock files asynchronously. - Introduced ComposerPackage class to encapsulate package details. - Added PhpPackage class to represent PHP packages with metadata and evidence. - Implemented PhpPackageCollector to gather packages from ComposerLockData. - Created PhpLanguageAnalyzer to perform analysis and emit results. - Added capability signals for known PHP frameworks and CMS. - Developed unit tests for the PHP language analyzer and its components. - Included sample composer.lock and expected output for testing. - Updated project files for the new PHP analyzer library and tests.
This commit is contained in:
@@ -174,6 +174,9 @@ public sealed class DenoLanguageAnalyzer : ILanguageAnalyzer
|
||||
metadata: runtimeMeta,
|
||||
view: "runtime");
|
||||
|
||||
analysisStore.Set(ScanAnalysisKeys.DenoRuntimePayload, payload);
|
||||
|
||||
// Backward compatibility with early runtime experiments that used a string key.
|
||||
analysisStore.Set("deno.runtime", payload);
|
||||
|
||||
// Also emit policy signals into AnalysisStore for downstream consumption.
|
||||
|
||||
@@ -24,123 +24,460 @@ internal static class DenoRuntimeShim
|
||||
|
||||
// NOTE: This shim is intentionally self contained and avoids network calls.
|
||||
private const string ShimSource = """
|
||||
// @ts-nocheck
|
||||
// deno-runtime trace shim (offline, deterministic)
|
||||
// Emits module load, permission use, npm resolution, and wasm load events.
|
||||
const events: Array<Record<string, unknown>> = [];
|
||||
const cwd = Deno.cwd().replace(/\\/g, "/");
|
||||
const entrypointEnv = Deno.env.get("STELLA_DENO_ENTRYPOINT") ?? "";
|
||||
|
||||
type ModuleRef = { normalized: string; path_sha256: string };
|
||||
|
||||
function nowIso(): string {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
function addEvent(evt: Record<string, unknown>) {
|
||||
// Deterministic key order via stringify on object literal insertion order.
|
||||
events.push(evt);
|
||||
}
|
||||
|
||||
function hashPath(input: string): string {
|
||||
const data = new TextEncoder().encode(input);
|
||||
const hash = crypto.subtle.digestSync("SHA-256", data);
|
||||
return Array.from(new Uint8Array(hash))
|
||||
function toHex(bytes: Uint8Array): string {
|
||||
return Array.from(bytes)
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
function relPath(abs: string): { normalized: string; path_sha256: string } {
|
||||
const cwd = Deno.cwd();
|
||||
const rel = abs.startsWith(cwd) ? abs.slice(cwd.length + 1) : abs;
|
||||
const normalized = rel.replaceAll("\\", "/");
|
||||
return { normalized, path_sha256: hashPath(normalized) };
|
||||
// Minimal synchronous SHA-256 (no async crypto required)
|
||||
function sha256Hex(value: string): string {
|
||||
const data = new TextEncoder().encode(value);
|
||||
const k = new Uint32Array([
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
|
||||
0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
|
||||
0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
|
||||
0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
|
||||
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
||||
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
|
||||
0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
|
||||
0xc67178f2,
|
||||
]);
|
||||
|
||||
const h = new Uint32Array([
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||
]);
|
||||
|
||||
const bitLength = data.length * 8;
|
||||
const paddedLength = (((data.length + 9 + 63) >> 6) << 6);
|
||||
const buffer = new Uint8Array(paddedLength);
|
||||
buffer.set(data);
|
||||
buffer[data.length] = 0x80;
|
||||
|
||||
const view = new DataView(buffer.buffer);
|
||||
const high = Math.floor(bitLength / 0x100000000);
|
||||
const low = bitLength >>> 0;
|
||||
view.setUint32(paddedLength - 8, high, false);
|
||||
view.setUint32(paddedLength - 4, low, false);
|
||||
|
||||
const w = new Uint32Array(64);
|
||||
for (let offset = 0; offset < paddedLength; offset += 64) {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
w[i] = view.getUint32(offset + i * 4, false);
|
||||
}
|
||||
for (let i = 16; i < 64; i++) {
|
||||
const s0 = rotateRight(w[i - 15], 7) ^ rotateRight(w[i - 15], 18) ^ (w[i - 15] >>> 3);
|
||||
const s1 = rotateRight(w[i - 2], 17) ^ rotateRight(w[i - 2], 19) ^ (w[i - 2] >>> 10);
|
||||
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
|
||||
}
|
||||
|
||||
let [a, b, c, d, e, f, g, hh] = h;
|
||||
for (let i = 0; i < 64; i++) {
|
||||
const S1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
|
||||
const ch = (e & f) ^ (~e & g);
|
||||
const temp1 = (hh + S1 + ch + k[i] + w[i]) >>> 0;
|
||||
const S0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
|
||||
const maj = (a & b) ^ (a & c) ^ (b & c);
|
||||
const temp2 = (S0 + maj) >>> 0;
|
||||
|
||||
hh = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = (d + temp1) >>> 0;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = (temp1 + temp2) >>> 0;
|
||||
}
|
||||
|
||||
h[0] = (h[0] + a) >>> 0;
|
||||
h[1] = (h[1] + b) >>> 0;
|
||||
h[2] = (h[2] + c) >>> 0;
|
||||
h[3] = (h[3] + d) >>> 0;
|
||||
h[4] = (h[4] + e) >>> 0;
|
||||
h[5] = (h[5] + f) >>> 0;
|
||||
h[6] = (h[6] + g) >>> 0;
|
||||
h[7] = (h[7] + hh) >>> 0;
|
||||
}
|
||||
|
||||
const out = new Uint8Array(32);
|
||||
const viewOut = new DataView(out.buffer);
|
||||
for (let i = 0; i < 8; i++) {
|
||||
viewOut.setUint32(i * 4, h[i], false);
|
||||
}
|
||||
|
||||
return toHex(out);
|
||||
}
|
||||
|
||||
// Wrap permission requests
|
||||
const originalPermissions = Deno.permissions;
|
||||
Deno.permissions = {
|
||||
...originalPermissions,
|
||||
request: async (...args: Parameters<typeof originalPermissions.request>) => {
|
||||
const res = await originalPermissions.request(...args);
|
||||
const name = args[0]?.name ?? "unknown";
|
||||
const module = relPath(import.meta.url);
|
||||
addEvent({
|
||||
type: "deno.permission.use",
|
||||
ts: nowIso(),
|
||||
permission: name,
|
||||
module,
|
||||
details: "permissions.request",
|
||||
});
|
||||
return res;
|
||||
},
|
||||
query: (...args: Parameters<typeof originalPermissions.query>) =>
|
||||
originalPermissions.query(...args),
|
||||
revoke: (...args: Parameters<typeof originalPermissions.revoke>) =>
|
||||
originalPermissions.revoke(...args),
|
||||
};
|
||||
function rotateRight(value: number, bits: number): number {
|
||||
return ((value >>> bits) | (value << (32 - bits))) >>> 0;
|
||||
}
|
||||
|
||||
// Hook dynamic import calls by wrapping import()
|
||||
const originalImport = globalThis.import ?? ((specifier: string) => import(specifier));
|
||||
globalThis.import = async (specifier: string) => {
|
||||
const mod = typeof specifier === "string" ? specifier : String(specifier);
|
||||
addEvent({
|
||||
function normalizePermission(name: string | undefined): string {
|
||||
const normalized = (name ?? "").toLowerCase();
|
||||
switch (normalized) {
|
||||
case "read":
|
||||
case "write":
|
||||
return "fs";
|
||||
case "net":
|
||||
return "net";
|
||||
case "env":
|
||||
return "env";
|
||||
case "ffi":
|
||||
return "ffi";
|
||||
case "run":
|
||||
case "sys":
|
||||
case "hrtime":
|
||||
return "process";
|
||||
case "worker":
|
||||
return "worker";
|
||||
default:
|
||||
return normalized || "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
const grantedPermissions = new Set<string>();
|
||||
const originalPermissions = Deno.permissions;
|
||||
|
||||
async function primePermissionSnapshot() {
|
||||
const descriptors: Array<Deno.PermissionDescriptor> = [
|
||||
{ name: "read" },
|
||||
{ name: "write" },
|
||||
{ name: "net" },
|
||||
{ name: "env" },
|
||||
{ name: "ffi" },
|
||||
{ name: "run" as Deno.PermissionName },
|
||||
{ name: "sys" as Deno.PermissionName },
|
||||
{ name: "hrtime" as Deno.PermissionName },
|
||||
];
|
||||
|
||||
for (const descriptor of descriptors) {
|
||||
try {
|
||||
const status = await originalPermissions.query(descriptor as Deno.PermissionDescriptor);
|
||||
if (status?.state === "granted") {
|
||||
grantedPermissions.add(normalizePermission(descriptor.name as string));
|
||||
}
|
||||
} catch (_) {
|
||||
// ignore permission probes that are unsupported in the current runtime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function snapshotPermissions(): string[] {
|
||||
return Array.from(grantedPermissions).sort();
|
||||
}
|
||||
|
||||
function relativePath(path: string): string {
|
||||
let candidate = path.replace(/\\/g, "/");
|
||||
|
||||
if (candidate.startsWith("file://")) {
|
||||
candidate = candidate.slice("file://".length);
|
||||
}
|
||||
|
||||
if (!candidate.startsWith("/") && !/^([A-Za-z]:\\\\|[A-Za-z]:\\/)/.test(candidate)) {
|
||||
candidate = `${cwd}/${candidate}`;
|
||||
}
|
||||
|
||||
if (candidate.startsWith(cwd)) {
|
||||
const offset = cwd.endsWith("/") ? cwd.length : cwd.length + 1;
|
||||
candidate = candidate.slice(offset);
|
||||
}
|
||||
|
||||
candidate = candidate.replace(/^\.\//, "").replace(/^\/+/, "");
|
||||
return candidate.replace(/\\/g, "/") || ".";
|
||||
}
|
||||
|
||||
function toFileUrl(path: string): URL {
|
||||
const normalized = path.replace(/\\/g, "/");
|
||||
if (normalized.startsWith("file://")) {
|
||||
return new URL(normalized);
|
||||
}
|
||||
|
||||
const absolute = normalized.startsWith("/") || /^([A-Za-z]:\\\\|[A-Za-z]:\\/)/.test(normalized)
|
||||
? normalized
|
||||
: `${cwd}/${normalized}`;
|
||||
|
||||
const prefix = absolute.startsWith("/") ? "file://" : "file:///";
|
||||
return new URL(prefix + encodeURI(absolute.replace(/#/g, "%23")));
|
||||
}
|
||||
|
||||
function normalizeModule(specifier: string): ModuleRef {
|
||||
try {
|
||||
const url = new URL(specifier);
|
||||
if (url.protocol === "file:") {
|
||||
const rel = relativePath(decodeURIComponent(url.pathname));
|
||||
return { normalized: rel, path_sha256: sha256Hex(rel) };
|
||||
}
|
||||
|
||||
if (url.protocol === "http:" || url.protocol === "https:") {
|
||||
const normalized = `${url.protocol}//${url.host}${url.pathname}`;
|
||||
return { normalized, path_sha256: sha256Hex(normalized) };
|
||||
}
|
||||
|
||||
if (url.protocol === "npm:") {
|
||||
const normalized = `npm:${url.pathname.replace(/^\//, "")}`;
|
||||
return { normalized, path_sha256: sha256Hex(normalized) };
|
||||
}
|
||||
} catch (_err) {
|
||||
// not a URL; treat as path
|
||||
}
|
||||
|
||||
const rel = relativePath(specifier);
|
||||
return { normalized: rel, path_sha256: sha256Hex(rel) };
|
||||
}
|
||||
|
||||
function extractOrigin(specifier: string): string | undefined {
|
||||
try {
|
||||
const url = new URL(specifier);
|
||||
if (url.protocol === "http:" || url.protocol === "https:") {
|
||||
return `${url.protocol}//${url.host}${url.pathname}`;
|
||||
}
|
||||
if (url.protocol === "npm:") {
|
||||
return `npm:${url.pathname.replace(/^\//, "")}`;
|
||||
}
|
||||
} catch (_) {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function addEvent(evt: Record<string, unknown>) {
|
||||
events.push(evt);
|
||||
}
|
||||
|
||||
function recordModuleLoad(specifier: string, reason: string, permissions?: string[]) {
|
||||
const module = normalizeModule(specifier);
|
||||
const origin = extractOrigin(specifier);
|
||||
const event: Record<string, unknown> = {
|
||||
type: "deno.module.load",
|
||||
ts: nowIso(),
|
||||
module: relPath(mod),
|
||||
reason: "dynamic-import",
|
||||
permissions: [],
|
||||
origin: mod.startsWith("http") ? mod : undefined,
|
||||
});
|
||||
return originalImport(specifier);
|
||||
};
|
||||
module,
|
||||
reason,
|
||||
permissions: permissions ?? snapshotPermissions(),
|
||||
};
|
||||
|
||||
if (origin) {
|
||||
event.origin = origin;
|
||||
}
|
||||
|
||||
addEvent(event);
|
||||
|
||||
if (specifier.startsWith("npm:")) {
|
||||
recordNpmResolution(specifier);
|
||||
}
|
||||
}
|
||||
|
||||
function recordPermissionUse(permission: string, details: string, module?: ModuleRef) {
|
||||
const normalizedPermission = normalizePermission(permission);
|
||||
if (normalizedPermission && normalizedPermission !== "unknown") {
|
||||
grantedPermissions.add(normalizedPermission);
|
||||
}
|
||||
|
||||
// Hook WebAssembly loads
|
||||
const originalInstantiate = WebAssembly.instantiate;
|
||||
WebAssembly.instantiate = async (
|
||||
bufferSource: BufferSource | WebAssembly.Module,
|
||||
importObject?: WebAssembly.Imports,
|
||||
) => {
|
||||
addEvent({
|
||||
type: "deno.wasm.load",
|
||||
type: "deno.permission.use",
|
||||
ts: nowIso(),
|
||||
module: relPath("wasm://buffer"),
|
||||
importer: relPath(import.meta.url).normalized,
|
||||
reason: "instantiate",
|
||||
permission: normalizedPermission,
|
||||
module: module ?? normalizeModule(entrypointEnv || "shim://runtime"),
|
||||
details,
|
||||
});
|
||||
return originalInstantiate(bufferSource, importObject);
|
||||
};
|
||||
}
|
||||
|
||||
function recordNpmResolution(specifier: string) {
|
||||
const bare = specifier.replace(/^npm:/, "");
|
||||
const [pkg, version] = bare.split("@");
|
||||
const denoDir = (Deno.env.get("DENO_DIR") ?? "").replace(/\\/g, "/");
|
||||
const resolved = denoDir
|
||||
? `file://${denoDir}/npm/registry.npmjs.org/${pkg ?? bare}/${version ?? ""}`
|
||||
: `npm:${bare}`;
|
||||
|
||||
// Capture npm resolution hints from env when present
|
||||
const npmMeta = Deno.env.get("STELLA_NPM_SPECIFIER");
|
||||
if (npmMeta) {
|
||||
addEvent({
|
||||
type: "deno.npm.resolution",
|
||||
ts: nowIso(),
|
||||
specifier: npmMeta,
|
||||
package: npmMeta,
|
||||
version: "",
|
||||
resolved: "file://$DENO_DIR/npm",
|
||||
specifier: `npm:${bare}`,
|
||||
package: pkg ?? bare,
|
||||
version: version ?? "",
|
||||
resolved,
|
||||
exists: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Write NDJSON on exit
|
||||
function flush() {
|
||||
const sorted = events.sort((a, b) => {
|
||||
const at = String(a.ts);
|
||||
const bt = String(b.ts);
|
||||
if (at === bt) return String(a.type).localeCompare(String(b.type));
|
||||
return at.localeCompare(bt);
|
||||
function recordWasmLoad(moduleSpecifier: string, importer: string, reason: string) {
|
||||
addEvent({
|
||||
type: "deno.wasm.load",
|
||||
ts: nowIso(),
|
||||
module: normalizeModule(moduleSpecifier),
|
||||
importer,
|
||||
reason,
|
||||
});
|
||||
const data = sorted.map((e) => JSON.stringify(e)).join("\\n") + "\\n";
|
||||
Deno.writeTextFileSync("deno-runtime.ndjson", data);
|
||||
}
|
||||
|
||||
addEvent({
|
||||
type: "deno.runtime.start",
|
||||
ts: nowIso(),
|
||||
module: relPath(import.meta.url),
|
||||
reason: "shim-start",
|
||||
});
|
||||
function hookModuleLoader(): boolean {
|
||||
try {
|
||||
const internal = (Deno as unknown as Record<string, unknown>)[Symbol.for("Deno.internal") as unknown as string]
|
||||
?? (Deno as unknown as Record<string, unknown>).internal;
|
||||
const loader = (internal as Record<string, unknown>)?.moduleLoader as Record<string, unknown> | undefined;
|
||||
if (!loader || typeof loader.load !== "function") {
|
||||
return false;
|
||||
}
|
||||
|
||||
globalThis.addEventListener("unload", () => {
|
||||
flush();
|
||||
});
|
||||
const originalLoad = loader.load.bind(loader) as (...args: unknown[]) => Promise<unknown>;
|
||||
loader.load = async (...args: unknown[]) => {
|
||||
const specifier = String(args[0] ?? "");
|
||||
const isDynamic = Boolean(args[2]);
|
||||
const reason = specifier.startsWith("npm:") ? "npm" : isDynamic ? "dynamic-import" : "static-import";
|
||||
recordModuleLoad(specifier, reason);
|
||||
return await originalLoad(...args);
|
||||
};
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
addEvent({ type: "deno.runtime.error", ts: nowIso(), message: String(err?.message ?? err) });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function wrapPermissions(entryModule: ModuleRef) {
|
||||
Deno.permissions = {
|
||||
...originalPermissions,
|
||||
request: async (...args: Parameters<typeof originalPermissions.request>) => {
|
||||
const status = await originalPermissions.request(...args);
|
||||
recordPermissionUse(args[0]?.name ?? "unknown", "permissions.request", entryModule);
|
||||
if (status?.state === "granted") {
|
||||
grantedPermissions.add(normalizePermission(args[0]?.name));
|
||||
}
|
||||
return status;
|
||||
},
|
||||
query: async (...args: Parameters<typeof originalPermissions.query>) => {
|
||||
const status = await originalPermissions.query(...args);
|
||||
if (status?.state === "granted") {
|
||||
grantedPermissions.add(normalizePermission(args[0]?.name));
|
||||
}
|
||||
return status;
|
||||
},
|
||||
revoke: async (...args: Parameters<typeof originalPermissions.revoke>) => {
|
||||
const status = await originalPermissions.revoke(...args);
|
||||
grantedPermissions.delete(normalizePermission(args[0]?.name));
|
||||
return status;
|
||||
},
|
||||
} as typeof Deno.permissions;
|
||||
}
|
||||
|
||||
function wrapDlopen(entryModule: ModuleRef) {
|
||||
const original = (Deno as unknown as Record<string, unknown>).dlopen as
|
||||
| ((path: string | URL, symbols: Record<string, string | Deno.ForeignFunctionDefinition>) => unknown)
|
||||
| undefined;
|
||||
|
||||
if (typeof original !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
(Deno as unknown as Record<string, unknown>).dlopen = (path: string | URL, symbols: Record<string, string | Deno.ForeignFunctionDefinition>) => {
|
||||
recordPermissionUse("ffi", "Deno.dlopen", entryModule);
|
||||
return original(path, symbols);
|
||||
};
|
||||
}
|
||||
|
||||
function wrapWasm(importer: ModuleRef) {
|
||||
const originalInstantiate = WebAssembly.instantiate;
|
||||
WebAssembly.instantiate = async (
|
||||
bufferSource: BufferSource | WebAssembly.Module,
|
||||
importObject?: WebAssembly.Imports,
|
||||
) => {
|
||||
recordWasmLoad("wasm://buffer", importer.normalized, "instantiate");
|
||||
return await originalInstantiate(bufferSource, importObject);
|
||||
};
|
||||
|
||||
const originalInstantiateStreaming = WebAssembly.instantiateStreaming;
|
||||
if (originalInstantiateStreaming) {
|
||||
WebAssembly.instantiateStreaming = async (
|
||||
source: Response | Promise<Response>,
|
||||
importObject?: WebAssembly.Imports,
|
||||
) => {
|
||||
try {
|
||||
const response = await source;
|
||||
const url = response?.url || "wasm://stream";
|
||||
recordWasmLoad(url, importer.normalized, "instantiateStreaming");
|
||||
} catch (_) {
|
||||
recordWasmLoad("wasm://stream", importer.normalized, "instantiateStreaming");
|
||||
}
|
||||
return await originalInstantiateStreaming(source as Response, importObject);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function flush() {
|
||||
try {
|
||||
const sorted = events.sort((a, b) => {
|
||||
const at = String(a.ts);
|
||||
const bt = String(b.ts);
|
||||
if (at === bt) return String(a.type).localeCompare(String(b.type));
|
||||
return at.localeCompare(bt);
|
||||
});
|
||||
|
||||
const data = sorted.map((e) => JSON.stringify(e)).join("
|
||||
");
|
||||
Deno.writeTextFileSync("deno-runtime.ndjson", data ? `${data}
|
||||
` : "");
|
||||
} catch (err) {
|
||||
// last-resort logging; avoid throwing
|
||||
console.error("deno-runtime shim failed to write trace", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (!entrypointEnv) {
|
||||
addEvent({ type: "deno.runtime.error", ts: nowIso(), message: "STELLA_DENO_ENTRYPOINT missing" });
|
||||
flush();
|
||||
return;
|
||||
}
|
||||
|
||||
const entryUrl = toFileUrl(entrypointEnv);
|
||||
const entryModule = normalizeModule(entryUrl.href);
|
||||
|
||||
addEvent({
|
||||
type: "deno.runtime.start",
|
||||
ts: nowIso(),
|
||||
module: entryModule,
|
||||
reason: "shim-start",
|
||||
});
|
||||
|
||||
await primePermissionSnapshot();
|
||||
const loaderHooked = hookModuleLoader();
|
||||
wrapPermissions(entryModule);
|
||||
wrapDlopen(entryModule);
|
||||
wrapWasm(entryModule);
|
||||
|
||||
if (!loaderHooked) {
|
||||
recordModuleLoad(entryUrl.href, "static-import", snapshotPermissions());
|
||||
}
|
||||
|
||||
try {
|
||||
await import(entryUrl.href);
|
||||
} catch (err) {
|
||||
addEvent({ type: "deno.runtime.error", ts: nowIso(), message: String(err?.message ?? err) });
|
||||
} finally {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
globalThis.addEventListener("unload", flush);
|
||||
await main();
|
||||
""";
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user