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
package/src/dev.ts
CHANGED
|
@@ -335,42 +335,44 @@ async function listExistingManagedFiles(outputDir: string): Promise<Set<string>>
|
|
|
335
335
|
return managedFiles;
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
+
async function listChildDirectories(current: string): Promise<string[]> {
|
|
339
|
+
let entries;
|
|
340
|
+
try {
|
|
341
|
+
entries = await readdir(current, { withFileTypes: true });
|
|
342
|
+
} catch {
|
|
343
|
+
return [];
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return entries
|
|
347
|
+
.filter(entry => entry.isDirectory())
|
|
348
|
+
.map(entry => join(current, entry.name));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function isWatchableDirectory(current: string, ignoredPaths: string[]): Promise<boolean> {
|
|
352
|
+
if (shouldIgnoreWatchPath(current, ignoredPaths)) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
const currentStats = await stat(current);
|
|
358
|
+
return currentStats.isDirectory();
|
|
359
|
+
} catch {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
338
364
|
async function collectWatchDirectories(root: string, ignoredPaths: string[]): Promise<string[]> {
|
|
339
365
|
const queue = [resolve(root)];
|
|
340
366
|
const directories: string[] = [];
|
|
341
367
|
|
|
342
368
|
while (queue.length > 0) {
|
|
343
369
|
const current = queue.shift()!;
|
|
344
|
-
if (
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
let currentStats;
|
|
349
|
-
try {
|
|
350
|
-
currentStats = await stat(current);
|
|
351
|
-
} catch {
|
|
352
|
-
continue;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (!currentStats.isDirectory()) {
|
|
370
|
+
if (!(await isWatchableDirectory(current, ignoredPaths))) {
|
|
356
371
|
continue;
|
|
357
372
|
}
|
|
358
373
|
|
|
359
374
|
directories.push(current);
|
|
360
|
-
|
|
361
|
-
let entries;
|
|
362
|
-
try {
|
|
363
|
-
entries = await readdir(current, { withFileTypes: true });
|
|
364
|
-
} catch {
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
for (const entry of entries) {
|
|
369
|
-
if (!entry.isDirectory()) {
|
|
370
|
-
continue;
|
|
371
|
-
}
|
|
372
|
-
queue.push(join(current, entry.name));
|
|
373
|
-
}
|
|
375
|
+
queue.push(...(await listChildDirectories(current)));
|
|
374
376
|
}
|
|
375
377
|
|
|
376
378
|
return normalizePaths(directories);
|
|
@@ -444,7 +446,51 @@ async function promoteManagedFiles(
|
|
|
444
446
|
}
|
|
445
447
|
}
|
|
446
448
|
|
|
447
|
-
|
|
449
|
+
interface WatchTargetPayload {
|
|
450
|
+
missing?: boolean;
|
|
451
|
+
origin?: string | null;
|
|
452
|
+
locations?: string[];
|
|
453
|
+
has_location?: boolean;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
interface WatchPythonEnv {
|
|
457
|
+
pythonPath: string;
|
|
458
|
+
env: { PYTHONPATH: string } | undefined;
|
|
459
|
+
timeoutMs: number;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const WATCH_TARGET_RESOLUTION_SCRIPT = [
|
|
463
|
+
'import importlib',
|
|
464
|
+
'import importlib.util',
|
|
465
|
+
'import json',
|
|
466
|
+
'import sys',
|
|
467
|
+
'',
|
|
468
|
+
'module_name = sys.argv[1]',
|
|
469
|
+
'spec = importlib.util.find_spec(module_name)',
|
|
470
|
+
'if spec is None:',
|
|
471
|
+
" print(json.dumps({'missing': True}))",
|
|
472
|
+
' raise SystemExit(0)',
|
|
473
|
+
'',
|
|
474
|
+
'module_path = None',
|
|
475
|
+
'module_origin = spec.origin',
|
|
476
|
+
'if spec.submodule_search_locations:',
|
|
477
|
+
' try:',
|
|
478
|
+
' module = importlib.import_module(module_name)',
|
|
479
|
+
' except Exception:',
|
|
480
|
+
' module = None',
|
|
481
|
+
' if module is not None:',
|
|
482
|
+
' module_path = getattr(module, "__path__", None)',
|
|
483
|
+
' module_origin = getattr(module, "__file__", None) or spec.origin',
|
|
484
|
+
'payload = {',
|
|
485
|
+
" 'missing': False,",
|
|
486
|
+
" 'origin': module_origin,",
|
|
487
|
+
" 'locations': list(module_path) if module_path is not None else list(spec.submodule_search_locations or []),",
|
|
488
|
+
" 'has_location': bool(spec.has_location),",
|
|
489
|
+
'}',
|
|
490
|
+
'print(json.dumps(payload))',
|
|
491
|
+
].join('\n');
|
|
492
|
+
|
|
493
|
+
async function resolveWatchPythonEnv(config: TywrapOptions): Promise<WatchPythonEnv> {
|
|
448
494
|
const pythonPath = await resolvePythonExecutable({
|
|
449
495
|
pythonPath: config.runtime?.node?.pythonPath,
|
|
450
496
|
virtualEnv: config.runtime?.node?.virtualEnv,
|
|
@@ -460,104 +506,100 @@ async function resolveWatchTargets(config: TywrapOptions): Promise<WatchTarget[]
|
|
|
460
506
|
const env = mergedPyPath ? { PYTHONPATH: mergedPyPath } : undefined;
|
|
461
507
|
const timeoutMs = config.runtime?.node?.timeout ?? 30000;
|
|
462
508
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
for (const moduleName of Object.keys(config.pythonModules ?? {})) {
|
|
466
|
-
const resolutionScript = [
|
|
467
|
-
'import importlib',
|
|
468
|
-
'import importlib.util',
|
|
469
|
-
'import json',
|
|
470
|
-
'import sys',
|
|
471
|
-
'',
|
|
472
|
-
'module_name = sys.argv[1]',
|
|
473
|
-
'spec = importlib.util.find_spec(module_name)',
|
|
474
|
-
'if spec is None:',
|
|
475
|
-
" print(json.dumps({'missing': True}))",
|
|
476
|
-
' raise SystemExit(0)',
|
|
477
|
-
'',
|
|
478
|
-
'module_path = None',
|
|
479
|
-
'module_origin = spec.origin',
|
|
480
|
-
'if spec.submodule_search_locations:',
|
|
481
|
-
' try:',
|
|
482
|
-
' module = importlib.import_module(module_name)',
|
|
483
|
-
' except Exception:',
|
|
484
|
-
' module = None',
|
|
485
|
-
' if module is not None:',
|
|
486
|
-
' module_path = getattr(module, "__path__", None)',
|
|
487
|
-
' module_origin = getattr(module, "__file__", None) or spec.origin',
|
|
488
|
-
'payload = {',
|
|
489
|
-
" 'missing': False,",
|
|
490
|
-
" 'origin': module_origin,",
|
|
491
|
-
" 'locations': list(module_path) if module_path is not None else list(spec.submodule_search_locations or []),",
|
|
492
|
-
" 'has_location': bool(spec.has_location),",
|
|
493
|
-
'}',
|
|
494
|
-
'print(json.dumps(payload))',
|
|
495
|
-
].join('\n');
|
|
496
|
-
|
|
497
|
-
const result = await processUtils.exec(pythonPath, ['-c', resolutionScript, moduleName], {
|
|
498
|
-
timeoutMs,
|
|
499
|
-
env,
|
|
500
|
-
});
|
|
509
|
+
return { pythonPath, env, timeoutMs };
|
|
510
|
+
}
|
|
501
511
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
512
|
+
async function resolveWatchTargetPayload(
|
|
513
|
+
moduleName: string,
|
|
514
|
+
pythonEnv: WatchPythonEnv
|
|
515
|
+
): Promise<WatchTargetPayload> {
|
|
516
|
+
const result = await processUtils.exec(
|
|
517
|
+
pythonEnv.pythonPath,
|
|
518
|
+
['-c', WATCH_TARGET_RESOLUTION_SCRIPT, moduleName],
|
|
519
|
+
{
|
|
520
|
+
timeoutMs: pythonEnv.timeoutMs,
|
|
521
|
+
env: pythonEnv.env,
|
|
506
522
|
}
|
|
523
|
+
);
|
|
507
524
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
525
|
+
if (result.code !== 0) {
|
|
526
|
+
throw new Error(
|
|
527
|
+
`Failed to resolve watch target for module "${moduleName}": ${result.stderr.trim() || result.stdout.trim() || 'unknown error'}`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
return JSON.parse(result.stdout) as WatchTargetPayload;
|
|
533
|
+
} catch (error) {
|
|
534
|
+
throw new Error(
|
|
535
|
+
`Failed to parse watch target for module "${moduleName}": ${(error as Error).message}`
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
async function resolvePackageWatchTargets(
|
|
541
|
+
moduleName: string,
|
|
542
|
+
packageLocations: string[]
|
|
543
|
+
): Promise<WatchTarget[]> {
|
|
544
|
+
const targets: WatchTarget[] = [];
|
|
545
|
+
for (const location of packageLocations) {
|
|
546
|
+
const packageDir = resolve(location);
|
|
547
|
+
const stats = await stat(packageDir);
|
|
548
|
+
if (!stats.isDirectory()) {
|
|
522
549
|
throw new Error(
|
|
523
|
-
`
|
|
550
|
+
`Module "${moduleName}" resolved to "${packageDir}", but it is not a package directory`
|
|
524
551
|
);
|
|
525
552
|
}
|
|
553
|
+
targets.push({ moduleName, path: packageDir, kind: 'directory' });
|
|
554
|
+
}
|
|
555
|
+
return targets;
|
|
556
|
+
}
|
|
526
557
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
558
|
+
async function resolveFileWatchTarget(
|
|
559
|
+
moduleName: string,
|
|
560
|
+
payload: WatchTargetPayload
|
|
561
|
+
): Promise<WatchTarget> {
|
|
562
|
+
if (typeof payload.origin !== 'string' || !payload.origin.endsWith('.py')) {
|
|
563
|
+
throw new Error(
|
|
564
|
+
`Module "${moduleName}" is not backed by a local Python source file or package directory and cannot be watched`
|
|
565
|
+
);
|
|
566
|
+
}
|
|
530
567
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
`Module "${moduleName}" resolved to "${packageDir}", but it is not a package directory`
|
|
539
|
-
);
|
|
540
|
-
}
|
|
541
|
-
watchTargets.push({ moduleName, path: packageDir, kind: 'directory' });
|
|
542
|
-
}
|
|
543
|
-
continue;
|
|
544
|
-
}
|
|
568
|
+
const modulePath = resolve(payload.origin);
|
|
569
|
+
const stats = await stat(modulePath);
|
|
570
|
+
if (!stats.isFile()) {
|
|
571
|
+
throw new Error(
|
|
572
|
+
`Module "${moduleName}" resolved to "${modulePath}", but it is not a Python source file`
|
|
573
|
+
);
|
|
574
|
+
}
|
|
545
575
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
`Module "${moduleName}" is not backed by a local Python source file or package directory and cannot be watched`
|
|
549
|
-
);
|
|
550
|
-
}
|
|
576
|
+
return { moduleName, path: modulePath, kind: 'file' };
|
|
577
|
+
}
|
|
551
578
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
579
|
+
async function resolveModuleWatchTargets(
|
|
580
|
+
moduleName: string,
|
|
581
|
+
pythonEnv: WatchPythonEnv
|
|
582
|
+
): Promise<WatchTarget[]> {
|
|
583
|
+
const payload = await resolveWatchTargetPayload(moduleName, pythonEnv);
|
|
584
|
+
|
|
585
|
+
if (payload.missing) {
|
|
586
|
+
throw new Error(`Module "${moduleName}" could not be resolved for watching`);
|
|
587
|
+
}
|
|
559
588
|
|
|
560
|
-
|
|
589
|
+
const packageLocations = Array.isArray(payload.locations) ? payload.locations : [];
|
|
590
|
+
if (packageLocations.length > 0) {
|
|
591
|
+
return resolvePackageWatchTargets(moduleName, packageLocations);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return [await resolveFileWatchTarget(moduleName, payload)];
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
async function resolveWatchTargets(config: TywrapOptions): Promise<WatchTarget[]> {
|
|
598
|
+
const pythonEnv = await resolveWatchPythonEnv(config);
|
|
599
|
+
|
|
600
|
+
const watchTargets: WatchTarget[] = [];
|
|
601
|
+
for (const moduleName of Object.keys(config.pythonModules ?? {})) {
|
|
602
|
+
watchTargets.push(...(await resolveModuleWatchTargets(moduleName, pythonEnv)));
|
|
561
603
|
}
|
|
562
604
|
|
|
563
605
|
return watchTargets;
|
|
@@ -786,6 +828,28 @@ export async function startNodeWatchSession<T extends RuntimeExecution & Disposa
|
|
|
786
828
|
return watcherRefreshPromise;
|
|
787
829
|
};
|
|
788
830
|
|
|
831
|
+
interface ReloadState {
|
|
832
|
+
stage: StageResult | null;
|
|
833
|
+
nextBridge: T | null;
|
|
834
|
+
preparedWatchers: PreparedWatchers | null;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
interface PreparedReload {
|
|
838
|
+
stage: StageResult;
|
|
839
|
+
preparedWatchers: PreparedWatchers;
|
|
840
|
+
nextIgnoredPaths: string[];
|
|
841
|
+
watchPaths: string[];
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const disposeReloadFailure = async (state: ReloadState): Promise<void> => {
|
|
845
|
+
if (state.preparedWatchers) {
|
|
846
|
+
closePreparedWatchers(state.preparedWatchers);
|
|
847
|
+
}
|
|
848
|
+
if (state.nextBridge) {
|
|
849
|
+
await state.nextBridge.dispose().catch(() => {});
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
|
|
789
853
|
const runReload = async (trigger: { path?: string; manual: boolean }): Promise<boolean> => {
|
|
790
854
|
if (closed) {
|
|
791
855
|
return false;
|
|
@@ -793,20 +857,18 @@ export async function startNodeWatchSession<T extends RuntimeExecution & Disposa
|
|
|
793
857
|
|
|
794
858
|
emit({ type: 'reload-start', path: trigger.path, manual: trigger.manual });
|
|
795
859
|
|
|
796
|
-
|
|
797
|
-
let nextBridge: T | null = null;
|
|
798
|
-
let preparedWatchers: PreparedWatchers | null = null;
|
|
860
|
+
const state: ReloadState = { stage: null, nextBridge: null, preparedWatchers: null };
|
|
799
861
|
|
|
800
862
|
const abortReload = async (
|
|
801
863
|
managerToDispose: ManagedBridgeReloader<T> | null = null
|
|
802
864
|
): Promise<boolean> => {
|
|
803
|
-
if (preparedWatchers) {
|
|
804
|
-
closePreparedWatchers(preparedWatchers);
|
|
805
|
-
preparedWatchers = null;
|
|
865
|
+
if (state.preparedWatchers) {
|
|
866
|
+
closePreparedWatchers(state.preparedWatchers);
|
|
867
|
+
state.preparedWatchers = null;
|
|
806
868
|
}
|
|
807
|
-
if (nextBridge) {
|
|
808
|
-
await nextBridge.dispose().catch(() => {});
|
|
809
|
-
nextBridge = null;
|
|
869
|
+
if (state.nextBridge) {
|
|
870
|
+
await state.nextBridge.dispose().catch(() => {});
|
|
871
|
+
state.nextBridge = null;
|
|
810
872
|
}
|
|
811
873
|
if (managerToDispose) {
|
|
812
874
|
await managerToDispose.dispose().catch(() => {});
|
|
@@ -814,10 +876,13 @@ export async function startNodeWatchSession<T extends RuntimeExecution & Disposa
|
|
|
814
876
|
return false;
|
|
815
877
|
};
|
|
816
878
|
|
|
817
|
-
|
|
818
|
-
|
|
879
|
+
// Generate the next stage, prepare watchers, and warm the next bridge.
|
|
880
|
+
// Returns null when the session closed mid-flight (caller aborts).
|
|
881
|
+
const prepareReload = async (): Promise<PreparedReload | null> => {
|
|
882
|
+
const stage = await generateToStage(configFile);
|
|
883
|
+
state.stage = stage;
|
|
819
884
|
if (closed) {
|
|
820
|
-
return
|
|
885
|
+
return null;
|
|
821
886
|
}
|
|
822
887
|
currentBridgeConfig = stage.config;
|
|
823
888
|
const nextIgnoredPaths = buildIgnoredPaths(stage.outputDir);
|
|
@@ -828,17 +893,50 @@ export async function startNodeWatchSession<T extends RuntimeExecution & Disposa
|
|
|
828
893
|
...extraWatchSources,
|
|
829
894
|
]);
|
|
830
895
|
const watchPaths = normalizePaths(watchSources.map(source => source.path));
|
|
831
|
-
preparedWatchers = await prepareWatchers(watchSources, nextIgnoredPaths);
|
|
896
|
+
const preparedWatchers = await prepareWatchers(watchSources, nextIgnoredPaths);
|
|
897
|
+
state.preparedWatchers = preparedWatchers;
|
|
832
898
|
if (closed) {
|
|
833
|
-
return
|
|
899
|
+
return null;
|
|
834
900
|
}
|
|
835
901
|
|
|
836
902
|
if (bridgeManager) {
|
|
837
|
-
nextBridge = await bridgeManager.prepareNextBridge();
|
|
903
|
+
state.nextBridge = await bridgeManager.prepareNextBridge();
|
|
904
|
+
if (closed) {
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
return { stage, preparedWatchers, nextIgnoredPaths, watchPaths };
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
// Activate (or create) the bridge for the freshly prepared stage.
|
|
913
|
+
// Returns the manager to dispose (or null) when the session closed
|
|
914
|
+
// mid-flight, otherwise null; sets `closedDuringActivation` accordingly.
|
|
915
|
+
let closedDuringActivation = false;
|
|
916
|
+
const activateReloadBridge = async (): Promise<ManagedBridgeReloader<T> | null> => {
|
|
917
|
+
if (bridgeManager && state.nextBridge) {
|
|
918
|
+
await bridgeManager.activatePreparedBridge(state.nextBridge);
|
|
919
|
+
state.nextBridge = null;
|
|
920
|
+
closedDuringActivation = closed;
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
if (!bridgeManager) {
|
|
924
|
+
const createdBridgeManager = await ManagedBridgeReloader.create(createBridgeForSession);
|
|
838
925
|
if (closed) {
|
|
839
|
-
|
|
926
|
+
closedDuringActivation = true;
|
|
927
|
+
return createdBridgeManager;
|
|
840
928
|
}
|
|
929
|
+
bridgeManager = createdBridgeManager;
|
|
930
|
+
}
|
|
931
|
+
return null;
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
try {
|
|
935
|
+
const prepared = await prepareReload();
|
|
936
|
+
if (!prepared) {
|
|
937
|
+
return abortReload();
|
|
841
938
|
}
|
|
939
|
+
const { stage } = prepared;
|
|
842
940
|
|
|
843
941
|
const previousManagedFiles =
|
|
844
942
|
activeOutputDir === stage.outputDir
|
|
@@ -853,29 +951,20 @@ export async function startNodeWatchSession<T extends RuntimeExecution & Disposa
|
|
|
853
951
|
return abortReload();
|
|
854
952
|
}
|
|
855
953
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
if (closed) {
|
|
860
|
-
return abortReload();
|
|
861
|
-
}
|
|
862
|
-
} else if (!bridgeManager) {
|
|
863
|
-
const createdBridgeManager = await ManagedBridgeReloader.create(createBridgeForSession);
|
|
864
|
-
if (closed) {
|
|
865
|
-
return abortReload(createdBridgeManager);
|
|
866
|
-
}
|
|
867
|
-
bridgeManager = createdBridgeManager;
|
|
954
|
+
const managerToDispose = await activateReloadBridge();
|
|
955
|
+
if (closedDuringActivation) {
|
|
956
|
+
return abortReload(managerToDispose);
|
|
868
957
|
}
|
|
869
958
|
|
|
870
959
|
managedFiles = promotedManagedFiles;
|
|
871
960
|
activeOutputDir = stage.outputDir;
|
|
872
|
-
ignoredPaths = nextIgnoredPaths;
|
|
873
|
-
commitWatchers(preparedWatchers);
|
|
961
|
+
ignoredPaths = prepared.nextIgnoredPaths;
|
|
962
|
+
commitWatchers(prepared.preparedWatchers);
|
|
874
963
|
emit({
|
|
875
964
|
type: 'reload-success',
|
|
876
965
|
path: trigger.path,
|
|
877
966
|
manual: trigger.manual,
|
|
878
|
-
paths: watchPaths,
|
|
967
|
+
paths: prepared.watchPaths,
|
|
879
968
|
written: [...stage.files.keys()].sort(),
|
|
880
969
|
warnings: stage.warnings,
|
|
881
970
|
});
|
|
@@ -883,12 +972,7 @@ export async function startNodeWatchSession<T extends RuntimeExecution & Disposa
|
|
|
883
972
|
return true;
|
|
884
973
|
} catch (error) {
|
|
885
974
|
lastReloadError = error instanceof Error ? error : new Error(String(error));
|
|
886
|
-
|
|
887
|
-
closePreparedWatchers(preparedWatchers);
|
|
888
|
-
}
|
|
889
|
-
if (nextBridge) {
|
|
890
|
-
await nextBridge.dispose().catch(() => {});
|
|
891
|
-
}
|
|
975
|
+
await disposeReloadFailure(state);
|
|
892
976
|
emit({
|
|
893
977
|
type: 'reload-error',
|
|
894
978
|
path: trigger.path,
|
|
@@ -897,8 +981,8 @@ export async function startNodeWatchSession<T extends RuntimeExecution & Disposa
|
|
|
897
981
|
});
|
|
898
982
|
return false;
|
|
899
983
|
} finally {
|
|
900
|
-
if (stage) {
|
|
901
|
-
await rm(stage.tempDir, { recursive: true, force: true });
|
|
984
|
+
if (state.stage) {
|
|
985
|
+
await rm(state.stage.tempDir, { recursive: true, force: true });
|
|
902
986
|
}
|
|
903
987
|
}
|
|
904
988
|
};
|
package/src/index.ts
CHANGED
|
@@ -3,70 +3,20 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @description Build-time code generation system that makes Python libraries
|
|
5
5
|
* feel native in TypeScript with zero runtime overhead
|
|
6
|
+
*
|
|
7
|
+
* This is the package root. It intentionally exposes only the stable,
|
|
8
|
+
* consumer-facing surface. Runtime plumbing (codec, transport, bridge
|
|
9
|
+
* implementations, disposables, validators) lives behind the subpath
|
|
10
|
+
* entrypoints: `tywrap/runtime`, `tywrap/node`, `tywrap/pyodide`, `tywrap/http`.
|
|
6
11
|
*/
|
|
7
12
|
|
|
8
13
|
import { tywrap } from './tywrap.js';
|
|
9
14
|
|
|
15
|
+
// Configuration
|
|
10
16
|
export type { TywrapConfig, ResolvedTywrapConfig } from './config/index.js';
|
|
11
17
|
export { defineConfig, resolveConfig } from './config/index.js';
|
|
12
|
-
// DisposableBase - lifecycle/resource base for cross-boundary components
|
|
13
|
-
export {
|
|
14
|
-
DisposableBase,
|
|
15
|
-
type ContextState,
|
|
16
|
-
type ExecuteOptions,
|
|
17
|
-
} from './runtime/bounded-context.js';
|
|
18
|
-
// RpcClient - the single correlated-RPC client (codec + transport), held by bridges
|
|
19
|
-
export { RpcClient, type RpcClientOptions } from './runtime/rpc-client.js';
|
|
20
|
-
// SafeCodec - validation and serialization for JS<->Python boundary
|
|
21
|
-
export { SafeCodec, type CodecOptions } from './runtime/safe-codec.js';
|
|
22
|
-
// Transport - abstract I/O channel interface
|
|
23
|
-
export type {
|
|
24
|
-
Transport,
|
|
25
|
-
TransportOptions,
|
|
26
|
-
ProtocolMessage,
|
|
27
|
-
ProtocolResponse,
|
|
28
|
-
} from './runtime/transport.js';
|
|
29
|
-
export {
|
|
30
|
-
PROTOCOL_ID,
|
|
31
|
-
isTransport,
|
|
32
|
-
isProtocolMessage,
|
|
33
|
-
isProtocolResponse,
|
|
34
|
-
} from './runtime/transport.js';
|
|
35
|
-
// Transport implementations
|
|
36
|
-
export { ProcessIO, type ProcessIOOptions } from './runtime/process-io.js';
|
|
37
|
-
export { HttpIO, type HttpIOOptions } from './runtime/http-io.js';
|
|
38
|
-
export { PyodideIO, type PyodideIOOptions } from './runtime/pyodide-io.js';
|
|
39
|
-
// WorkerPool - concurrent transport management
|
|
40
|
-
export { WorkerPool, type WorkerPoolOptions, type PooledWorker } from './runtime/worker-pool.js';
|
|
41
|
-
// PooledTransport - Transport adapter that wraps WorkerPool
|
|
42
|
-
export { PooledTransport, type PooledTransportOptions } from './runtime/pooled-transport.js';
|
|
43
|
-
export type { Disposable } from './runtime/disposable.js';
|
|
44
|
-
export { isDisposable, safeDispose, disposeAll } from './runtime/disposable.js';
|
|
45
|
-
export {
|
|
46
|
-
ValidationError,
|
|
47
|
-
isFiniteNumber,
|
|
48
|
-
isPositiveNumber,
|
|
49
|
-
isNonNegativeNumber,
|
|
50
|
-
isNonEmptyString,
|
|
51
|
-
isPlainObject,
|
|
52
|
-
assertFiniteNumber,
|
|
53
|
-
assertPositive,
|
|
54
|
-
assertNonNegative,
|
|
55
|
-
assertString,
|
|
56
|
-
assertNonEmptyString,
|
|
57
|
-
assertArray,
|
|
58
|
-
assertObject,
|
|
59
|
-
containsSpecialFloat,
|
|
60
|
-
assertNoSpecialFloats,
|
|
61
|
-
sanitizeForFilename,
|
|
62
|
-
containsPathTraversal,
|
|
63
|
-
} from './runtime/validators.js';
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @deprecated Use DisposableBase instead. RuntimeBridge will be removed in the next major version.
|
|
67
|
-
*/
|
|
68
|
-
export { RuntimeBridge } from './runtime/base.js';
|
|
69
18
|
|
|
19
|
+
// Bridge error hierarchy
|
|
70
20
|
export {
|
|
71
21
|
BridgeError,
|
|
72
22
|
BridgeCodecError,
|
|
@@ -75,12 +25,6 @@ export {
|
|
|
75
25
|
BridgeDisposedError,
|
|
76
26
|
BridgeExecutionError,
|
|
77
27
|
} from './runtime/errors.js';
|
|
78
|
-
export { getRuntimeBridge, setRuntimeBridge, clearRuntimeBridge } from './runtime/index.js';
|
|
79
|
-
|
|
80
|
-
// Runtime-specific exports (Bridge facades that HOLD an RpcClient)
|
|
81
|
-
export { NodeBridge, type NodeBridgeOptions } from './runtime/node.js';
|
|
82
|
-
export { PyodideBridge, type PyodideBridgeOptions } from './runtime/pyodide.js';
|
|
83
|
-
export { HttpBridge, type HttpBridgeOptions } from './runtime/http.js';
|
|
84
28
|
|
|
85
29
|
// Core types
|
|
86
30
|
export type {
|
|
@@ -138,6 +82,8 @@ export type { GenerateFailure, GenerateResult, GenerateRunOptions } from './tywr
|
|
|
138
82
|
|
|
139
83
|
// Runtime detection utilities
|
|
140
84
|
export { detectRuntime, isNodejs, isDeno, isBun, isBrowser } from './utils/runtime.js';
|
|
85
|
+
|
|
86
|
+
// Arrow/codec public helpers
|
|
141
87
|
export {
|
|
142
88
|
decodeValue,
|
|
143
89
|
decodeValueAsync,
|
|
@@ -146,11 +92,8 @@ export {
|
|
|
146
92
|
clearArrowDecoder,
|
|
147
93
|
} from './utils/codec.js';
|
|
148
94
|
|
|
149
|
-
// Version info
|
|
150
|
-
export
|
|
95
|
+
// Version info — single-sourced from package.json via scripts/generate-version.mjs
|
|
96
|
+
export { VERSION } from './version.js';
|
|
151
97
|
|
|
152
|
-
/**
|
|
153
|
-
* Quick setup function for getting started
|
|
154
|
-
*/
|
|
155
98
|
// Default export for convenience
|
|
156
99
|
export default tywrap;
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* ownership tracking. It carries ZERO RPC methods by design — the
|
|
7
7
|
* cross-boundary RPC contract (call/instantiate/callMethod/disposeInstance)
|
|
8
8
|
* is PythonRuntime, implemented only by the bridge facades, never by a base
|
|
9
|
-
* class or a transport. This separation is what keeps transports (
|
|
10
|
-
* PooledTransport,
|
|
9
|
+
* class or a transport. This separation is what keeps transports (SubprocessTransport,
|
|
10
|
+
* PooledTransport, TransportPool, PyodideTransport) and RpcClient from having to stub
|
|
11
11
|
* RPC methods they do not implement.
|
|
12
12
|
*
|
|
13
13
|
* Reused by RpcClient and all transports.
|