@ricsam/quickjs-test-utils 0.0.1 → 1.0.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 +97 -43
- package/dist/cjs/context.cjs +86 -0
- package/dist/cjs/context.cjs.map +10 -0
- package/dist/cjs/eval.cjs +69 -0
- package/dist/cjs/eval.cjs.map +10 -0
- package/dist/cjs/fetch-context.cjs +89 -0
- package/dist/cjs/fetch-context.cjs.map +10 -0
- package/dist/cjs/fs-context.cjs +54 -0
- package/dist/cjs/fs-context.cjs.map +10 -0
- package/dist/cjs/index.cjs +58 -0
- package/dist/cjs/index.cjs.map +10 -0
- package/dist/cjs/integration-server.cjs +137 -0
- package/dist/cjs/integration-server.cjs.map +10 -0
- package/dist/cjs/package.json +5 -0
- package/dist/cjs/quickjs-types.cjs +700 -0
- package/dist/cjs/quickjs-types.cjs.map +10 -0
- package/dist/cjs/runtime-context.cjs +55 -0
- package/dist/cjs/runtime-context.cjs.map +10 -0
- package/dist/cjs/typecheck.cjs +108 -0
- package/dist/cjs/typecheck.cjs.map +10 -0
- package/dist/mjs/context.mjs +61 -0
- package/dist/mjs/context.mjs.map +10 -0
- package/dist/mjs/eval.mjs +38 -0
- package/dist/mjs/eval.mjs.map +10 -0
- package/dist/mjs/fetch-context.mjs +61 -0
- package/dist/mjs/fetch-context.mjs.map +10 -0
- package/dist/mjs/fs-context.mjs +29 -0
- package/dist/mjs/fs-context.mjs.map +10 -0
- package/dist/mjs/index.mjs +45 -0
- package/dist/mjs/index.mjs.map +10 -0
- package/dist/mjs/integration-server.mjs +106 -0
- package/dist/mjs/integration-server.mjs.map +10 -0
- package/dist/mjs/package.json +5 -0
- package/dist/mjs/quickjs-types.mjs +669 -0
- package/dist/mjs/quickjs-types.mjs.map +10 -0
- package/dist/mjs/runtime-context.mjs +26 -0
- package/dist/mjs/runtime-context.mjs.map +10 -0
- package/dist/mjs/typecheck.mjs +77 -0
- package/dist/mjs/typecheck.mjs.map +10 -0
- package/dist/types/context.d.ts +35 -0
- package/dist/types/eval.d.ts +31 -0
- package/dist/types/fetch-context.d.ts +41 -0
- package/dist/types/fs-context.d.ts +12 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/integration-server.d.ts +39 -0
- package/dist/types/quickjs-types.d.ts +42 -0
- package/dist/types/runtime-context.d.ts +9 -0
- package/dist/types/typecheck.d.ts +115 -0
- package/package.json +62 -6
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/quickjs-types.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * QuickJS type definitions as string constants.\n *\n * These are the canonical source for QuickJS global type definitions.\n * The .d.ts files in each package are generated from these strings during build.\n *\n * @example\n * import { TYPE_DEFINITIONS } from \"@ricsam/quickjs-test-utils\";\n *\n * // Use with ts-morph for type checking code strings\n * project.createSourceFile(\"types.d.ts\", TYPE_DEFINITIONS.fetch);\n */\n\n/**\n * Type definitions for @ricsam/quickjs-core globals.\n *\n * Includes: ReadableStream, WritableStream, TransformStream, Blob, File, URL, URLSearchParams, DOMException\n */\nexport const CORE_TYPES = `/**\n * QuickJS Global Type Definitions for @ricsam/quickjs-core\n *\n * These types define the globals injected by setupCore() into a QuickJS context.\n * Use these types to typecheck user code that will run inside QuickJS.\n *\n * @example\n * // In your tsconfig.quickjs.json\n * {\n * \"compilerOptions\": {\n * \"lib\": [\"ESNext\", \"DOM\"]\n * }\n * }\n *\n * // Then reference this file or use ts-morph for code strings\n */\n\nexport {};\n\ndeclare global {\n // ============================================\n // Web Streams API\n // ============================================\n\n /**\n * A readable stream of data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream\n */\n const ReadableStream: typeof globalThis.ReadableStream;\n\n /**\n * A writable stream of data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/WritableStream\n */\n const WritableStream: typeof globalThis.WritableStream;\n\n /**\n * A transform stream that can be used to pipe data through a transformer.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/TransformStream\n */\n const TransformStream: typeof globalThis.TransformStream;\n\n /**\n * Default reader for ReadableStream\n * @see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader\n */\n const ReadableStreamDefaultReader: typeof globalThis.ReadableStreamDefaultReader;\n\n /**\n * Default writer for WritableStream\n * @see https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter\n */\n const WritableStreamDefaultWriter: typeof globalThis.WritableStreamDefaultWriter;\n\n // ============================================\n // Blob and File APIs\n // ============================================\n\n /**\n * A file-like object of immutable, raw data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Blob\n */\n const Blob: typeof globalThis.Blob;\n\n /**\n * A file object representing a file.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/File\n */\n const File: typeof globalThis.File;\n\n // ============================================\n // URL APIs\n // ============================================\n\n /**\n * Interface for URL manipulation.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/URL\n */\n const URL: typeof globalThis.URL;\n\n /**\n * Utility for working with URL query strings.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams\n */\n const URLSearchParams: typeof globalThis.URLSearchParams;\n\n // ============================================\n // Error Handling\n // ============================================\n\n /**\n * Exception type for DOM operations.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMException\n */\n const DOMException: typeof globalThis.DOMException;\n}\n`;\n\n/**\n * Type definitions for @ricsam/quickjs-fetch globals.\n *\n * Includes: Headers, Request, Response, AbortController, AbortSignal, FormData, fetch, serve, Server, ServerWebSocket\n */\nexport const FETCH_TYPES = `/**\n * QuickJS Global Type Definitions for @ricsam/quickjs-fetch\n *\n * These types define the globals injected by setupFetch() into a QuickJS context.\n * Use these types to typecheck user code that will run inside QuickJS.\n *\n * @example\n * // Typecheck QuickJS code with serve()\n * serve({\n * fetch(request, server) {\n * if (request.url.includes(\"/ws\")) {\n * server.upgrade(request, { data: { id: 123 } });\n * return new Response(null, { status: 101 });\n * }\n * return new Response(\"Hello!\");\n * },\n * websocket: {\n * message(ws, message) {\n * ws.send(\"Echo: \" + message);\n * }\n * }\n * });\n */\n\nexport {};\n\ndeclare global {\n // ============================================\n // Standard Fetch API (from lib.dom)\n // ============================================\n\n /**\n * Headers class for HTTP headers manipulation.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Headers\n */\n const Headers: typeof globalThis.Headers;\n\n /**\n * Request class for HTTP requests.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Request\n */\n const Request: typeof globalThis.Request;\n\n /**\n * Response class for HTTP responses.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Response\n */\n const Response: typeof globalThis.Response;\n\n /**\n * AbortController for cancelling fetch requests.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortController\n */\n const AbortController: typeof globalThis.AbortController;\n\n /**\n * AbortSignal for listening to abort events.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal\n */\n const AbortSignal: typeof globalThis.AbortSignal;\n\n /**\n * FormData for constructing form data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/FormData\n */\n const FormData: typeof globalThis.FormData;\n\n /**\n * Fetch function for making HTTP requests.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/fetch\n */\n function fetch(\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response>;\n\n // ============================================\n // QuickJS-specific: serve() API\n // ============================================\n\n /**\n * Server interface for handling WebSocket upgrades within serve() handlers.\n */\n interface Server {\n /**\n * Upgrade an HTTP request to a WebSocket connection.\n *\n * @param request - The incoming HTTP request to upgrade\n * @param options - Optional data to associate with the WebSocket connection\n * @returns true if upgrade will proceed, false otherwise\n *\n * @example\n * serve({\n * fetch(request, server) {\n * if (server.upgrade(request, { data: { userId: 123 } })) {\n * return new Response(null, { status: 101 });\n * }\n * return new Response(\"Upgrade failed\", { status: 400 });\n * }\n * });\n */\n upgrade(request: Request, options?: { data?: unknown }): boolean;\n }\n\n /**\n * ServerWebSocket interface for WebSocket connections within serve() handlers.\n *\n * @typeParam T - The type of data associated with this WebSocket connection\n */\n interface ServerWebSocket<T = unknown> {\n /**\n * User data associated with this connection.\n * Set via \\`server.upgrade(request, { data: ... })\\`.\n */\n readonly data: T;\n\n /**\n * Send a message to the client.\n *\n * @param message - The message to send (string, ArrayBuffer, or Uint8Array)\n */\n send(message: string | ArrayBuffer | Uint8Array): void;\n\n /**\n * Close the WebSocket connection.\n *\n * @param code - Optional close code (default: 1000)\n * @param reason - Optional close reason\n */\n close(code?: number, reason?: string): void;\n\n /**\n * WebSocket ready state.\n * - 0: CONNECTING\n * - 1: OPEN\n * - 2: CLOSING\n * - 3: CLOSED\n */\n readonly readyState: number;\n }\n\n /**\n * Options for the serve() function.\n *\n * @typeParam T - The type of data associated with WebSocket connections\n */\n interface ServeOptions<T = unknown> {\n /**\n * Handler for HTTP requests.\n *\n * @param request - The incoming HTTP request\n * @param server - Server interface for WebSocket upgrades\n * @returns Response or Promise resolving to Response\n */\n fetch(request: Request, server: Server): Response | Promise<Response>;\n\n /**\n * WebSocket event handlers.\n */\n websocket?: {\n /**\n * Called when a WebSocket connection is opened.\n *\n * @param ws - The WebSocket connection\n */\n open?(ws: ServerWebSocket<T>): void | Promise<void>;\n\n /**\n * Called when a message is received.\n *\n * @param ws - The WebSocket connection\n * @param message - The received message (string or ArrayBuffer)\n */\n message?(\n ws: ServerWebSocket<T>,\n message: string | ArrayBuffer\n ): void | Promise<void>;\n\n /**\n * Called when the connection is closed.\n *\n * @param ws - The WebSocket connection\n * @param code - The close code\n * @param reason - The close reason\n */\n close?(\n ws: ServerWebSocket<T>,\n code: number,\n reason: string\n ): void | Promise<void>;\n\n /**\n * Called when an error occurs.\n *\n * @param ws - The WebSocket connection\n * @param error - The error that occurred\n */\n error?(ws: ServerWebSocket<T>, error: Error): void | Promise<void>;\n };\n }\n\n /**\n * Register an HTTP server handler in QuickJS.\n *\n * Only one serve() handler can be active at a time.\n * Calling serve() again replaces the previous handler.\n *\n * @param options - Server configuration including fetch handler and optional WebSocket handlers\n *\n * @example\n * serve({\n * fetch(request, server) {\n * const url = new URL(request.url);\n *\n * if (url.pathname === \"/ws\") {\n * if (server.upgrade(request, { data: { connectedAt: Date.now() } })) {\n * return new Response(null, { status: 101 });\n * }\n * }\n *\n * if (url.pathname === \"/api/hello\") {\n * return Response.json({ message: \"Hello!\" });\n * }\n *\n * return new Response(\"Not Found\", { status: 404 });\n * },\n * websocket: {\n * open(ws) {\n * console.log(\"Connected at:\", ws.data.connectedAt);\n * },\n * message(ws, message) {\n * ws.send(\"Echo: \" + message);\n * },\n * close(ws, code, reason) {\n * console.log(\"Closed:\", code, reason);\n * }\n * }\n * });\n */\n function serve<T = unknown>(options: ServeOptions<T>): void;\n}\n`;\n\n/**\n * Type definitions for @ricsam/quickjs-fs globals.\n *\n * Includes: fs namespace, FileSystemHandle, FileSystemFileHandle, FileSystemDirectoryHandle, FileSystemWritableFileStream\n */\nexport const FS_TYPES = `/**\n * QuickJS Global Type Definitions for @ricsam/quickjs-fs\n *\n * These types define the globals injected by setupFs() into a QuickJS context.\n * Use these types to typecheck user code that will run inside QuickJS.\n *\n * @example\n * // Typecheck QuickJS code with file system access\n * const root = await fs.getDirectory(\"/data\");\n * const fileHandle = await root.getFileHandle(\"config.json\");\n * const file = await fileHandle.getFile();\n * const content = await file.text();\n */\n\nexport {};\n\ndeclare global {\n // ============================================\n // fs namespace - QuickJS-specific entry point\n // ============================================\n\n /**\n * File System namespace providing access to the file system.\n * This is the QuickJS-specific entry point (differs from browser's navigator.storage.getDirectory()).\n */\n namespace fs {\n /**\n * Get a directory handle for the given path.\n *\n * The host controls which paths are accessible. Invalid or unauthorized\n * paths will throw an error.\n *\n * @param path - The path to request from the host\n * @returns A promise resolving to a directory handle\n * @throws If the path is not allowed or doesn't exist\n *\n * @example\n * const root = await fs.getDirectory(\"/\");\n * const dataDir = await fs.getDirectory(\"/data\");\n */\n function getDirectory(path: string): Promise<FileSystemDirectoryHandle>;\n }\n\n // ============================================\n // File System Access API\n // ============================================\n\n /**\n * Base interface for file system handles.\n */\n interface FileSystemHandle {\n /**\n * The kind of handle: \"file\" or \"directory\".\n */\n readonly kind: \"file\" | \"directory\";\n\n /**\n * The name of the file or directory.\n */\n readonly name: string;\n\n /**\n * Compare two handles to check if they reference the same entry.\n *\n * @param other - Another FileSystemHandle to compare against\n * @returns true if both handles reference the same entry\n */\n isSameEntry(other: FileSystemHandle): Promise<boolean>;\n }\n\n /**\n * Handle for a file in the file system.\n */\n interface FileSystemFileHandle extends FileSystemHandle {\n /**\n * Always \"file\" for file handles.\n */\n readonly kind: \"file\";\n\n /**\n * Get the file contents as a File object.\n *\n * @returns A promise resolving to a File object\n *\n * @example\n * const file = await fileHandle.getFile();\n * const text = await file.text();\n */\n getFile(): Promise<File>;\n\n /**\n * Create a writable stream for writing to the file.\n *\n * @param options - Options for the writable stream\n * @returns A promise resolving to a writable stream\n *\n * @example\n * const writable = await fileHandle.createWritable();\n * await writable.write(\"Hello, World!\");\n * await writable.close();\n */\n createWritable(options?: {\n /**\n * If true, keeps existing file data. Otherwise, truncates the file.\n */\n keepExistingData?: boolean;\n }): Promise<FileSystemWritableFileStream>;\n }\n\n /**\n * Handle for a directory in the file system.\n */\n interface FileSystemDirectoryHandle extends FileSystemHandle {\n /**\n * Always \"directory\" for directory handles.\n */\n readonly kind: \"directory\";\n\n /**\n * Get a file handle within this directory.\n *\n * @param name - The name of the file\n * @param options - Options for getting the file handle\n * @returns A promise resolving to a file handle\n * @throws If the file doesn't exist and create is not true\n *\n * @example\n * const file = await dir.getFileHandle(\"data.json\");\n * const newFile = await dir.getFileHandle(\"output.txt\", { create: true });\n */\n getFileHandle(\n name: string,\n options?: {\n /**\n * If true, creates the file if it doesn't exist.\n */\n create?: boolean;\n }\n ): Promise<FileSystemFileHandle>;\n\n /**\n * Get a subdirectory handle within this directory.\n *\n * @param name - The name of the subdirectory\n * @param options - Options for getting the directory handle\n * @returns A promise resolving to a directory handle\n * @throws If the directory doesn't exist and create is not true\n *\n * @example\n * const subdir = await dir.getDirectoryHandle(\"logs\");\n * const newDir = await dir.getDirectoryHandle(\"cache\", { create: true });\n */\n getDirectoryHandle(\n name: string,\n options?: {\n /**\n * If true, creates the directory if it doesn't exist.\n */\n create?: boolean;\n }\n ): Promise<FileSystemDirectoryHandle>;\n\n /**\n * Remove a file or directory within this directory.\n *\n * @param name - The name of the entry to remove\n * @param options - Options for removal\n * @throws If the entry doesn't exist or cannot be removed\n *\n * @example\n * await dir.removeEntry(\"old-file.txt\");\n * await dir.removeEntry(\"old-dir\", { recursive: true });\n */\n removeEntry(\n name: string,\n options?: {\n /**\n * If true, removes directories recursively.\n */\n recursive?: boolean;\n }\n ): Promise<void>;\n\n /**\n * Resolve the path from this directory to a descendant handle.\n *\n * @param possibleDescendant - A handle that may be a descendant\n * @returns An array of path segments, or null if not a descendant\n *\n * @example\n * const path = await root.resolve(nestedFile);\n * // [\"subdir\", \"file.txt\"]\n */\n resolve(possibleDescendant: FileSystemHandle): Promise<string[] | null>;\n\n /**\n * Iterate over entries in this directory.\n *\n * @returns An async iterator of [name, handle] pairs\n *\n * @example\n * for await (const [name, handle] of dir.entries()) {\n * console.log(name, handle.kind);\n * }\n */\n entries(): AsyncIterableIterator<[string, FileSystemHandle]>;\n\n /**\n * Iterate over entry names in this directory.\n *\n * @returns An async iterator of names\n *\n * @example\n * for await (const name of dir.keys()) {\n * console.log(name);\n * }\n */\n keys(): AsyncIterableIterator<string>;\n\n /**\n * Iterate over handles in this directory.\n *\n * @returns An async iterator of handles\n *\n * @example\n * for await (const handle of dir.values()) {\n * console.log(handle.name, handle.kind);\n * }\n */\n values(): AsyncIterableIterator<FileSystemHandle>;\n\n /**\n * Async iterator support for directory entries.\n *\n * @example\n * for await (const [name, handle] of dir) {\n * console.log(name, handle.kind);\n * }\n */\n [Symbol.asyncIterator](): AsyncIterableIterator<[string, FileSystemHandle]>;\n }\n\n /**\n * Parameters for write operations on FileSystemWritableFileStream.\n */\n interface WriteParams {\n /**\n * The type of write operation.\n * - \"write\": Write data at the current position or specified position\n * - \"seek\": Move the file position\n * - \"truncate\": Truncate the file to a specific size\n */\n type: \"write\" | \"seek\" | \"truncate\";\n\n /**\n * The data to write (for \"write\" type).\n */\n data?: string | ArrayBuffer | Uint8Array | Blob;\n\n /**\n * The position to write at or seek to.\n */\n position?: number;\n\n /**\n * The size to truncate to (for \"truncate\" type).\n */\n size?: number;\n }\n\n /**\n * Writable stream for writing to a file.\n * Extends WritableStream with file-specific operations.\n */\n interface FileSystemWritableFileStream extends WritableStream<Uint8Array> {\n /**\n * Write data to the file.\n *\n * @param data - The data to write\n * @returns A promise that resolves when the write completes\n *\n * @example\n * await writable.write(\"Hello, World!\");\n * await writable.write(new Uint8Array([1, 2, 3]));\n * await writable.write({ type: \"write\", data: \"text\", position: 0 });\n */\n write(\n data: string | ArrayBuffer | Uint8Array | Blob | WriteParams\n ): Promise<void>;\n\n /**\n * Seek to a position in the file.\n *\n * @param position - The byte position to seek to\n * @returns A promise that resolves when the seek completes\n *\n * @example\n * await writable.seek(0); // Seek to beginning\n * await writable.write(\"Overwrite\");\n */\n seek(position: number): Promise<void>;\n\n /**\n * Truncate the file to a specific size.\n *\n * @param size - The size to truncate to\n * @returns A promise that resolves when the truncation completes\n *\n * @example\n * await writable.truncate(100); // Keep only first 100 bytes\n */\n truncate(size: number): Promise<void>;\n }\n}\n`;\n\n/**\n * Map of package names to their type definitions.\n */\nexport const TYPE_DEFINITIONS = {\n core: CORE_TYPES,\n fetch: FETCH_TYPES,\n fs: FS_TYPES,\n} as const;\n\n/**\n * Type for the keys of TYPE_DEFINITIONS.\n */\nexport type TypeDefinitionKey = keyof typeof TYPE_DEFINITIONS;\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;AAkBO,IAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuGnB,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwPpB,IAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+TjB,IAAM,mBAAmB;AAAA,EAC9B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,IAAI;AACN;",
|
|
8
|
+
"debugId": "DB524E5F7383894464756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/test-utils/src/runtime-context.ts
|
|
3
|
+
import { getQuickJS } from "quickjs-emscripten";
|
|
4
|
+
import { clearAllInstanceState } from "@ricsam/quickjs-core";
|
|
5
|
+
import {
|
|
6
|
+
setupRuntime
|
|
7
|
+
} from "@ricsam/quickjs-runtime";
|
|
8
|
+
async function createRuntimeTestContext(options) {
|
|
9
|
+
const QuickJS = await getQuickJS();
|
|
10
|
+
const runtime = QuickJS.newRuntime();
|
|
11
|
+
const context = runtime.newContext();
|
|
12
|
+
clearAllInstanceState();
|
|
13
|
+
const runtimeHandle = setupRuntime(context, options);
|
|
14
|
+
return { runtime, context, runtimeHandle };
|
|
15
|
+
}
|
|
16
|
+
function disposeRuntimeTestContext(ctx) {
|
|
17
|
+
ctx.runtimeHandle.dispose();
|
|
18
|
+
ctx.context.dispose();
|
|
19
|
+
ctx.runtime.dispose();
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
disposeRuntimeTestContext,
|
|
23
|
+
createRuntimeTestContext
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
//# debugId=8B023BD3945C89FC64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/runtime-context.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { getQuickJS, type QuickJSContext, type QuickJSRuntime } from \"quickjs-emscripten\";\nimport { clearAllInstanceState } from \"@ricsam/quickjs-core\";\nimport {\n setupRuntime,\n type RuntimeHandle,\n type SetupRuntimeOptions,\n} from \"@ricsam/quickjs-runtime\";\n\nexport interface RuntimeTestContext {\n runtime: QuickJSRuntime;\n context: QuickJSContext;\n runtimeHandle: RuntimeHandle;\n}\n\nexport async function createRuntimeTestContext(\n options?: SetupRuntimeOptions\n): Promise<RuntimeTestContext> {\n const QuickJS = await getQuickJS();\n const runtime = QuickJS.newRuntime();\n const context = runtime.newContext();\n clearAllInstanceState();\n\n // setupRuntime internally creates the coreHandle\n const runtimeHandle = setupRuntime(context, options);\n\n return { runtime, context, runtimeHandle };\n}\n\nexport function disposeRuntimeTestContext(ctx: RuntimeTestContext): void {\n ctx.runtimeHandle.dispose();\n ctx.context.dispose();\n ctx.runtime.dispose();\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;AAAA;AACA;AACA;AAAA;AAAA;AAYA,eAAsB,wBAAwB,CAC5C,SAC6B;AAAA,EAC7B,MAAM,UAAU,MAAM,WAAW;AAAA,EACjC,MAAM,UAAU,QAAQ,WAAW;AAAA,EACnC,MAAM,UAAU,QAAQ,WAAW;AAAA,EACnC,sBAAsB;AAAA,EAGtB,MAAM,gBAAgB,aAAa,SAAS,OAAO;AAAA,EAEnD,OAAO,EAAE,SAAS,SAAS,cAAc;AAAA;AAGpC,SAAS,yBAAyB,CAAC,KAA+B;AAAA,EACvE,IAAI,cAAc,QAAQ;AAAA,EAC1B,IAAI,QAAQ,QAAQ;AAAA,EACpB,IAAI,QAAQ,QAAQ;AAAA;",
|
|
8
|
+
"debugId": "8B023BD3945C89FC64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/test-utils/src/typecheck.ts
|
|
3
|
+
import { Project, ts } from "ts-morph";
|
|
4
|
+
import { TYPE_DEFINITIONS } from "./quickjs-types.ts";
|
|
5
|
+
function getMessageText(messageText) {
|
|
6
|
+
if (typeof messageText === "string") {
|
|
7
|
+
return messageText;
|
|
8
|
+
}
|
|
9
|
+
if (messageText && typeof messageText === "object" && "getMessageText" in messageText && typeof messageText.getMessageText === "function") {
|
|
10
|
+
return messageText.getMessageText();
|
|
11
|
+
}
|
|
12
|
+
if (messageText && typeof messageText === "object" && "messageText" in messageText) {
|
|
13
|
+
return String(messageText.messageText);
|
|
14
|
+
}
|
|
15
|
+
return String(messageText);
|
|
16
|
+
}
|
|
17
|
+
function typecheckQuickJSCode(code, options) {
|
|
18
|
+
const include = options?.include ?? ["core", "fetch", "fs"];
|
|
19
|
+
const project = new Project({
|
|
20
|
+
useInMemoryFileSystem: true,
|
|
21
|
+
compilerOptions: {
|
|
22
|
+
target: ts.ScriptTarget.ESNext,
|
|
23
|
+
module: ts.ModuleKind.ESNext,
|
|
24
|
+
lib: ["lib.esnext.d.ts", "lib.dom.d.ts"],
|
|
25
|
+
strict: true,
|
|
26
|
+
noEmit: true,
|
|
27
|
+
skipLibCheck: true,
|
|
28
|
+
...options?.compilerOptions
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
for (const pkg of include) {
|
|
32
|
+
const content = TYPE_DEFINITIONS[pkg];
|
|
33
|
+
if (content) {
|
|
34
|
+
project.createSourceFile(`${pkg}.d.ts`, content);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const sourceFile = project.createSourceFile("usercode.ts", code);
|
|
38
|
+
const diagnostics = sourceFile.getPreEmitDiagnostics();
|
|
39
|
+
const errors = diagnostics.map((diagnostic) => {
|
|
40
|
+
const start = diagnostic.getStart();
|
|
41
|
+
const sourceFile2 = diagnostic.getSourceFile();
|
|
42
|
+
let line;
|
|
43
|
+
let column;
|
|
44
|
+
if (start !== undefined && sourceFile2) {
|
|
45
|
+
const lineAndChar = sourceFile2.getLineAndColumnAtPos(start);
|
|
46
|
+
line = lineAndChar.line;
|
|
47
|
+
column = lineAndChar.column;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
message: getMessageText(diagnostic.getMessageText()),
|
|
51
|
+
line,
|
|
52
|
+
column,
|
|
53
|
+
code: diagnostic.getCode()
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
success: errors.length === 0,
|
|
58
|
+
errors
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function formatTypecheckErrors(result) {
|
|
62
|
+
if (result.success) {
|
|
63
|
+
return "No type errors found.";
|
|
64
|
+
}
|
|
65
|
+
return result.errors.map((error) => {
|
|
66
|
+
const location = error.line !== undefined ? `:${error.line}:${error.column ?? 1}` : "";
|
|
67
|
+
const code = error.code ? ` (TS${error.code})` : "";
|
|
68
|
+
return `usercode.ts${location}${code}: ${error.message}`;
|
|
69
|
+
}).join(`
|
|
70
|
+
`);
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
typecheckQuickJSCode,
|
|
74
|
+
formatTypecheckErrors
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
//# debugId=5D284798E24F133564756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/typecheck.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Type-checking utility for QuickJS user code using ts-morph.\n *\n * This utility allows you to validate TypeScript code strings against\n * the QuickJS global type definitions before running them in the sandbox.\n *\n * @example\n * import { typecheckQuickJSCode } from \"@ricsam/quickjs-test-utils\";\n *\n * const result = typecheckQuickJSCode(`\n * serve({\n * fetch(request, server) {\n * return new Response(\"Hello!\");\n * }\n * });\n * `, { include: [\"fetch\"] });\n *\n * if (!result.success) {\n * console.error(\"Type errors:\", result.errors);\n * }\n */\n\nimport { Project, ts } from \"ts-morph\";\nimport { TYPE_DEFINITIONS, type TypeDefinitionKey } from \"./quickjs-types.ts\";\n\n/**\n * Result of type-checking QuickJS code.\n */\nexport interface TypecheckResult {\n /**\n * Whether the code passed type checking.\n */\n success: boolean;\n\n /**\n * Array of type errors found in the code.\n */\n errors: TypecheckError[];\n}\n\n/**\n * A single type-checking error.\n */\nexport interface TypecheckError {\n /**\n * The error message from TypeScript.\n */\n message: string;\n\n /**\n * The line number where the error occurred (1-indexed).\n */\n line?: number;\n\n /**\n * The column number where the error occurred (1-indexed).\n */\n column?: number;\n\n /**\n * The TypeScript error code.\n */\n code?: number;\n}\n\n/**\n * Options for type-checking QuickJS code.\n */\nexport interface TypecheckOptions {\n /**\n * Which package types to include.\n * @default [\"core\", \"fetch\", \"fs\"]\n */\n include?: Array<\"core\" | \"fetch\" | \"fs\">;\n\n /**\n * Additional compiler options to pass to TypeScript.\n */\n compilerOptions?: Partial<ts.CompilerOptions>;\n}\n\n/**\n * Get the message text from a TypeScript diagnostic message.\n * Handles both string messages and DiagnosticMessageChain objects.\n */\nfunction getMessageText(messageText: unknown): string {\n if (typeof messageText === \"string\") {\n return messageText;\n }\n\n // Handle ts-morph DiagnosticMessageChain wrapper\n if (\n messageText &&\n typeof messageText === \"object\" &&\n \"getMessageText\" in messageText &&\n typeof (messageText as { getMessageText: unknown }).getMessageText ===\n \"function\"\n ) {\n return (messageText as { getMessageText: () => string }).getMessageText();\n }\n\n // Handle raw TypeScript DiagnosticMessageChain\n if (\n messageText &&\n typeof messageText === \"object\" &&\n \"messageText\" in messageText\n ) {\n return String((messageText as { messageText: unknown }).messageText);\n }\n\n return String(messageText);\n}\n\n\n/**\n * Type-check QuickJS user code against the package type definitions.\n *\n * @param code - The TypeScript/JavaScript code to check\n * @param options - Configuration options\n * @returns The result of type checking\n *\n * @example\n * // Check code that uses the fetch API\n * const result = typecheckQuickJSCode(`\n * const response = await fetch(\"https://api.example.com/data\");\n * const data = await response.json();\n * `, { include: [\"core\", \"fetch\"] });\n *\n * @example\n * // Check code that uses serve()\n * const result = typecheckQuickJSCode(`\n * serve({\n * fetch(request, server) {\n * return new Response(\"Hello!\");\n * }\n * });\n * `, { include: [\"fetch\"] });\n *\n * @example\n * // Check code that uses the file system API\n * const result = typecheckQuickJSCode(`\n * const root = await fs.getDirectory(\"/data\");\n * const file = await root.getFileHandle(\"config.json\");\n * `, { include: [\"core\", \"fs\"] });\n */\nexport function typecheckQuickJSCode(\n code: string,\n options?: TypecheckOptions\n): TypecheckResult {\n const include = options?.include ?? [\"core\", \"fetch\", \"fs\"];\n\n // Create a project with in-memory file system\n const project = new Project({\n useInMemoryFileSystem: true,\n compilerOptions: {\n target: ts.ScriptTarget.ESNext,\n module: ts.ModuleKind.ESNext,\n lib: [\"lib.esnext.d.ts\", \"lib.dom.d.ts\"],\n strict: true,\n noEmit: true,\n skipLibCheck: true,\n ...options?.compilerOptions,\n },\n });\n\n // Add type definition files from embedded strings\n for (const pkg of include) {\n const content = TYPE_DEFINITIONS[pkg as TypeDefinitionKey];\n if (content) {\n project.createSourceFile(`${pkg}.d.ts`, content);\n }\n }\n\n // Add the user code\n const sourceFile = project.createSourceFile(\"usercode.ts\", code);\n\n // Get diagnostics\n const diagnostics = sourceFile.getPreEmitDiagnostics();\n\n // Convert diagnostics to our error format\n const errors: TypecheckError[] = diagnostics.map((diagnostic) => {\n const start = diagnostic.getStart();\n const sourceFile = diagnostic.getSourceFile();\n\n let line: number | undefined;\n let column: number | undefined;\n\n if (start !== undefined && sourceFile) {\n const lineAndChar = sourceFile.getLineAndColumnAtPos(start);\n line = lineAndChar.line;\n column = lineAndChar.column;\n }\n\n return {\n message: getMessageText(diagnostic.getMessageText()),\n line,\n column,\n code: diagnostic.getCode(),\n };\n });\n\n return {\n success: errors.length === 0,\n errors,\n };\n}\n\n/**\n * Format type-check errors for display.\n *\n * @param result - The type-check result\n * @returns A formatted string of errors\n *\n * @example\n * const result = typecheckQuickJSCode(code);\n * if (!result.success) {\n * console.error(formatTypecheckErrors(result));\n * }\n */\nexport function formatTypecheckErrors(result: TypecheckResult): string {\n if (result.success) {\n return \"No type errors found.\";\n }\n\n return result.errors\n .map((error) => {\n const location =\n error.line !== undefined ? `:${error.line}:${error.column ?? 1}` : \"\";\n const code = error.code ? ` (TS${error.code})` : \"\";\n return `usercode.ts${location}${code}: ${error.message}`;\n })\n .join(\"\\n\");\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;AAsBA;AACA;AA8DA,SAAS,cAAc,CAAC,aAA8B;AAAA,EACpD,IAAI,OAAO,gBAAgB,UAAU;AAAA,IACnC,OAAO;AAAA,EACT;AAAA,EAGA,IACE,eACA,OAAO,gBAAgB,YACvB,oBAAoB,eACpB,OAAQ,YAA4C,mBAClD,YACF;AAAA,IACA,OAAQ,YAAiD,eAAe;AAAA,EAC1E;AAAA,EAGA,IACE,eACA,OAAO,gBAAgB,YACvB,iBAAiB,aACjB;AAAA,IACA,OAAO,OAAQ,YAAyC,WAAW;AAAA,EACrE;AAAA,EAEA,OAAO,OAAO,WAAW;AAAA;AAmCpB,SAAS,oBAAoB,CAClC,MACA,SACiB;AAAA,EACjB,MAAM,UAAU,SAAS,WAAW,CAAC,QAAQ,SAAS,IAAI;AAAA,EAG1D,MAAM,UAAU,IAAI,QAAQ;AAAA,IAC1B,uBAAuB;AAAA,IACvB,iBAAiB;AAAA,MACf,QAAQ,GAAG,aAAa;AAAA,MACxB,QAAQ,GAAG,WAAW;AAAA,MACtB,KAAK,CAAC,mBAAmB,cAAc;AAAA,MACvC,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,cAAc;AAAA,SACX,SAAS;AAAA,IACd;AAAA,EACF,CAAC;AAAA,EAGD,WAAW,OAAO,SAAS;AAAA,IACzB,MAAM,UAAU,iBAAiB;AAAA,IACjC,IAAI,SAAS;AAAA,MACX,QAAQ,iBAAiB,GAAG,YAAY,OAAO;AAAA,IACjD;AAAA,EACF;AAAA,EAGA,MAAM,aAAa,QAAQ,iBAAiB,eAAe,IAAI;AAAA,EAG/D,MAAM,cAAc,WAAW,sBAAsB;AAAA,EAGrD,MAAM,SAA2B,YAAY,IAAI,CAAC,eAAe;AAAA,IAC/D,MAAM,QAAQ,WAAW,SAAS;AAAA,IAClC,MAAM,cAAa,WAAW,cAAc;AAAA,IAE5C,IAAI;AAAA,IACJ,IAAI;AAAA,IAEJ,IAAI,UAAU,aAAa,aAAY;AAAA,MACrC,MAAM,cAAc,YAAW,sBAAsB,KAAK;AAAA,MAC1D,OAAO,YAAY;AAAA,MACnB,SAAS,YAAY;AAAA,IACvB;AAAA,IAEA,OAAO;AAAA,MACL,SAAS,eAAe,WAAW,eAAe,CAAC;AAAA,MACnD;AAAA,MACA;AAAA,MACA,MAAM,WAAW,QAAQ;AAAA,IAC3B;AAAA,GACD;AAAA,EAED,OAAO;AAAA,IACL,SAAS,OAAO,WAAW;AAAA,IAC3B;AAAA,EACF;AAAA;AAeK,SAAS,qBAAqB,CAAC,QAAiC;AAAA,EACrE,IAAI,OAAO,SAAS;AAAA,IAClB,OAAO;AAAA,EACT;AAAA,EAEA,OAAO,OAAO,OACX,IAAI,CAAC,UAAU;AAAA,IACd,MAAM,WACJ,MAAM,SAAS,YAAY,IAAI,MAAM,QAAQ,MAAM,UAAU,MAAM;AAAA,IACrE,MAAM,OAAO,MAAM,OAAO,OAAO,MAAM,UAAU;AAAA,IACjD,OAAO,cAAc,WAAW,SAAS,MAAM;AAAA,GAChD,EACA,KAAK;AAAA,CAAI;AAAA;",
|
|
8
|
+
"debugId": "5D284798E24F133564756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type QuickJSContext, type QuickJSRuntime } from "quickjs-emscripten";
|
|
2
|
+
import { type CoreHandle } from "@ricsam/quickjs-core";
|
|
3
|
+
export interface TestContext {
|
|
4
|
+
runtime: QuickJSRuntime;
|
|
5
|
+
context: QuickJSContext;
|
|
6
|
+
coreHandle: CoreHandle;
|
|
7
|
+
}
|
|
8
|
+
export declare function createTestContext(): Promise<TestContext>;
|
|
9
|
+
export declare function disposeTestContext(ctx: TestContext): void;
|
|
10
|
+
/**
|
|
11
|
+
* Helper for use with beforeEach/afterEach
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const testCtx = useTestContext();
|
|
15
|
+
*
|
|
16
|
+
* beforeEach(async () => {
|
|
17
|
+
* await testCtx.setup();
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* afterEach(() => {
|
|
21
|
+
* testCtx.teardown();
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* test("my test", () => {
|
|
25
|
+
* const result = evalCode(testCtx.context, `1 + 1`);
|
|
26
|
+
* expect(result).toBe(2);
|
|
27
|
+
* });
|
|
28
|
+
*/
|
|
29
|
+
export declare function useTestContext(): {
|
|
30
|
+
setup(): Promise<TestContext>;
|
|
31
|
+
teardown(): void;
|
|
32
|
+
readonly context: QuickJSContext;
|
|
33
|
+
readonly runtime: QuickJSRuntime;
|
|
34
|
+
readonly coreHandle: CoreHandle;
|
|
35
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { QuickJSContext } from "quickjs-emscripten";
|
|
2
|
+
/**
|
|
3
|
+
* Evaluate code synchronously and return the result
|
|
4
|
+
*
|
|
5
|
+
* Handles error checking and handle disposal automatically.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const result = evalCode<number>(context, `1 + 1`);
|
|
9
|
+
* expect(result).toBe(2);
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const obj = evalCode<{ name: string }>(context, `({ name: "test" })`);
|
|
13
|
+
* expect(obj.name).toBe("test");
|
|
14
|
+
*/
|
|
15
|
+
export declare function evalCode<T>(context: QuickJSContext, code: string): T;
|
|
16
|
+
/**
|
|
17
|
+
* Evaluate async code and return the resolved result
|
|
18
|
+
*
|
|
19
|
+
* Handles promise resolution, error checking, and handle disposal automatically.
|
|
20
|
+
* Also executes pending jobs after resolution.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const result = await evalCodeAsync<string>(context, `
|
|
24
|
+
* (async () => {
|
|
25
|
+
* const blob = new Blob(["hello"]);
|
|
26
|
+
* return await blob.text();
|
|
27
|
+
* })()
|
|
28
|
+
* `);
|
|
29
|
+
* expect(result).toBe("hello");
|
|
30
|
+
*/
|
|
31
|
+
export declare function evalCodeAsync<T>(context: QuickJSContext, code: string): Promise<T>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type FetchHandle, type SetupFetchOptions } from "@ricsam/quickjs-fetch";
|
|
2
|
+
import { type TestContext } from "./context.ts";
|
|
3
|
+
export interface FetchTestContext extends TestContext {
|
|
4
|
+
fetchHandle: FetchHandle;
|
|
5
|
+
}
|
|
6
|
+
export interface CreateFetchTestContextOptions {
|
|
7
|
+
/** Handler for outbound fetch() calls from QuickJS */
|
|
8
|
+
onFetch?: SetupFetchOptions["onFetch"];
|
|
9
|
+
}
|
|
10
|
+
export declare function createFetchTestContext(options?: CreateFetchTestContextOptions): Promise<FetchTestContext>;
|
|
11
|
+
export declare function disposeFetchTestContext(ctx: FetchTestContext): void;
|
|
12
|
+
/**
|
|
13
|
+
* Helper for use with beforeEach/afterEach for fetch tests
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const testCtx = useFetchTestContext();
|
|
17
|
+
*
|
|
18
|
+
* beforeEach(async () => {
|
|
19
|
+
* await testCtx.setup();
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* afterEach(() => {
|
|
23
|
+
* testCtx.teardown();
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* test("headers test", () => {
|
|
27
|
+
* const result = evalCode(testCtx.context, `
|
|
28
|
+
* const headers = new Headers({ "Content-Type": "application/json" });
|
|
29
|
+
* headers.get("content-type");
|
|
30
|
+
* `);
|
|
31
|
+
* expect(result).toBe("application/json");
|
|
32
|
+
* });
|
|
33
|
+
*/
|
|
34
|
+
export declare function useFetchTestContext(): {
|
|
35
|
+
setup(): Promise<FetchTestContext>;
|
|
36
|
+
teardown(): void;
|
|
37
|
+
readonly context: import("quickjs-emscripten").QuickJSContext;
|
|
38
|
+
readonly runtime: import("quickjs-emscripten").QuickJSRuntime;
|
|
39
|
+
readonly coreHandle: import("@ricsam/quickjs-core").CoreHandle;
|
|
40
|
+
readonly fetchHandle: FetchHandle;
|
|
41
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type FsHandle, type HostDirectoryHandle } from "@ricsam/quickjs-fs";
|
|
2
|
+
import { type TestContext } from "./context.ts";
|
|
3
|
+
export interface FsTestContext extends TestContext {
|
|
4
|
+
fsHandle: FsHandle;
|
|
5
|
+
memFs: HostDirectoryHandle;
|
|
6
|
+
}
|
|
7
|
+
export interface CreateFsTestContextOptions {
|
|
8
|
+
/** Initial files to populate the in-memory filesystem */
|
|
9
|
+
initialFiles?: Record<string, string | Uint8Array>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createFsTestContext(options?: CreateFsTestContextOptions): Promise<FsTestContext>;
|
|
12
|
+
export declare function disposeFsTestContext(ctx: FsTestContext): void;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createTestContext, disposeTestContext, useTestContext, type TestContext, } from "./context.ts";
|
|
2
|
+
export { createFetchTestContext, disposeFetchTestContext, useFetchTestContext, type FetchTestContext, type CreateFetchTestContextOptions, } from "./fetch-context.ts";
|
|
3
|
+
export { evalCode, evalCodeAsync } from "./eval.ts";
|
|
4
|
+
export { startIntegrationServer, type IntegrationTestServer, type StartIntegrationServerOptions, } from "./integration-server.ts";
|
|
5
|
+
export { typecheckQuickJSCode, formatTypecheckErrors, type TypecheckResult, type TypecheckError, type TypecheckOptions, } from "./typecheck.ts";
|
|
6
|
+
export { CORE_TYPES, FETCH_TYPES, FS_TYPES, TYPE_DEFINITIONS, type TypeDefinitionKey, } from "./quickjs-types.ts";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type QuickJSContext, type QuickJSRuntime } from "quickjs-emscripten";
|
|
2
|
+
import { type FetchHandle, type SetupFetchOptions } from "@ricsam/quickjs-fetch";
|
|
3
|
+
import { type CoreHandle } from "@ricsam/quickjs-core";
|
|
4
|
+
export interface IntegrationTestServer {
|
|
5
|
+
baseURL: string;
|
|
6
|
+
wsURL: string;
|
|
7
|
+
port: number;
|
|
8
|
+
fetchHandle: FetchHandle;
|
|
9
|
+
context: QuickJSContext;
|
|
10
|
+
runtime: QuickJSRuntime;
|
|
11
|
+
coreHandle: CoreHandle;
|
|
12
|
+
stop(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export interface StartIntegrationServerOptions {
|
|
15
|
+
/** Handler for outbound fetch() calls from QuickJS */
|
|
16
|
+
onFetch?: SetupFetchOptions["onFetch"];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Start a real HTTP server that dispatches requests to a QuickJS serve() handler.
|
|
20
|
+
*
|
|
21
|
+
* @param quickJSCode - JavaScript code to run in QuickJS, should call serve()
|
|
22
|
+
* @param options - Optional configuration
|
|
23
|
+
* @returns Server info including baseURL, wsURL, and stop function
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const server = await startIntegrationServer(`
|
|
27
|
+
* serve({
|
|
28
|
+
* fetch(request) {
|
|
29
|
+
* return new Response("Hello!");
|
|
30
|
+
* }
|
|
31
|
+
* });
|
|
32
|
+
* `);
|
|
33
|
+
*
|
|
34
|
+
* const response = await fetch(\`\${server.baseURL}/test\`);
|
|
35
|
+
* expect(await response.text()).toBe("Hello!");
|
|
36
|
+
*
|
|
37
|
+
* await server.stop();
|
|
38
|
+
*/
|
|
39
|
+
export declare function startIntegrationServer(quickJSCode: string, options?: StartIntegrationServerOptions): Promise<IntegrationTestServer>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuickJS type definitions as string constants.
|
|
3
|
+
*
|
|
4
|
+
* These are the canonical source for QuickJS global type definitions.
|
|
5
|
+
* The .d.ts files in each package are generated from these strings during build.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { TYPE_DEFINITIONS } from "@ricsam/quickjs-test-utils";
|
|
9
|
+
*
|
|
10
|
+
* // Use with ts-morph for type checking code strings
|
|
11
|
+
* project.createSourceFile("types.d.ts", TYPE_DEFINITIONS.fetch);
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Type definitions for @ricsam/quickjs-core globals.
|
|
15
|
+
*
|
|
16
|
+
* Includes: ReadableStream, WritableStream, TransformStream, Blob, File, URL, URLSearchParams, DOMException
|
|
17
|
+
*/
|
|
18
|
+
export declare const CORE_TYPES = "/**\n * QuickJS Global Type Definitions for @ricsam/quickjs-core\n *\n * These types define the globals injected by setupCore() into a QuickJS context.\n * Use these types to typecheck user code that will run inside QuickJS.\n *\n * @example\n * // In your tsconfig.quickjs.json\n * {\n * \"compilerOptions\": {\n * \"lib\": [\"ESNext\", \"DOM\"]\n * }\n * }\n *\n * // Then reference this file or use ts-morph for code strings\n */\n\nexport {};\n\ndeclare global {\n // ============================================\n // Web Streams API\n // ============================================\n\n /**\n * A readable stream of data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream\n */\n const ReadableStream: typeof globalThis.ReadableStream;\n\n /**\n * A writable stream of data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/WritableStream\n */\n const WritableStream: typeof globalThis.WritableStream;\n\n /**\n * A transform stream that can be used to pipe data through a transformer.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/TransformStream\n */\n const TransformStream: typeof globalThis.TransformStream;\n\n /**\n * Default reader for ReadableStream\n * @see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader\n */\n const ReadableStreamDefaultReader: typeof globalThis.ReadableStreamDefaultReader;\n\n /**\n * Default writer for WritableStream\n * @see https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter\n */\n const WritableStreamDefaultWriter: typeof globalThis.WritableStreamDefaultWriter;\n\n // ============================================\n // Blob and File APIs\n // ============================================\n\n /**\n * A file-like object of immutable, raw data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Blob\n */\n const Blob: typeof globalThis.Blob;\n\n /**\n * A file object representing a file.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/File\n */\n const File: typeof globalThis.File;\n\n // ============================================\n // URL APIs\n // ============================================\n\n /**\n * Interface for URL manipulation.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/URL\n */\n const URL: typeof globalThis.URL;\n\n /**\n * Utility for working with URL query strings.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams\n */\n const URLSearchParams: typeof globalThis.URLSearchParams;\n\n // ============================================\n // Error Handling\n // ============================================\n\n /**\n * Exception type for DOM operations.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMException\n */\n const DOMException: typeof globalThis.DOMException;\n}\n";
|
|
19
|
+
/**
|
|
20
|
+
* Type definitions for @ricsam/quickjs-fetch globals.
|
|
21
|
+
*
|
|
22
|
+
* Includes: Headers, Request, Response, AbortController, AbortSignal, FormData, fetch, serve, Server, ServerWebSocket
|
|
23
|
+
*/
|
|
24
|
+
export declare const FETCH_TYPES = "/**\n * QuickJS Global Type Definitions for @ricsam/quickjs-fetch\n *\n * These types define the globals injected by setupFetch() into a QuickJS context.\n * Use these types to typecheck user code that will run inside QuickJS.\n *\n * @example\n * // Typecheck QuickJS code with serve()\n * serve({\n * fetch(request, server) {\n * if (request.url.includes(\"/ws\")) {\n * server.upgrade(request, { data: { id: 123 } });\n * return new Response(null, { status: 101 });\n * }\n * return new Response(\"Hello!\");\n * },\n * websocket: {\n * message(ws, message) {\n * ws.send(\"Echo: \" + message);\n * }\n * }\n * });\n */\n\nexport {};\n\ndeclare global {\n // ============================================\n // Standard Fetch API (from lib.dom)\n // ============================================\n\n /**\n * Headers class for HTTP headers manipulation.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Headers\n */\n const Headers: typeof globalThis.Headers;\n\n /**\n * Request class for HTTP requests.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Request\n */\n const Request: typeof globalThis.Request;\n\n /**\n * Response class for HTTP responses.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Response\n */\n const Response: typeof globalThis.Response;\n\n /**\n * AbortController for cancelling fetch requests.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortController\n */\n const AbortController: typeof globalThis.AbortController;\n\n /**\n * AbortSignal for listening to abort events.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal\n */\n const AbortSignal: typeof globalThis.AbortSignal;\n\n /**\n * FormData for constructing form data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/FormData\n */\n const FormData: typeof globalThis.FormData;\n\n /**\n * Fetch function for making HTTP requests.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/fetch\n */\n function fetch(\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response>;\n\n // ============================================\n // QuickJS-specific: serve() API\n // ============================================\n\n /**\n * Server interface for handling WebSocket upgrades within serve() handlers.\n */\n interface Server {\n /**\n * Upgrade an HTTP request to a WebSocket connection.\n *\n * @param request - The incoming HTTP request to upgrade\n * @param options - Optional data to associate with the WebSocket connection\n * @returns true if upgrade will proceed, false otherwise\n *\n * @example\n * serve({\n * fetch(request, server) {\n * if (server.upgrade(request, { data: { userId: 123 } })) {\n * return new Response(null, { status: 101 });\n * }\n * return new Response(\"Upgrade failed\", { status: 400 });\n * }\n * });\n */\n upgrade(request: Request, options?: { data?: unknown }): boolean;\n }\n\n /**\n * ServerWebSocket interface for WebSocket connections within serve() handlers.\n *\n * @typeParam T - The type of data associated with this WebSocket connection\n */\n interface ServerWebSocket<T = unknown> {\n /**\n * User data associated with this connection.\n * Set via `server.upgrade(request, { data: ... })`.\n */\n readonly data: T;\n\n /**\n * Send a message to the client.\n *\n * @param message - The message to send (string, ArrayBuffer, or Uint8Array)\n */\n send(message: string | ArrayBuffer | Uint8Array): void;\n\n /**\n * Close the WebSocket connection.\n *\n * @param code - Optional close code (default: 1000)\n * @param reason - Optional close reason\n */\n close(code?: number, reason?: string): void;\n\n /**\n * WebSocket ready state.\n * - 0: CONNECTING\n * - 1: OPEN\n * - 2: CLOSING\n * - 3: CLOSED\n */\n readonly readyState: number;\n }\n\n /**\n * Options for the serve() function.\n *\n * @typeParam T - The type of data associated with WebSocket connections\n */\n interface ServeOptions<T = unknown> {\n /**\n * Handler for HTTP requests.\n *\n * @param request - The incoming HTTP request\n * @param server - Server interface for WebSocket upgrades\n * @returns Response or Promise resolving to Response\n */\n fetch(request: Request, server: Server): Response | Promise<Response>;\n\n /**\n * WebSocket event handlers.\n */\n websocket?: {\n /**\n * Called when a WebSocket connection is opened.\n *\n * @param ws - The WebSocket connection\n */\n open?(ws: ServerWebSocket<T>): void | Promise<void>;\n\n /**\n * Called when a message is received.\n *\n * @param ws - The WebSocket connection\n * @param message - The received message (string or ArrayBuffer)\n */\n message?(\n ws: ServerWebSocket<T>,\n message: string | ArrayBuffer\n ): void | Promise<void>;\n\n /**\n * Called when the connection is closed.\n *\n * @param ws - The WebSocket connection\n * @param code - The close code\n * @param reason - The close reason\n */\n close?(\n ws: ServerWebSocket<T>,\n code: number,\n reason: string\n ): void | Promise<void>;\n\n /**\n * Called when an error occurs.\n *\n * @param ws - The WebSocket connection\n * @param error - The error that occurred\n */\n error?(ws: ServerWebSocket<T>, error: Error): void | Promise<void>;\n };\n }\n\n /**\n * Register an HTTP server handler in QuickJS.\n *\n * Only one serve() handler can be active at a time.\n * Calling serve() again replaces the previous handler.\n *\n * @param options - Server configuration including fetch handler and optional WebSocket handlers\n *\n * @example\n * serve({\n * fetch(request, server) {\n * const url = new URL(request.url);\n *\n * if (url.pathname === \"/ws\") {\n * if (server.upgrade(request, { data: { connectedAt: Date.now() } })) {\n * return new Response(null, { status: 101 });\n * }\n * }\n *\n * if (url.pathname === \"/api/hello\") {\n * return Response.json({ message: \"Hello!\" });\n * }\n *\n * return new Response(\"Not Found\", { status: 404 });\n * },\n * websocket: {\n * open(ws) {\n * console.log(\"Connected at:\", ws.data.connectedAt);\n * },\n * message(ws, message) {\n * ws.send(\"Echo: \" + message);\n * },\n * close(ws, code, reason) {\n * console.log(\"Closed:\", code, reason);\n * }\n * }\n * });\n */\n function serve<T = unknown>(options: ServeOptions<T>): void;\n}\n";
|
|
25
|
+
/**
|
|
26
|
+
* Type definitions for @ricsam/quickjs-fs globals.
|
|
27
|
+
*
|
|
28
|
+
* Includes: fs namespace, FileSystemHandle, FileSystemFileHandle, FileSystemDirectoryHandle, FileSystemWritableFileStream
|
|
29
|
+
*/
|
|
30
|
+
export declare const FS_TYPES = "/**\n * QuickJS Global Type Definitions for @ricsam/quickjs-fs\n *\n * These types define the globals injected by setupFs() into a QuickJS context.\n * Use these types to typecheck user code that will run inside QuickJS.\n *\n * @example\n * // Typecheck QuickJS code with file system access\n * const root = await fs.getDirectory(\"/data\");\n * const fileHandle = await root.getFileHandle(\"config.json\");\n * const file = await fileHandle.getFile();\n * const content = await file.text();\n */\n\nexport {};\n\ndeclare global {\n // ============================================\n // fs namespace - QuickJS-specific entry point\n // ============================================\n\n /**\n * File System namespace providing access to the file system.\n * This is the QuickJS-specific entry point (differs from browser's navigator.storage.getDirectory()).\n */\n namespace fs {\n /**\n * Get a directory handle for the given path.\n *\n * The host controls which paths are accessible. Invalid or unauthorized\n * paths will throw an error.\n *\n * @param path - The path to request from the host\n * @returns A promise resolving to a directory handle\n * @throws If the path is not allowed or doesn't exist\n *\n * @example\n * const root = await fs.getDirectory(\"/\");\n * const dataDir = await fs.getDirectory(\"/data\");\n */\n function getDirectory(path: string): Promise<FileSystemDirectoryHandle>;\n }\n\n // ============================================\n // File System Access API\n // ============================================\n\n /**\n * Base interface for file system handles.\n */\n interface FileSystemHandle {\n /**\n * The kind of handle: \"file\" or \"directory\".\n */\n readonly kind: \"file\" | \"directory\";\n\n /**\n * The name of the file or directory.\n */\n readonly name: string;\n\n /**\n * Compare two handles to check if they reference the same entry.\n *\n * @param other - Another FileSystemHandle to compare against\n * @returns true if both handles reference the same entry\n */\n isSameEntry(other: FileSystemHandle): Promise<boolean>;\n }\n\n /**\n * Handle for a file in the file system.\n */\n interface FileSystemFileHandle extends FileSystemHandle {\n /**\n * Always \"file\" for file handles.\n */\n readonly kind: \"file\";\n\n /**\n * Get the file contents as a File object.\n *\n * @returns A promise resolving to a File object\n *\n * @example\n * const file = await fileHandle.getFile();\n * const text = await file.text();\n */\n getFile(): Promise<File>;\n\n /**\n * Create a writable stream for writing to the file.\n *\n * @param options - Options for the writable stream\n * @returns A promise resolving to a writable stream\n *\n * @example\n * const writable = await fileHandle.createWritable();\n * await writable.write(\"Hello, World!\");\n * await writable.close();\n */\n createWritable(options?: {\n /**\n * If true, keeps existing file data. Otherwise, truncates the file.\n */\n keepExistingData?: boolean;\n }): Promise<FileSystemWritableFileStream>;\n }\n\n /**\n * Handle for a directory in the file system.\n */\n interface FileSystemDirectoryHandle extends FileSystemHandle {\n /**\n * Always \"directory\" for directory handles.\n */\n readonly kind: \"directory\";\n\n /**\n * Get a file handle within this directory.\n *\n * @param name - The name of the file\n * @param options - Options for getting the file handle\n * @returns A promise resolving to a file handle\n * @throws If the file doesn't exist and create is not true\n *\n * @example\n * const file = await dir.getFileHandle(\"data.json\");\n * const newFile = await dir.getFileHandle(\"output.txt\", { create: true });\n */\n getFileHandle(\n name: string,\n options?: {\n /**\n * If true, creates the file if it doesn't exist.\n */\n create?: boolean;\n }\n ): Promise<FileSystemFileHandle>;\n\n /**\n * Get a subdirectory handle within this directory.\n *\n * @param name - The name of the subdirectory\n * @param options - Options for getting the directory handle\n * @returns A promise resolving to a directory handle\n * @throws If the directory doesn't exist and create is not true\n *\n * @example\n * const subdir = await dir.getDirectoryHandle(\"logs\");\n * const newDir = await dir.getDirectoryHandle(\"cache\", { create: true });\n */\n getDirectoryHandle(\n name: string,\n options?: {\n /**\n * If true, creates the directory if it doesn't exist.\n */\n create?: boolean;\n }\n ): Promise<FileSystemDirectoryHandle>;\n\n /**\n * Remove a file or directory within this directory.\n *\n * @param name - The name of the entry to remove\n * @param options - Options for removal\n * @throws If the entry doesn't exist or cannot be removed\n *\n * @example\n * await dir.removeEntry(\"old-file.txt\");\n * await dir.removeEntry(\"old-dir\", { recursive: true });\n */\n removeEntry(\n name: string,\n options?: {\n /**\n * If true, removes directories recursively.\n */\n recursive?: boolean;\n }\n ): Promise<void>;\n\n /**\n * Resolve the path from this directory to a descendant handle.\n *\n * @param possibleDescendant - A handle that may be a descendant\n * @returns An array of path segments, or null if not a descendant\n *\n * @example\n * const path = await root.resolve(nestedFile);\n * // [\"subdir\", \"file.txt\"]\n */\n resolve(possibleDescendant: FileSystemHandle): Promise<string[] | null>;\n\n /**\n * Iterate over entries in this directory.\n *\n * @returns An async iterator of [name, handle] pairs\n *\n * @example\n * for await (const [name, handle] of dir.entries()) {\n * console.log(name, handle.kind);\n * }\n */\n entries(): AsyncIterableIterator<[string, FileSystemHandle]>;\n\n /**\n * Iterate over entry names in this directory.\n *\n * @returns An async iterator of names\n *\n * @example\n * for await (const name of dir.keys()) {\n * console.log(name);\n * }\n */\n keys(): AsyncIterableIterator<string>;\n\n /**\n * Iterate over handles in this directory.\n *\n * @returns An async iterator of handles\n *\n * @example\n * for await (const handle of dir.values()) {\n * console.log(handle.name, handle.kind);\n * }\n */\n values(): AsyncIterableIterator<FileSystemHandle>;\n\n /**\n * Async iterator support for directory entries.\n *\n * @example\n * for await (const [name, handle] of dir) {\n * console.log(name, handle.kind);\n * }\n */\n [Symbol.asyncIterator](): AsyncIterableIterator<[string, FileSystemHandle]>;\n }\n\n /**\n * Parameters for write operations on FileSystemWritableFileStream.\n */\n interface WriteParams {\n /**\n * The type of write operation.\n * - \"write\": Write data at the current position or specified position\n * - \"seek\": Move the file position\n * - \"truncate\": Truncate the file to a specific size\n */\n type: \"write\" | \"seek\" | \"truncate\";\n\n /**\n * The data to write (for \"write\" type).\n */\n data?: string | ArrayBuffer | Uint8Array | Blob;\n\n /**\n * The position to write at or seek to.\n */\n position?: number;\n\n /**\n * The size to truncate to (for \"truncate\" type).\n */\n size?: number;\n }\n\n /**\n * Writable stream for writing to a file.\n * Extends WritableStream with file-specific operations.\n */\n interface FileSystemWritableFileStream extends WritableStream<Uint8Array> {\n /**\n * Write data to the file.\n *\n * @param data - The data to write\n * @returns A promise that resolves when the write completes\n *\n * @example\n * await writable.write(\"Hello, World!\");\n * await writable.write(new Uint8Array([1, 2, 3]));\n * await writable.write({ type: \"write\", data: \"text\", position: 0 });\n */\n write(\n data: string | ArrayBuffer | Uint8Array | Blob | WriteParams\n ): Promise<void>;\n\n /**\n * Seek to a position in the file.\n *\n * @param position - The byte position to seek to\n * @returns A promise that resolves when the seek completes\n *\n * @example\n * await writable.seek(0); // Seek to beginning\n * await writable.write(\"Overwrite\");\n */\n seek(position: number): Promise<void>;\n\n /**\n * Truncate the file to a specific size.\n *\n * @param size - The size to truncate to\n * @returns A promise that resolves when the truncation completes\n *\n * @example\n * await writable.truncate(100); // Keep only first 100 bytes\n */\n truncate(size: number): Promise<void>;\n }\n}\n";
|
|
31
|
+
/**
|
|
32
|
+
* Map of package names to their type definitions.
|
|
33
|
+
*/
|
|
34
|
+
export declare const TYPE_DEFINITIONS: {
|
|
35
|
+
readonly core: "/**\n * QuickJS Global Type Definitions for @ricsam/quickjs-core\n *\n * These types define the globals injected by setupCore() into a QuickJS context.\n * Use these types to typecheck user code that will run inside QuickJS.\n *\n * @example\n * // In your tsconfig.quickjs.json\n * {\n * \"compilerOptions\": {\n * \"lib\": [\"ESNext\", \"DOM\"]\n * }\n * }\n *\n * // Then reference this file or use ts-morph for code strings\n */\n\nexport {};\n\ndeclare global {\n // ============================================\n // Web Streams API\n // ============================================\n\n /**\n * A readable stream of data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream\n */\n const ReadableStream: typeof globalThis.ReadableStream;\n\n /**\n * A writable stream of data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/WritableStream\n */\n const WritableStream: typeof globalThis.WritableStream;\n\n /**\n * A transform stream that can be used to pipe data through a transformer.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/TransformStream\n */\n const TransformStream: typeof globalThis.TransformStream;\n\n /**\n * Default reader for ReadableStream\n * @see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader\n */\n const ReadableStreamDefaultReader: typeof globalThis.ReadableStreamDefaultReader;\n\n /**\n * Default writer for WritableStream\n * @see https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter\n */\n const WritableStreamDefaultWriter: typeof globalThis.WritableStreamDefaultWriter;\n\n // ============================================\n // Blob and File APIs\n // ============================================\n\n /**\n * A file-like object of immutable, raw data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Blob\n */\n const Blob: typeof globalThis.Blob;\n\n /**\n * A file object representing a file.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/File\n */\n const File: typeof globalThis.File;\n\n // ============================================\n // URL APIs\n // ============================================\n\n /**\n * Interface for URL manipulation.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/URL\n */\n const URL: typeof globalThis.URL;\n\n /**\n * Utility for working with URL query strings.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams\n */\n const URLSearchParams: typeof globalThis.URLSearchParams;\n\n // ============================================\n // Error Handling\n // ============================================\n\n /**\n * Exception type for DOM operations.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMException\n */\n const DOMException: typeof globalThis.DOMException;\n}\n";
|
|
36
|
+
readonly fetch: "/**\n * QuickJS Global Type Definitions for @ricsam/quickjs-fetch\n *\n * These types define the globals injected by setupFetch() into a QuickJS context.\n * Use these types to typecheck user code that will run inside QuickJS.\n *\n * @example\n * // Typecheck QuickJS code with serve()\n * serve({\n * fetch(request, server) {\n * if (request.url.includes(\"/ws\")) {\n * server.upgrade(request, { data: { id: 123 } });\n * return new Response(null, { status: 101 });\n * }\n * return new Response(\"Hello!\");\n * },\n * websocket: {\n * message(ws, message) {\n * ws.send(\"Echo: \" + message);\n * }\n * }\n * });\n */\n\nexport {};\n\ndeclare global {\n // ============================================\n // Standard Fetch API (from lib.dom)\n // ============================================\n\n /**\n * Headers class for HTTP headers manipulation.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Headers\n */\n const Headers: typeof globalThis.Headers;\n\n /**\n * Request class for HTTP requests.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Request\n */\n const Request: typeof globalThis.Request;\n\n /**\n * Response class for HTTP responses.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Response\n */\n const Response: typeof globalThis.Response;\n\n /**\n * AbortController for cancelling fetch requests.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortController\n */\n const AbortController: typeof globalThis.AbortController;\n\n /**\n * AbortSignal for listening to abort events.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal\n */\n const AbortSignal: typeof globalThis.AbortSignal;\n\n /**\n * FormData for constructing form data.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/FormData\n */\n const FormData: typeof globalThis.FormData;\n\n /**\n * Fetch function for making HTTP requests.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/fetch\n */\n function fetch(\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response>;\n\n // ============================================\n // QuickJS-specific: serve() API\n // ============================================\n\n /**\n * Server interface for handling WebSocket upgrades within serve() handlers.\n */\n interface Server {\n /**\n * Upgrade an HTTP request to a WebSocket connection.\n *\n * @param request - The incoming HTTP request to upgrade\n * @param options - Optional data to associate with the WebSocket connection\n * @returns true if upgrade will proceed, false otherwise\n *\n * @example\n * serve({\n * fetch(request, server) {\n * if (server.upgrade(request, { data: { userId: 123 } })) {\n * return new Response(null, { status: 101 });\n * }\n * return new Response(\"Upgrade failed\", { status: 400 });\n * }\n * });\n */\n upgrade(request: Request, options?: { data?: unknown }): boolean;\n }\n\n /**\n * ServerWebSocket interface for WebSocket connections within serve() handlers.\n *\n * @typeParam T - The type of data associated with this WebSocket connection\n */\n interface ServerWebSocket<T = unknown> {\n /**\n * User data associated with this connection.\n * Set via `server.upgrade(request, { data: ... })`.\n */\n readonly data: T;\n\n /**\n * Send a message to the client.\n *\n * @param message - The message to send (string, ArrayBuffer, or Uint8Array)\n */\n send(message: string | ArrayBuffer | Uint8Array): void;\n\n /**\n * Close the WebSocket connection.\n *\n * @param code - Optional close code (default: 1000)\n * @param reason - Optional close reason\n */\n close(code?: number, reason?: string): void;\n\n /**\n * WebSocket ready state.\n * - 0: CONNECTING\n * - 1: OPEN\n * - 2: CLOSING\n * - 3: CLOSED\n */\n readonly readyState: number;\n }\n\n /**\n * Options for the serve() function.\n *\n * @typeParam T - The type of data associated with WebSocket connections\n */\n interface ServeOptions<T = unknown> {\n /**\n * Handler for HTTP requests.\n *\n * @param request - The incoming HTTP request\n * @param server - Server interface for WebSocket upgrades\n * @returns Response or Promise resolving to Response\n */\n fetch(request: Request, server: Server): Response | Promise<Response>;\n\n /**\n * WebSocket event handlers.\n */\n websocket?: {\n /**\n * Called when a WebSocket connection is opened.\n *\n * @param ws - The WebSocket connection\n */\n open?(ws: ServerWebSocket<T>): void | Promise<void>;\n\n /**\n * Called when a message is received.\n *\n * @param ws - The WebSocket connection\n * @param message - The received message (string or ArrayBuffer)\n */\n message?(\n ws: ServerWebSocket<T>,\n message: string | ArrayBuffer\n ): void | Promise<void>;\n\n /**\n * Called when the connection is closed.\n *\n * @param ws - The WebSocket connection\n * @param code - The close code\n * @param reason - The close reason\n */\n close?(\n ws: ServerWebSocket<T>,\n code: number,\n reason: string\n ): void | Promise<void>;\n\n /**\n * Called when an error occurs.\n *\n * @param ws - The WebSocket connection\n * @param error - The error that occurred\n */\n error?(ws: ServerWebSocket<T>, error: Error): void | Promise<void>;\n };\n }\n\n /**\n * Register an HTTP server handler in QuickJS.\n *\n * Only one serve() handler can be active at a time.\n * Calling serve() again replaces the previous handler.\n *\n * @param options - Server configuration including fetch handler and optional WebSocket handlers\n *\n * @example\n * serve({\n * fetch(request, server) {\n * const url = new URL(request.url);\n *\n * if (url.pathname === \"/ws\") {\n * if (server.upgrade(request, { data: { connectedAt: Date.now() } })) {\n * return new Response(null, { status: 101 });\n * }\n * }\n *\n * if (url.pathname === \"/api/hello\") {\n * return Response.json({ message: \"Hello!\" });\n * }\n *\n * return new Response(\"Not Found\", { status: 404 });\n * },\n * websocket: {\n * open(ws) {\n * console.log(\"Connected at:\", ws.data.connectedAt);\n * },\n * message(ws, message) {\n * ws.send(\"Echo: \" + message);\n * },\n * close(ws, code, reason) {\n * console.log(\"Closed:\", code, reason);\n * }\n * }\n * });\n */\n function serve<T = unknown>(options: ServeOptions<T>): void;\n}\n";
|
|
37
|
+
readonly fs: "/**\n * QuickJS Global Type Definitions for @ricsam/quickjs-fs\n *\n * These types define the globals injected by setupFs() into a QuickJS context.\n * Use these types to typecheck user code that will run inside QuickJS.\n *\n * @example\n * // Typecheck QuickJS code with file system access\n * const root = await fs.getDirectory(\"/data\");\n * const fileHandle = await root.getFileHandle(\"config.json\");\n * const file = await fileHandle.getFile();\n * const content = await file.text();\n */\n\nexport {};\n\ndeclare global {\n // ============================================\n // fs namespace - QuickJS-specific entry point\n // ============================================\n\n /**\n * File System namespace providing access to the file system.\n * This is the QuickJS-specific entry point (differs from browser's navigator.storage.getDirectory()).\n */\n namespace fs {\n /**\n * Get a directory handle for the given path.\n *\n * The host controls which paths are accessible. Invalid or unauthorized\n * paths will throw an error.\n *\n * @param path - The path to request from the host\n * @returns A promise resolving to a directory handle\n * @throws If the path is not allowed or doesn't exist\n *\n * @example\n * const root = await fs.getDirectory(\"/\");\n * const dataDir = await fs.getDirectory(\"/data\");\n */\n function getDirectory(path: string): Promise<FileSystemDirectoryHandle>;\n }\n\n // ============================================\n // File System Access API\n // ============================================\n\n /**\n * Base interface for file system handles.\n */\n interface FileSystemHandle {\n /**\n * The kind of handle: \"file\" or \"directory\".\n */\n readonly kind: \"file\" | \"directory\";\n\n /**\n * The name of the file or directory.\n */\n readonly name: string;\n\n /**\n * Compare two handles to check if they reference the same entry.\n *\n * @param other - Another FileSystemHandle to compare against\n * @returns true if both handles reference the same entry\n */\n isSameEntry(other: FileSystemHandle): Promise<boolean>;\n }\n\n /**\n * Handle for a file in the file system.\n */\n interface FileSystemFileHandle extends FileSystemHandle {\n /**\n * Always \"file\" for file handles.\n */\n readonly kind: \"file\";\n\n /**\n * Get the file contents as a File object.\n *\n * @returns A promise resolving to a File object\n *\n * @example\n * const file = await fileHandle.getFile();\n * const text = await file.text();\n */\n getFile(): Promise<File>;\n\n /**\n * Create a writable stream for writing to the file.\n *\n * @param options - Options for the writable stream\n * @returns A promise resolving to a writable stream\n *\n * @example\n * const writable = await fileHandle.createWritable();\n * await writable.write(\"Hello, World!\");\n * await writable.close();\n */\n createWritable(options?: {\n /**\n * If true, keeps existing file data. Otherwise, truncates the file.\n */\n keepExistingData?: boolean;\n }): Promise<FileSystemWritableFileStream>;\n }\n\n /**\n * Handle for a directory in the file system.\n */\n interface FileSystemDirectoryHandle extends FileSystemHandle {\n /**\n * Always \"directory\" for directory handles.\n */\n readonly kind: \"directory\";\n\n /**\n * Get a file handle within this directory.\n *\n * @param name - The name of the file\n * @param options - Options for getting the file handle\n * @returns A promise resolving to a file handle\n * @throws If the file doesn't exist and create is not true\n *\n * @example\n * const file = await dir.getFileHandle(\"data.json\");\n * const newFile = await dir.getFileHandle(\"output.txt\", { create: true });\n */\n getFileHandle(\n name: string,\n options?: {\n /**\n * If true, creates the file if it doesn't exist.\n */\n create?: boolean;\n }\n ): Promise<FileSystemFileHandle>;\n\n /**\n * Get a subdirectory handle within this directory.\n *\n * @param name - The name of the subdirectory\n * @param options - Options for getting the directory handle\n * @returns A promise resolving to a directory handle\n * @throws If the directory doesn't exist and create is not true\n *\n * @example\n * const subdir = await dir.getDirectoryHandle(\"logs\");\n * const newDir = await dir.getDirectoryHandle(\"cache\", { create: true });\n */\n getDirectoryHandle(\n name: string,\n options?: {\n /**\n * If true, creates the directory if it doesn't exist.\n */\n create?: boolean;\n }\n ): Promise<FileSystemDirectoryHandle>;\n\n /**\n * Remove a file or directory within this directory.\n *\n * @param name - The name of the entry to remove\n * @param options - Options for removal\n * @throws If the entry doesn't exist or cannot be removed\n *\n * @example\n * await dir.removeEntry(\"old-file.txt\");\n * await dir.removeEntry(\"old-dir\", { recursive: true });\n */\n removeEntry(\n name: string,\n options?: {\n /**\n * If true, removes directories recursively.\n */\n recursive?: boolean;\n }\n ): Promise<void>;\n\n /**\n * Resolve the path from this directory to a descendant handle.\n *\n * @param possibleDescendant - A handle that may be a descendant\n * @returns An array of path segments, or null if not a descendant\n *\n * @example\n * const path = await root.resolve(nestedFile);\n * // [\"subdir\", \"file.txt\"]\n */\n resolve(possibleDescendant: FileSystemHandle): Promise<string[] | null>;\n\n /**\n * Iterate over entries in this directory.\n *\n * @returns An async iterator of [name, handle] pairs\n *\n * @example\n * for await (const [name, handle] of dir.entries()) {\n * console.log(name, handle.kind);\n * }\n */\n entries(): AsyncIterableIterator<[string, FileSystemHandle]>;\n\n /**\n * Iterate over entry names in this directory.\n *\n * @returns An async iterator of names\n *\n * @example\n * for await (const name of dir.keys()) {\n * console.log(name);\n * }\n */\n keys(): AsyncIterableIterator<string>;\n\n /**\n * Iterate over handles in this directory.\n *\n * @returns An async iterator of handles\n *\n * @example\n * for await (const handle of dir.values()) {\n * console.log(handle.name, handle.kind);\n * }\n */\n values(): AsyncIterableIterator<FileSystemHandle>;\n\n /**\n * Async iterator support for directory entries.\n *\n * @example\n * for await (const [name, handle] of dir) {\n * console.log(name, handle.kind);\n * }\n */\n [Symbol.asyncIterator](): AsyncIterableIterator<[string, FileSystemHandle]>;\n }\n\n /**\n * Parameters for write operations on FileSystemWritableFileStream.\n */\n interface WriteParams {\n /**\n * The type of write operation.\n * - \"write\": Write data at the current position or specified position\n * - \"seek\": Move the file position\n * - \"truncate\": Truncate the file to a specific size\n */\n type: \"write\" | \"seek\" | \"truncate\";\n\n /**\n * The data to write (for \"write\" type).\n */\n data?: string | ArrayBuffer | Uint8Array | Blob;\n\n /**\n * The position to write at or seek to.\n */\n position?: number;\n\n /**\n * The size to truncate to (for \"truncate\" type).\n */\n size?: number;\n }\n\n /**\n * Writable stream for writing to a file.\n * Extends WritableStream with file-specific operations.\n */\n interface FileSystemWritableFileStream extends WritableStream<Uint8Array> {\n /**\n * Write data to the file.\n *\n * @param data - The data to write\n * @returns A promise that resolves when the write completes\n *\n * @example\n * await writable.write(\"Hello, World!\");\n * await writable.write(new Uint8Array([1, 2, 3]));\n * await writable.write({ type: \"write\", data: \"text\", position: 0 });\n */\n write(\n data: string | ArrayBuffer | Uint8Array | Blob | WriteParams\n ): Promise<void>;\n\n /**\n * Seek to a position in the file.\n *\n * @param position - The byte position to seek to\n * @returns A promise that resolves when the seek completes\n *\n * @example\n * await writable.seek(0); // Seek to beginning\n * await writable.write(\"Overwrite\");\n */\n seek(position: number): Promise<void>;\n\n /**\n * Truncate the file to a specific size.\n *\n * @param size - The size to truncate to\n * @returns A promise that resolves when the truncation completes\n *\n * @example\n * await writable.truncate(100); // Keep only first 100 bytes\n */\n truncate(size: number): Promise<void>;\n }\n}\n";
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Type for the keys of TYPE_DEFINITIONS.
|
|
41
|
+
*/
|
|
42
|
+
export type TypeDefinitionKey = keyof typeof TYPE_DEFINITIONS;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type QuickJSContext, type QuickJSRuntime } from "quickjs-emscripten";
|
|
2
|
+
import { type RuntimeHandle, type SetupRuntimeOptions } from "@ricsam/quickjs-runtime";
|
|
3
|
+
export interface RuntimeTestContext {
|
|
4
|
+
runtime: QuickJSRuntime;
|
|
5
|
+
context: QuickJSContext;
|
|
6
|
+
runtimeHandle: RuntimeHandle;
|
|
7
|
+
}
|
|
8
|
+
export declare function createRuntimeTestContext(options?: SetupRuntimeOptions): Promise<RuntimeTestContext>;
|
|
9
|
+
export declare function disposeRuntimeTestContext(ctx: RuntimeTestContext): void;
|