tywrap 0.5.1 → 0.6.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/dist/cli.js +8 -6
- package/dist/cli.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +236 -147
- package/dist/config/index.js.map +1 -1
- package/dist/core/annotation-parser.d.ts.map +1 -1
- package/dist/core/annotation-parser.js +112 -97
- package/dist/core/annotation-parser.js.map +1 -1
- package/dist/core/discovery.d.ts +17 -0
- package/dist/core/discovery.d.ts.map +1 -1
- package/dist/core/discovery.js +85 -49
- package/dist/core/discovery.js.map +1 -1
- package/dist/core/emit-call.d.ts +58 -0
- package/dist/core/emit-call.d.ts.map +1 -0
- package/dist/core/emit-call.js +103 -0
- package/dist/core/emit-call.js.map +1 -0
- package/dist/core/generator.d.ts +2 -0
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/generator.js +54 -211
- package/dist/core/generator.js.map +1 -1
- package/dist/core/mapper.d.ts.map +1 -1
- package/dist/core/mapper.js +118 -104
- package/dist/core/mapper.js.map +1 -1
- package/dist/core/validation.d.ts +23 -0
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +52 -48
- package/dist/core/validation.js.map +1 -1
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +175 -133
- package/dist/dev.js.map +1 -1
- package/dist/index.d.ts +6 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -31
- package/dist/index.js.map +1 -1
- package/dist/runtime/bounded-context.d.ts +2 -2
- package/dist/runtime/bounded-context.js +2 -2
- package/dist/runtime/{safe-codec.d.ts → bridge-codec.d.ts} +26 -11
- package/dist/runtime/bridge-codec.d.ts.map +1 -0
- package/dist/runtime/{safe-codec.js → bridge-codec.js} +69 -72
- package/dist/runtime/bridge-codec.js.map +1 -0
- package/dist/runtime/{http-io.d.ts → http-transport.d.ts} +6 -6
- package/dist/runtime/http-transport.d.ts.map +1 -0
- package/dist/runtime/{http-io.js → http-transport.js} +3 -3
- package/dist/runtime/http-transport.js.map +1 -0
- package/dist/runtime/http.d.ts +3 -3
- package/dist/runtime/http.d.ts.map +1 -1
- package/dist/runtime/http.js +4 -4
- package/dist/runtime/http.js.map +1 -1
- package/dist/runtime/index.d.ts +9 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +9 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/node.d.ts +6 -6
- package/dist/runtime/node.d.ts.map +1 -1
- package/dist/runtime/node.js +7 -7
- package/dist/runtime/node.js.map +1 -1
- package/dist/runtime/pooled-transport.d.ts +11 -11
- package/dist/runtime/pooled-transport.d.ts.map +1 -1
- package/dist/runtime/pooled-transport.js +10 -10
- package/dist/runtime/pooled-transport.js.map +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.js +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -1
- package/dist/runtime/{pyodide-io.d.ts → pyodide-transport.d.ts} +8 -8
- package/dist/runtime/pyodide-transport.d.ts.map +1 -0
- package/dist/runtime/{pyodide-io.js → pyodide-transport.js} +5 -5
- package/dist/runtime/pyodide-transport.js.map +1 -0
- package/dist/runtime/pyodide.d.ts +3 -3
- package/dist/runtime/pyodide.d.ts.map +1 -1
- package/dist/runtime/pyodide.js +5 -5
- package/dist/runtime/pyodide.js.map +1 -1
- package/dist/runtime/rpc-client.d.ts +23 -5
- package/dist/runtime/rpc-client.d.ts.map +1 -1
- package/dist/runtime/rpc-client.js +36 -31
- package/dist/runtime/rpc-client.js.map +1 -1
- package/dist/runtime/{process-io.d.ts → subprocess-transport.d.ts} +25 -9
- package/dist/runtime/subprocess-transport.d.ts.map +1 -0
- package/dist/runtime/{process-io.js → subprocess-transport.js} +60 -41
- package/dist/runtime/subprocess-transport.js.map +1 -0
- package/dist/runtime/{worker-pool.d.ts → transport-pool.d.ts} +15 -15
- package/dist/runtime/transport-pool.d.ts.map +1 -0
- package/dist/runtime/{worker-pool.js → transport-pool.js} +6 -6
- package/dist/runtime/transport-pool.js.map +1 -0
- package/dist/runtime/transport.d.ts +14 -8
- package/dist/runtime/transport.d.ts.map +1 -1
- package/dist/runtime/transport.js +10 -4
- package/dist/runtime/transport.js.map +1 -1
- package/dist/types/index.d.ts +10 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/tywrap.d.ts +0 -5
- package/dist/tywrap.d.ts.map +1 -1
- package/dist/tywrap.js +0 -20
- package/dist/tywrap.js.map +1 -1
- package/dist/utils/cache.d.ts +13 -2
- package/dist/utils/cache.d.ts.map +1 -1
- package/dist/utils/cache.js +52 -60
- package/dist/utils/cache.js.map +1 -1
- package/dist/utils/codec.d.ts +1 -1
- package/dist/utils/codec.d.ts.map +1 -1
- package/dist/utils/codec.js +181 -182
- package/dist/utils/codec.js.map +1 -1
- package/dist/utils/python.d.ts.map +1 -1
- package/dist/utils/python.js +28 -17
- package/dist/utils/python.js.map +1 -1
- package/dist/utils/runtime.d.ts.map +1 -1
- package/dist/utils/runtime.js +22 -16
- package/dist/utils/runtime.js.map +1 -1
- package/dist/version.d.ts +12 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +12 -0
- package/dist/version.js.map +1 -0
- package/package.json +3 -2
- package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
- package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
- package/runtime/python_bridge.py +54 -6
- package/runtime/safe_codec.py +8 -8
- package/runtime/tywrap_bridge_core.py +159 -23
- package/src/cli.ts +8 -12
- package/src/config/index.ts +271 -165
- package/src/core/annotation-parser.ts +135 -123
- package/src/core/discovery.ts +102 -54
- package/src/core/emit-call.ts +171 -0
- package/src/core/generator.ts +67 -263
- package/src/core/mapper.ts +125 -110
- package/src/core/validation.ts +84 -48
- package/src/dev.ts +237 -153
- package/src/index.ts +11 -68
- package/src/runtime/bounded-context.ts +2 -2
- package/src/runtime/{safe-codec.ts → bridge-codec.ts} +83 -95
- package/src/runtime/{http-io.ts → http-transport.ts} +5 -5
- package/src/runtime/http.ts +5 -5
- package/src/runtime/index.ts +23 -1
- package/src/runtime/node.ts +15 -15
- package/src/runtime/pooled-transport.ts +15 -15
- package/src/runtime/pyodide-bootstrap-core.generated.ts +1 -1
- package/src/runtime/{pyodide-io.ts → pyodide-transport.ts} +7 -7
- package/src/runtime/pyodide.ts +6 -6
- package/src/runtime/rpc-client.ts +51 -40
- package/src/runtime/{process-io.ts → subprocess-transport.ts} +77 -47
- package/src/runtime/{worker-pool.ts → transport-pool.ts} +26 -26
- package/src/runtime/transport.ts +15 -8
- package/src/types/index.ts +10 -1
- package/src/tywrap.ts +0 -24
- package/src/utils/cache.ts +61 -63
- package/src/utils/codec.ts +217 -196
- package/src/utils/python.ts +33 -20
- package/src/utils/runtime.ts +24 -13
- package/src/version.ts +12 -0
- package/dist/runtime/http-io.d.ts.map +0 -1
- package/dist/runtime/http-io.js.map +0 -1
- package/dist/runtime/optimized-node.d.ts +0 -23
- package/dist/runtime/optimized-node.d.ts.map +0 -1
- package/dist/runtime/optimized-node.js +0 -23
- package/dist/runtime/optimized-node.js.map +0 -1
- package/dist/runtime/process-io.d.ts.map +0 -1
- package/dist/runtime/process-io.js.map +0 -1
- package/dist/runtime/protocol.d.ts +0 -3
- package/dist/runtime/protocol.d.ts.map +0 -1
- package/dist/runtime/protocol.js +0 -3
- package/dist/runtime/protocol.js.map +0 -1
- package/dist/runtime/pyodide-io.d.ts.map +0 -1
- package/dist/runtime/pyodide-io.js.map +0 -1
- package/dist/runtime/safe-codec.d.ts.map +0 -1
- package/dist/runtime/safe-codec.js.map +0 -1
- package/dist/runtime/worker-pool.d.ts.map +0 -1
- package/dist/runtime/worker-pool.js.map +0 -1
- package/dist/utils/bundle-optimizer.d.ts +0 -182
- package/dist/utils/bundle-optimizer.d.ts.map +0 -1
- package/dist/utils/bundle-optimizer.js +0 -680
- package/dist/utils/bundle-optimizer.js.map +0 -1
- package/dist/utils/memory-profiler.d.ts +0 -123
- package/dist/utils/memory-profiler.d.ts.map +0 -1
- package/dist/utils/memory-profiler.js +0 -426
- package/dist/utils/memory-profiler.js.map +0 -1
- package/src/runtime/optimized-node.ts +0 -25
- package/src/runtime/protocol.ts +0 -2
- package/src/utils/bundle-optimizer.ts +0 -876
- package/src/utils/memory-profiler.ts +0 -582
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* TransportPool - Manages multiple Transport instances for concurrent request handling.
|
|
3
3
|
*
|
|
4
4
|
* Provides semaphore-based concurrency control with configurable limits per worker
|
|
5
5
|
* and a wait queue for callers when all workers are at capacity.
|
|
@@ -16,9 +16,9 @@ import type { Transport } from './transport.js';
|
|
|
16
16
|
// =============================================================================
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Configuration options for the
|
|
19
|
+
* Configuration options for the TransportPool.
|
|
20
20
|
*/
|
|
21
|
-
export interface
|
|
21
|
+
export interface TransportPoolOptions {
|
|
22
22
|
/** Factory function to create transports */
|
|
23
23
|
createTransport: () => Transport;
|
|
24
24
|
|
|
@@ -38,20 +38,20 @@ export interface WorkerPoolOptions {
|
|
|
38
38
|
* Callback invoked after each worker is created and initialized.
|
|
39
39
|
* Use this for per-worker warmup (e.g., importing modules, running setup).
|
|
40
40
|
*/
|
|
41
|
-
onWorkerReady?: (worker:
|
|
41
|
+
onWorkerReady?: (worker: TransportLease) => Promise<void>;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Optional callback used only for background replacement workers after a
|
|
45
45
|
* fatal timeout/crash. This lets callers publish a replacement only after it
|
|
46
46
|
* is proven ready, without charging hidden work to normal request startup.
|
|
47
47
|
*/
|
|
48
|
-
onReplacementWorkerReady?: (worker:
|
|
48
|
+
onReplacementWorkerReady?: (worker: TransportLease) => Promise<void>;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* A pooled worker with its transport and current in-flight request count.
|
|
53
53
|
*/
|
|
54
|
-
export interface
|
|
54
|
+
export interface TransportLease {
|
|
55
55
|
/** The underlying transport instance */
|
|
56
56
|
transport: Transport;
|
|
57
57
|
|
|
@@ -64,7 +64,7 @@ export interface PooledWorker {
|
|
|
64
64
|
*/
|
|
65
65
|
interface QueuedWaiter {
|
|
66
66
|
/** Resolve function to fulfill the promise with a worker */
|
|
67
|
-
resolve: (worker:
|
|
67
|
+
resolve: (worker: TransportLease) => void;
|
|
68
68
|
|
|
69
69
|
/** Reject function to reject the promise with an error */
|
|
70
70
|
reject: (error: Error) => void;
|
|
@@ -88,8 +88,8 @@ interface QueuedWaiter {
|
|
|
88
88
|
*
|
|
89
89
|
* @example
|
|
90
90
|
* ```typescript
|
|
91
|
-
* const pool = new
|
|
92
|
-
* createTransport: () => new
|
|
91
|
+
* const pool = new TransportPool({
|
|
92
|
+
* createTransport: () => new SubprocessTransport({ pythonPath: 'python3' }),
|
|
93
93
|
* maxWorkers: 4,
|
|
94
94
|
* maxConcurrentPerWorker: 2,
|
|
95
95
|
* queueTimeoutMs: 5000,
|
|
@@ -105,25 +105,25 @@ interface QueuedWaiter {
|
|
|
105
105
|
* await pool.dispose();
|
|
106
106
|
* ```
|
|
107
107
|
*/
|
|
108
|
-
export class
|
|
108
|
+
export class TransportPool extends DisposableBase {
|
|
109
109
|
private readonly options: Omit<
|
|
110
|
-
Required<
|
|
110
|
+
Required<TransportPoolOptions>,
|
|
111
111
|
'onWorkerReady' | 'onReplacementWorkerReady'
|
|
112
112
|
> & {
|
|
113
|
-
onWorkerReady?: (worker:
|
|
114
|
-
onReplacementWorkerReady?: (worker:
|
|
113
|
+
onWorkerReady?: (worker: TransportLease) => Promise<void>;
|
|
114
|
+
onReplacementWorkerReady?: (worker: TransportLease) => Promise<void>;
|
|
115
115
|
};
|
|
116
|
-
private readonly workers:
|
|
116
|
+
private readonly workers: TransportLease[] = [];
|
|
117
117
|
private readonly waitQueue: QueuedWaiter[] = [];
|
|
118
118
|
/** Tracks workers being created to prevent race condition in acquire() */
|
|
119
119
|
private pendingCreations = 0;
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
* Create a new
|
|
122
|
+
* Create a new TransportPool.
|
|
123
123
|
*
|
|
124
124
|
* @param options - Pool configuration options
|
|
125
125
|
*/
|
|
126
|
-
constructor(options:
|
|
126
|
+
constructor(options: TransportPoolOptions) {
|
|
127
127
|
super();
|
|
128
128
|
|
|
129
129
|
// Validate required options
|
|
@@ -216,7 +216,7 @@ export class WorkerPool extends DisposableBase {
|
|
|
216
216
|
* @throws BridgeTimeoutError if queue timeout expires
|
|
217
217
|
* @throws BridgeExecutionError if pool is disposed while waiting
|
|
218
218
|
*/
|
|
219
|
-
async acquire(): Promise<
|
|
219
|
+
async acquire(): Promise<TransportLease> {
|
|
220
220
|
// Check for disposed state
|
|
221
221
|
if (this.isDisposed || this.state === 'disposing') {
|
|
222
222
|
throw new BridgeExecutionError('Pool has been disposed');
|
|
@@ -261,7 +261,7 @@ export class WorkerPool extends DisposableBase {
|
|
|
261
261
|
*
|
|
262
262
|
* @param worker - The worker to release
|
|
263
263
|
*/
|
|
264
|
-
release(worker:
|
|
264
|
+
release(worker: TransportLease): void {
|
|
265
265
|
// Validate the worker belongs to this pool
|
|
266
266
|
if (!this.workers.includes(worker)) {
|
|
267
267
|
return;
|
|
@@ -288,7 +288,7 @@ export class WorkerPool extends DisposableBase {
|
|
|
288
288
|
* });
|
|
289
289
|
* ```
|
|
290
290
|
*/
|
|
291
|
-
async withWorker<T>(fn: (worker:
|
|
291
|
+
async withWorker<T>(fn: (worker: TransportLease) => Promise<T>): Promise<T> {
|
|
292
292
|
const worker = await this.acquire();
|
|
293
293
|
let workerRemoved = false;
|
|
294
294
|
|
|
@@ -341,7 +341,7 @@ export class WorkerPool extends DisposableBase {
|
|
|
341
341
|
* This is called when a worker is detected as dead (crashed, pipe error, etc.).
|
|
342
342
|
* The worker's transport is disposed in the background.
|
|
343
343
|
*/
|
|
344
|
-
private removeWorker(worker:
|
|
344
|
+
private removeWorker(worker: TransportLease): void {
|
|
345
345
|
const index = this.workers.indexOf(worker);
|
|
346
346
|
if (index !== -1) {
|
|
347
347
|
this.workers.splice(index, 1);
|
|
@@ -387,7 +387,7 @@ export class WorkerPool extends DisposableBase {
|
|
|
387
387
|
/**
|
|
388
388
|
* Find an available worker with capacity for another request.
|
|
389
389
|
*/
|
|
390
|
-
private findAvailableWorker():
|
|
390
|
+
private findAvailableWorker(): TransportLease | undefined {
|
|
391
391
|
return this.workers.find(w => w.inFlightCount < this.options.maxConcurrentPerWorker);
|
|
392
392
|
}
|
|
393
393
|
|
|
@@ -452,7 +452,7 @@ export class WorkerPool extends DisposableBase {
|
|
|
452
452
|
this.publishAvailableWorker(worker);
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
-
private publishAvailableWorker(worker:
|
|
455
|
+
private publishAvailableWorker(worker: TransportLease): void {
|
|
456
456
|
while (
|
|
457
457
|
this.waitQueue.length > 0 &&
|
|
458
458
|
worker.inFlightCount < this.options.maxConcurrentPerWorker
|
|
@@ -473,13 +473,13 @@ export class WorkerPool extends DisposableBase {
|
|
|
473
473
|
* If onWorkerReady is configured, calls it after the transport is initialized.
|
|
474
474
|
* This is useful for per-worker warmup (importing modules, running setup).
|
|
475
475
|
*/
|
|
476
|
-
private async createWorker(onWorkerReady = this.options.onWorkerReady): Promise<
|
|
476
|
+
private async createWorker(onWorkerReady = this.options.onWorkerReady): Promise<TransportLease> {
|
|
477
477
|
const transport = this.options.createTransport();
|
|
478
478
|
|
|
479
479
|
// Initialize the transport
|
|
480
480
|
await transport.init();
|
|
481
481
|
|
|
482
|
-
const worker:
|
|
482
|
+
const worker: TransportLease = {
|
|
483
483
|
transport,
|
|
484
484
|
inFlightCount: 0,
|
|
485
485
|
};
|
|
@@ -512,8 +512,8 @@ export class WorkerPool extends DisposableBase {
|
|
|
512
512
|
/**
|
|
513
513
|
* Wait in queue for a worker to become available.
|
|
514
514
|
*/
|
|
515
|
-
private waitForWorker(): Promise<
|
|
516
|
-
return new Promise<
|
|
515
|
+
private waitForWorker(): Promise<TransportLease> {
|
|
516
|
+
return new Promise<TransportLease>((resolve, reject) => {
|
|
517
517
|
const timer = setTimeout(() => {
|
|
518
518
|
// Remove this waiter from the queue
|
|
519
519
|
const index = this.waitQueue.findIndex(w => w.timer === timer);
|
package/src/runtime/transport.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides an abstract I/O channel for all bridge communications across
|
|
5
5
|
* the JS-Python boundary. Concrete implementations handle different runtimes:
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
6
|
+
* - SubprocessTransport: Child process with stdio streams (Node.js)
|
|
7
|
+
* - HttpTransport: HTTP POST requests (remote Python server)
|
|
8
|
+
* - PyodideTransport: In-memory Pyodide calls (browser/WASM)
|
|
9
9
|
*
|
|
10
10
|
* @see https://github.com/bbopen/tywrap/issues/149
|
|
11
11
|
*/
|
|
@@ -16,9 +16,16 @@ import type { Disposable } from './disposable.js';
|
|
|
16
16
|
// PROTOCOL CONSTANTS
|
|
17
17
|
// =============================================================================
|
|
18
18
|
|
|
19
|
-
/** Protocol identifier for tywrap communication */
|
|
19
|
+
/** Protocol identifier for tywrap communication. Single source of truth for the version. */
|
|
20
20
|
export const PROTOCOL_ID = 'tywrap/1';
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Numeric protocol version negotiated with the Python bridge. Derived from the
|
|
24
|
+
* trailing number of {@link PROTOCOL_ID} so the two cannot drift — bump
|
|
25
|
+
* PROTOCOL_ID alone and this follows.
|
|
26
|
+
*/
|
|
27
|
+
export const TYWRAP_PROTOCOL_VERSION = Number.parseInt(PROTOCOL_ID.split('/')[1] ?? '', 10);
|
|
28
|
+
|
|
22
29
|
// =============================================================================
|
|
23
30
|
// PROTOCOL TYPES
|
|
24
31
|
// =============================================================================
|
|
@@ -112,13 +119,13 @@ export interface ProtocolResponse {
|
|
|
112
119
|
* 4. Call `dispose()` to release resources
|
|
113
120
|
*
|
|
114
121
|
* Implementations:
|
|
115
|
-
* -
|
|
116
|
-
* -
|
|
117
|
-
* -
|
|
122
|
+
* - SubprocessTransport: Spawns a Python child process, communicates via stdio
|
|
123
|
+
* - HttpTransport: Sends HTTP POST requests to a Python server
|
|
124
|
+
* - PyodideTransport: Calls Pyodide directly in-memory (WASM)
|
|
118
125
|
*
|
|
119
126
|
* @example
|
|
120
127
|
* ```typescript
|
|
121
|
-
* const transport = new
|
|
128
|
+
* const transport = new SubprocessTransport({ pythonPath: 'python3' });
|
|
122
129
|
* await transport.init();
|
|
123
130
|
*
|
|
124
131
|
* const response = await transport.send(
|
package/src/types/index.ts
CHANGED
|
@@ -310,7 +310,14 @@ export interface TywrapOptions {
|
|
|
310
310
|
|
|
311
311
|
export interface PythonModuleConfig {
|
|
312
312
|
version?: string;
|
|
313
|
-
|
|
313
|
+
/**
|
|
314
|
+
* @deprecated Dead input. The per-module runtime is never read during code
|
|
315
|
+
* generation — the active runtime is resolved from the top-level
|
|
316
|
+
* {@link RuntimeConfig} (`runtime.node`, etc.). Kept optional so existing
|
|
317
|
+
* configs that still set it continue to validate; it has no effect and will
|
|
318
|
+
* be removed in a future major release.
|
|
319
|
+
*/
|
|
320
|
+
runtime?: RuntimeStrategy;
|
|
314
321
|
functions?: string[];
|
|
315
322
|
classes?: string[];
|
|
316
323
|
/** Exclude specific exports by exact name. */
|
|
@@ -343,11 +350,13 @@ export interface PyodideConfig {
|
|
|
343
350
|
export interface NodeConfig {
|
|
344
351
|
pythonPath?: string;
|
|
345
352
|
virtualEnv?: string;
|
|
353
|
+
/** Per-call timeout in milliseconds for the subprocess bridge. */
|
|
346
354
|
timeout?: number;
|
|
347
355
|
}
|
|
348
356
|
|
|
349
357
|
export interface HttpConfig {
|
|
350
358
|
baseURL: string;
|
|
359
|
+
/** Per-request timeout in milliseconds for the HTTP bridge. */
|
|
351
360
|
timeout?: number;
|
|
352
361
|
headers?: Record<string, string>;
|
|
353
362
|
}
|
package/src/tywrap.ts
CHANGED
|
@@ -52,30 +52,6 @@ export async function tywrap(options: Partial<TywrapOptions> = {}): Promise<Tywr
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
export default tywrap;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Append minimal tsd tests for a generated module (optional dev aid)
|
|
59
|
-
*/
|
|
60
|
-
export async function emitTypeTestsForModule(
|
|
61
|
-
moduleName: string,
|
|
62
|
-
outDir = 'test-d/generated'
|
|
63
|
-
): Promise<void> {
|
|
64
|
-
const { writeFile, mkdir } = await import('fs/promises');
|
|
65
|
-
const { join } = await import('path');
|
|
66
|
-
try {
|
|
67
|
-
await mkdir(outDir, { recursive: true });
|
|
68
|
-
} catch {}
|
|
69
|
-
const filePath = join(outDir, `${moduleName}.test-d.ts`);
|
|
70
|
-
const content = `import { expectType } from 'tsd';
|
|
71
|
-
import * as mod from '../../generated/${moduleName}.generated.ts';
|
|
72
|
-
|
|
73
|
-
// Opportunistic: ensure module namespace is an object
|
|
74
|
-
expectType<Record<string, unknown>>(mod);
|
|
75
|
-
`;
|
|
76
|
-
await writeFile(filePath, content, 'utf-8');
|
|
77
|
-
}
|
|
78
|
-
|
|
79
55
|
export interface GenerateRunOptions {
|
|
80
56
|
/**
|
|
81
57
|
* If true, do not write files; instead compare generated output to what's on disk.
|
package/src/utils/cache.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface CacheConfig {
|
|
|
47
47
|
debug?: boolean;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
export class
|
|
50
|
+
export class ArtifactCache {
|
|
51
51
|
private memoryCache = new Map<string, CacheEntry>();
|
|
52
52
|
private stats = {
|
|
53
53
|
hits: 0,
|
|
@@ -149,75 +149,73 @@ export class IntelligentCache {
|
|
|
149
149
|
hash.update(prefix);
|
|
150
150
|
hash.update('\0');
|
|
151
151
|
|
|
152
|
-
// Add inputs
|
|
152
|
+
// Add inputs. Each input contributes a type tag, an optional payload, and a
|
|
153
|
+
// boundary separator so that "1" vs 1 and ["a","bc"] vs ["ab","c"] never collide.
|
|
153
154
|
for (const input of inputs) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
hash.update(
|
|
158
|
-
hash.update('\0');
|
|
159
|
-
continue;
|
|
155
|
+
const { tag, payload } = ArtifactCache.encodeKeyInput(input);
|
|
156
|
+
hash.update(tag);
|
|
157
|
+
if (payload !== undefined) {
|
|
158
|
+
hash.update(payload);
|
|
160
159
|
}
|
|
161
|
-
if (input === null) {
|
|
162
|
-
hash.update('null:');
|
|
163
|
-
hash.update('\0');
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
if (typeof input === 'string') {
|
|
167
|
-
hash.update('str:');
|
|
168
|
-
hash.update(input);
|
|
169
|
-
hash.update('\0');
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
if (typeof input === 'number') {
|
|
173
|
-
hash.update('num:');
|
|
174
|
-
hash.update(String(input));
|
|
175
|
-
hash.update('\0');
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
if (typeof input === 'boolean') {
|
|
179
|
-
hash.update('bool:');
|
|
180
|
-
hash.update(input ? '1' : '0');
|
|
181
|
-
hash.update('\0');
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
if (typeof input === 'bigint') {
|
|
185
|
-
hash.update('bigint:');
|
|
186
|
-
hash.update(input.toString());
|
|
187
|
-
hash.update('\0');
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
if (Buffer.isBuffer(input)) {
|
|
191
|
-
hash.update('buf:');
|
|
192
|
-
hash.update(input);
|
|
193
|
-
hash.update('\0');
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (typeof input === 'symbol') {
|
|
198
|
-
hash.update('sym:');
|
|
199
|
-
hash.update(input.toString());
|
|
200
|
-
hash.update('\0');
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
hash.update('json:');
|
|
205
|
-
let json: string;
|
|
206
|
-
try {
|
|
207
|
-
json =
|
|
208
|
-
JSON.stringify(input, (_key, value) =>
|
|
209
|
-
typeof value === 'symbol' ? value.toString() : value
|
|
210
|
-
) ?? 'undefined';
|
|
211
|
-
} catch {
|
|
212
|
-
json = String(input);
|
|
213
|
-
}
|
|
214
|
-
hash.update(json);
|
|
215
160
|
hash.update('\0');
|
|
216
161
|
}
|
|
217
162
|
|
|
218
163
|
return hash.digest('hex').substring(0, 16); // Use first 16 chars for readability
|
|
219
164
|
}
|
|
220
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Encode a single key input as a type tag plus optional payload.
|
|
168
|
+
* Why: disambiguate types so "1" and 1 don't collide, and keep input boundaries so
|
|
169
|
+
* ["a", "bc"] doesn't collide with ["ab", "c"].
|
|
170
|
+
*/
|
|
171
|
+
private static encodeKeyInput(input: unknown): {
|
|
172
|
+
tag: string;
|
|
173
|
+
payload?: string | Buffer;
|
|
174
|
+
} {
|
|
175
|
+
if (input === undefined) {
|
|
176
|
+
return { tag: 'undef:' };
|
|
177
|
+
}
|
|
178
|
+
if (input === null) {
|
|
179
|
+
return { tag: 'null:' };
|
|
180
|
+
}
|
|
181
|
+
if (typeof input === 'string') {
|
|
182
|
+
return { tag: 'str:', payload: input };
|
|
183
|
+
}
|
|
184
|
+
if (typeof input === 'number') {
|
|
185
|
+
return { tag: 'num:', payload: String(input) };
|
|
186
|
+
}
|
|
187
|
+
if (typeof input === 'boolean') {
|
|
188
|
+
return { tag: 'bool:', payload: input ? '1' : '0' };
|
|
189
|
+
}
|
|
190
|
+
if (typeof input === 'bigint') {
|
|
191
|
+
return { tag: 'bigint:', payload: input.toString() };
|
|
192
|
+
}
|
|
193
|
+
if (Buffer.isBuffer(input)) {
|
|
194
|
+
return { tag: 'buf:', payload: input };
|
|
195
|
+
}
|
|
196
|
+
if (typeof input === 'symbol') {
|
|
197
|
+
return { tag: 'sym:', payload: input.toString() };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return { tag: 'json:', payload: ArtifactCache.stringifyKeyInput(input) };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Stringify an arbitrary key input for hashing, normalizing symbols and
|
|
205
|
+
* falling back to String() when JSON serialization fails.
|
|
206
|
+
*/
|
|
207
|
+
private static stringifyKeyInput(input: unknown): string {
|
|
208
|
+
try {
|
|
209
|
+
return (
|
|
210
|
+
JSON.stringify(input, (_key, value) =>
|
|
211
|
+
typeof value === 'symbol' ? value.toString() : value
|
|
212
|
+
) ?? 'undefined'
|
|
213
|
+
);
|
|
214
|
+
} catch {
|
|
215
|
+
return String(input);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
221
219
|
/**
|
|
222
220
|
* Get entry from cache with performance tracking
|
|
223
221
|
*/
|
|
@@ -685,4 +683,4 @@ export class IntelligentCache {
|
|
|
685
683
|
}
|
|
686
684
|
|
|
687
685
|
// Export singleton instance
|
|
688
|
-
export const globalCache = new
|
|
686
|
+
export const globalCache = new ArtifactCache();
|