@dbx-tools/projen 0.9.40 → 0.9.46
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 +58 -67
- package/index.ts +6 -8
- package/package.json +9 -10
- package/shims/python-node/bootstrap.ts +129 -0
- package/shims/python-node/child-process.ts +96 -0
- package/shims/python-node/crypto.ts +60 -0
- package/shims/python-node/fs-promises.ts +179 -0
- package/shims/python-node/fs.ts +173 -0
- package/shims/python-node/headers.ts +75 -0
- package/shims/python-node/host.ts +341 -0
- package/shims/python-node/os.ts +20 -0
- package/shims/python-node/process.ts +13 -0
- package/shims/python-node/readline.ts +37 -0
- package/shims/python-node/tsconfig.json +24 -0
- package/src/_lazy-require.ts +7 -25
- package/src/bun-app.ts +2 -2
- package/src/bun-workflow.ts +2 -170
- package/src/codegen.ts +23 -20
- package/src/dbx-tools-config.ts +52 -9
- package/src/generated.ts +4 -17
- package/src/mixin.ts +1 -5
- package/src/packages.ts +18 -15
- package/src/pnpm-workspace.ts +1 -4
- package/src/project-js.ts +65 -196
- package/src/project-predicate.ts +7 -3
- package/src/project-py.ts +127 -37
- package/src/publish.ts +13 -10
- package/src/python-node-bindings.ts +2 -0
- package/src/python-node-bundle.ts +6 -0
- package/src/release-options.ts +17 -17
- package/src/release.ts +31 -14
- package/src/scaffold.ts +4 -7
- package/src/tags.ts +6 -16
- package/src/vscode.ts +1 -1
- package/src/watch.ts +5 -5
- package/src/workspace-lock.ts +1 -0
- package/src/workspace-version.ts +4 -4
- package/tasks/barrels.ts +3 -5
- package/tasks/clean.ts +1 -0
- package/tasks/lib/compile-workspace.ts +41 -19
- package/tasks/lib/local-publish.ts +0 -28
- package/tasks/lib/publish-npm.ts +8 -0
- package/tasks/lib/publish-python.ts +4 -0
- package/tasks/lib/python-node-bindings.ts +107 -26
- package/tasks/lib/python-registry.ts +4 -0
- package/tasks/lib/python-release.ts +2 -0
- package/tasks/lib/release.ts +2 -2
- package/tasks/projenrc.ts +1 -0
- package/tasks/sync.ts +4 -4
- package/src/openapi.ts +0 -293
- package/tasks/local-publish.ts +0 -6
- package/tasks/openapi.ts +0 -35
package/README.md
CHANGED
|
@@ -4,21 +4,20 @@ Projen engine for Bun-first dbx-tools workspaces.
|
|
|
4
4
|
|
|
5
5
|
Import this package from `.projenrc.ts` when a repository should discover
|
|
6
6
|
packages from the filesystem and generate manifests, tsconfigs,
|
|
7
|
-
barrels,
|
|
7
|
+
barrels, codegen outputs, and release tasks.
|
|
8
8
|
|
|
9
9
|
Key features:
|
|
10
10
|
|
|
11
11
|
- Filesystem package discovery: every `src`-bearing folder under configured
|
|
12
12
|
workspace roots becomes a TypeScript package.
|
|
13
13
|
- Tag-driven runtime defaults for shared libraries, Node packages, CLIs,
|
|
14
|
-
servers,
|
|
14
|
+
servers, and React/browser UI packages.
|
|
15
15
|
- Generated package manifests, tsconfigs, package-root barrels, Bun app configs,
|
|
16
16
|
VS Code settings, and a committed `pnpm-workspace.yaml` retained for
|
|
17
17
|
Databricks Apps deployment.
|
|
18
18
|
- Extensible mixin system so repositories can add deps, tasks, or generated
|
|
19
19
|
files based on package predicates.
|
|
20
|
-
-
|
|
21
|
-
`.d.ts` inputs.
|
|
20
|
+
- Zod schema generation from `.d.ts` inputs.
|
|
22
21
|
- Read-only generated-file ownership, cleanup, and watch-loop helpers.
|
|
23
22
|
|
|
24
23
|
## Define A Workspace Root
|
|
@@ -58,18 +57,16 @@ workflow.
|
|
|
58
57
|
Repository policy stays in the consuming `.projenrc.ts`:
|
|
59
58
|
|
|
60
59
|
- `releaseDocs` supplies repository-defined preparation and build steps plus
|
|
61
|
-
the Pages artifact path. The engine adds release checkout,
|
|
62
|
-
|
|
60
|
+
the Pages artifact path. The engine adds release checkout, artifact upload,
|
|
61
|
+
and deployment without naming a docs script or output tree.
|
|
63
62
|
- `releasePythonRoot` passes the actual Python package root to local release
|
|
64
63
|
preparation. Omit it when the workspace has no standard Python packages.
|
|
65
64
|
- `releaseValidationTasks` names repository tasks that must pass in tag CI
|
|
66
65
|
before release artifacts are built and published.
|
|
67
66
|
- `releaseSetupSteps` installs any extra validation/build prerequisites in the
|
|
68
67
|
shared release job before the validation tasks run.
|
|
69
|
-
- `
|
|
70
|
-
|
|
71
|
-
- `workflowCacheIgnorePaths` excludes generated output trees that may contain
|
|
72
|
-
package manifests from the dependency-only Bun cache key.
|
|
68
|
+
- `releaseSynthesisCommands` runs repository-specific synth owners before the
|
|
69
|
+
root synthesis and clean-diff release check.
|
|
73
70
|
- `extraWorkspaceMembers` declares self-synthesizing tooling packages outside
|
|
74
71
|
`packageRoots`. Their version, generated entrypoint, formatting, linting, and
|
|
75
72
|
workspace membership are derived from that declaration.
|
|
@@ -228,7 +225,7 @@ parameter to `Project`). Drop to `mixin.create(predicate, fn)` +
|
|
|
228
225
|
`project.with(...)` only when you need a predicate the filters cannot express.
|
|
229
226
|
|
|
230
227
|
Built-in tag mixins set runtime defaults for `shared`, `node`, `cli`, `server`,
|
|
231
|
-
|
|
228
|
+
and `ui`. Repo-specific mixins layer package-specific dependencies,
|
|
232
229
|
scripts, and generated files on top.
|
|
233
230
|
|
|
234
231
|
AppKit 0.81's `appkitServerConfig()` is the preferred tsdown preset for a
|
|
@@ -272,12 +269,19 @@ codegen.generateCodegen();
|
|
|
272
269
|
barrels.generateBarrels();
|
|
273
270
|
```
|
|
274
271
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
272
|
+
Add declaration inputs through the owning package's typed config:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
pkg.dbxToolsConfig.codegenInputs.push("node_modules/example/model.d.ts");
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
`generateCodegen()` reads the serialized `dbxToolsConfig.codegenInputs` and
|
|
279
|
+
writes generated schema modules. They are written read-only, and the root
|
|
280
|
+
ESLint task runs with an explicit separate fix task, so each generated module is
|
|
281
|
+
added to `ignorePatterns` at synth - named individually via
|
|
282
|
+
`codegen.codegenModulePaths()`, never as a blanket `<package>/src/**`. A codegen
|
|
283
|
+
package may hold hand-written modules beside its generated ones, and those must
|
|
284
|
+
stay linted.
|
|
281
285
|
|
|
282
286
|
`generateBarrels()` writes package-root `index.ts` barrels with module
|
|
283
287
|
namespaces, flat unique type exports, and `PACKAGE_IDENTIFIER`, returning the
|
|
@@ -290,17 +294,6 @@ package is attempted even if one fails; the failures are re-thrown together as a
|
|
|
290
294
|
`AggregateError` naming each package, rather than the first one abandoning the
|
|
291
295
|
rest of the sweep.
|
|
292
296
|
|
|
293
|
-
## Generate OpenAPI Clients
|
|
294
|
-
|
|
295
|
-
```ts
|
|
296
|
-
import { openapi } from "@dbx-tools/projen";
|
|
297
|
-
|
|
298
|
-
const packages = await openapi.generateOpenapi();
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
OpenAPI generation scans packages for tsoa controllers, emits `openapi.json`,
|
|
302
|
-
generates TypeScript schemas, and adds an `openapi-fetch` client.
|
|
303
|
-
|
|
304
297
|
## Configure pnpm Catalogs
|
|
305
298
|
|
|
306
299
|
```ts
|
|
@@ -351,7 +344,6 @@ file contract as the CLI.
|
|
|
351
344
|
- `pnpmWorkspace` - generated pnpm workspace file and catalog model.
|
|
352
345
|
- `barrels` / `moduleExports` - public entrypoint generation.
|
|
353
346
|
- `codegen` - `.d.ts` to zod schema generation.
|
|
354
|
-
- `openapi` - tsoa/OpenAPI package generation.
|
|
355
347
|
- `bunApp` / `tsconfig` / `vscode` - generated support files/components.
|
|
356
348
|
- `generated` / `clean` / `watch` / `scaffold` - read-only file ownership,
|
|
357
349
|
cleanup, watchers, and synth orchestration.
|
|
@@ -359,17 +351,17 @@ file contract as the CLI.
|
|
|
359
351
|
- `engineRoot` - engine package root resolution for bootstrapped repos.
|
|
360
352
|
|
|
361
353
|
The engine registers its commands as projen tasks on the workspace root, so run
|
|
362
|
-
them with `bun run <task>` - `sync` (add `--watch`), `barrels`, `
|
|
363
|
-
`clean`.
|
|
354
|
+
them with `bun run <task>` - `sync` (add `--watch`), `barrels`, and `clean`.
|
|
364
355
|
[`@dbx-tools/cli`](../packages/js/cli/dbx-tools) is only needed to
|
|
365
356
|
bootstrap a folder that has no `.projenrc.ts` or toolchain yet.
|
|
366
357
|
|
|
367
358
|
## Run Tasks From The ROOT
|
|
368
359
|
|
|
369
|
-
Every repo-wide task lives on the root. `compile` batches
|
|
370
|
-
|
|
371
|
-
`test` delegates with `bun run --filter '*'`. Both read the
|
|
372
|
-
list, so a new package is covered without a re-synth. Work
|
|
360
|
+
Every repo-wide task lives on the root. `compile` batches package tsconfigs in
|
|
361
|
+
up to four `tsc --build` processes and runs package-owned compile lifecycles
|
|
362
|
+
alongside them; `test` delegates with `bun run --filter '*'`. Both read the
|
|
363
|
+
current workspace list, so a new package is covered without a re-synth. Work
|
|
364
|
+
from the root:
|
|
373
365
|
|
|
374
366
|
| Task | What it does |
|
|
375
367
|
| ----------------------- | --------------------------------------------------- |
|
|
@@ -389,10 +381,10 @@ diverged. From `main`, it calls the existing `bump` task, commits the generated
|
|
|
389
381
|
version changes, pushes `main`, and pushes the matching annotated `vX.Y.Z` tag.
|
|
390
382
|
Pass `--no-bump` only to release an existing synchronized local bump. The tag
|
|
391
383
|
workflow verifies that the tag points at the exact `origin/main` commit. One
|
|
392
|
-
`build-release` job
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
`
|
|
384
|
+
`build-release` job installs workspace dependencies, runs validation, packs the
|
|
385
|
+
npm archives, builds all Python distributions, and builds the documentation
|
|
386
|
+
site. Install validation prerequisites through `releaseSetupSteps`;
|
|
387
|
+
`releaseDocs.prepareSteps` runs after validation.
|
|
396
388
|
|
|
397
389
|
Publication jobs download the resulting artifacts rather than rebuilding the
|
|
398
390
|
workspace. Each Python package retains its own publishing environment and waits
|
|
@@ -413,7 +405,7 @@ bun run release --local-registry false --local-pypi auto
|
|
|
413
405
|
```
|
|
414
406
|
|
|
415
407
|
`--install auto` keeps Projen's normal local dependency-install behavior;
|
|
416
|
-
`always` performs a
|
|
408
|
+
`always` performs a manifest-resolved install first, and `never` uses dependencies already
|
|
417
409
|
installed while still regenerating versioned sources. `--no-validation` skips
|
|
418
410
|
optional task checks locally and in CI, not version or immutable-source checks.
|
|
419
411
|
`--no-docs` skips building and deploying the site, not package README validation.
|
|
@@ -426,8 +418,8 @@ invokes, so there is no second place to run the same thing:
|
|
|
426
418
|
|
|
427
419
|
- `compile` / `test` - what the root's `--filter '*'` delegation calls.
|
|
428
420
|
- `prepack` - standalone-publish safety that compiles one package before packing
|
|
429
|
-
|
|
430
|
-
and publishes with lifecycle scripts disabled
|
|
421
|
+
when that package has a compiled publication surface; the workspace release
|
|
422
|
+
driver compiles from the root and publishes with lifecycle scripts disabled.
|
|
431
423
|
- `watch` - a single-package `tsc --build -w`, for narrowing a long
|
|
432
424
|
edit/compile loop to one package.
|
|
433
425
|
- `build` / `package` - a complete compile/test/pack lifecycle when invoked in
|
|
@@ -458,33 +450,32 @@ workflow order without introducing another version owner.
|
|
|
458
450
|
|
|
459
451
|
### `release`
|
|
460
452
|
|
|
461
|
-
```text
|
|
462
|
-
Usage: release [options]
|
|
463
|
-
|
|
464
453
|
Prepare an annotated release and select its build and publication steps
|
|
465
454
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
--branch <name> release branch (default: "main")
|
|
469
|
-
--prefix <prefix> release tag prefix (default: "v")
|
|
470
|
-
--remote <name> git remote (default: "origin")
|
|
471
|
-
--python-root <path> Python package root for local publish (default: "packages/py")
|
|
472
|
-
--validate <task> task to run before pushing (default: [])
|
|
473
|
-
--no-bump use an existing synchronized local version bump
|
|
474
|
-
--publish <target> publication scope (choices: "auto", "npm", "pypi", "local",
|
|
475
|
-
"none", default: "auto")
|
|
476
|
-
--install <mode> local workspace dependency installation (choices: "auto",
|
|
477
|
-
"always", "never", default: "auto")
|
|
478
|
-
--no-npm skip npm build and publication, including local npm publication
|
|
479
|
-
--no-pypi skip Python build and publication, including local Python
|
|
480
|
-
publication
|
|
481
|
-
--docs build and deploy docs for a selected scope
|
|
482
|
-
--no-docs skip documentation build and deployment
|
|
483
|
-
--no-validation skip optional release validation tasks; version/source checks
|
|
484
|
-
remain mandatory
|
|
485
|
-
--no-local-publish skip publishing to configured local registries
|
|
486
|
-
--local-registry <auto|false|url> local npm registry selection (default: "auto")
|
|
487
|
-
--local-pypi <auto|false|url> local devpi registry selection (default: "auto")
|
|
455
|
+
```sh
|
|
456
|
+
release [options]
|
|
488
457
|
```
|
|
489
458
|
|
|
459
|
+
#### Options
|
|
460
|
+
|
|
461
|
+
| Option | Description |
|
|
462
|
+
| ------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
463
|
+
| `--root <path>` | repository root |
|
|
464
|
+
| `--branch <name>` | release branch (default: "main") |
|
|
465
|
+
| `--prefix <prefix>` | release tag prefix (default: "v") |
|
|
466
|
+
| `--remote <name>` | git remote (default: "origin") |
|
|
467
|
+
| `--python-root <path>` | Python package root for local publish (default: "packages/py") |
|
|
468
|
+
| `--validate <task>` | task to run before pushing (default: []) |
|
|
469
|
+
| `--no-bump` | use an existing synchronized local version bump |
|
|
470
|
+
| `--publish <target>` | publication scope (choices: "auto", "npm", "pypi", "local", "none", default: "auto") |
|
|
471
|
+
| `--install <mode>` | local workspace dependency installation (choices: "auto", "always", "never", default: "auto") |
|
|
472
|
+
| `--no-npm` | skip npm build and publication, including local npm publication |
|
|
473
|
+
| `--no-pypi` | skip Python build and publication, including local Python publication |
|
|
474
|
+
| `--docs` | build and deploy docs for a selected scope |
|
|
475
|
+
| `--no-docs` | skip documentation build and deployment |
|
|
476
|
+
| `--no-validation` | skip optional release validation tasks; version/source checks remain mandatory |
|
|
477
|
+
| `--no-local-publish` | skip publishing to configured local registries |
|
|
478
|
+
| `--local-registry <auto\|false\|url>` | local npm registry selection (default: "auto") |
|
|
479
|
+
| `--local-pypi <auto\|false\|url>` | local devpi registry selection (default: "auto") |
|
|
480
|
+
|
|
490
481
|
<!-- cli-reference:end -->
|
package/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
4
|
|
|
5
5
|
export const PACKAGE_IDENTIFIER = "@dbx-tools/projen";
|
|
6
|
-
export const PACKAGE_VERSION = "0.9.
|
|
6
|
+
export const PACKAGE_VERSION = "0.9.46";
|
|
7
7
|
export * as barrels from "./src/barrels.ts";
|
|
8
8
|
export * as bunApp from "./src/bun-app.ts";
|
|
9
9
|
export * as bunWorkflow from "./src/bun-workflow.ts";
|
|
@@ -14,7 +14,6 @@ export * as engineRoot from "./src/engine-root.ts";
|
|
|
14
14
|
export * as generated from "./src/generated.ts";
|
|
15
15
|
export * as mixin from "./src/mixin.ts";
|
|
16
16
|
export * as moduleExports from "./src/module-exports.ts";
|
|
17
|
-
export * as openapi from "./src/openapi.ts";
|
|
18
17
|
export * as packages from "./src/packages.ts";
|
|
19
18
|
export * as pnpmWorkspace from "./src/pnpm-workspace.ts";
|
|
20
19
|
export * as project from "./src/project.ts";
|
|
@@ -37,9 +36,8 @@ export * as workspaceLock from "./src/workspace-lock.ts";
|
|
|
37
36
|
export * as workspaceVersion from "./src/workspace-version.ts";
|
|
38
37
|
export { BUN_DEV_OVERRIDE, BUN_BUILD_OVERRIDE, BUN_APP_OVERRIDES, RootBunfigFile, BunfigFile, BunDevServerFile, BunBuildFile } from "./src/bun-app.ts";
|
|
39
38
|
export { BUN_VERSION } from "./src/bun-workflow.ts";
|
|
40
|
-
export
|
|
41
|
-
export {
|
|
42
|
-
export type { DBXToolsConfigOptions } from "./src/dbx-tools-config.ts";
|
|
39
|
+
export { DBXToolsConfigDataSchema, DBXToolsConfig } from "./src/dbx-tools-config.ts";
|
|
40
|
+
export type { DBXToolsConfigOptions, DBXToolsConfigData } from "./src/dbx-tools-config.ts";
|
|
43
41
|
export { resolvePkgRoot } from "./src/engine-root.ts";
|
|
44
42
|
export type { HeaderOpts } from "./src/generated.ts";
|
|
45
43
|
export type { ConstructsMixin } from "./src/mixin.ts";
|
|
@@ -49,8 +47,8 @@ export type { RecordedPackage } from "./src/packages.ts";
|
|
|
49
47
|
export { PnpmWorkspaceState } from "./src/pnpm-workspace.ts";
|
|
50
48
|
export type { Catalog, AllowBuilds, DBXToolsPNPMWorkspaceOptions } from "./src/pnpm-workspace.ts";
|
|
51
49
|
export type { DBXToolsProjectLanguage, DBXToolsProjectOptions, DBXToolsProject, ApplyToProjectsOptions } from "./src/project.ts";
|
|
52
|
-
export { PackageIdentifier,
|
|
53
|
-
export type { DBXToolsJavaScriptProject, DBXToolsReleaseMode, DBXToolsJavaScriptProjectOptions, DBXToolsTypeScriptProjectOptions } from "./src/project-js.ts";
|
|
50
|
+
export { PackageIdentifier, LICENSE, DBXToolsNodeProject, ROOT_INSTALL_ONLY_MIXIN, DBXToolsTypeScriptProject } from "./src/project-js.ts";
|
|
51
|
+
export type { DBXToolsJavaScriptProject, DBXToolsReleaseMode, DiscoveredTypeScriptPackageOptions, DBXToolsJavaScriptProjectOptions, DBXToolsTypeScriptProjectOptions } from "./src/project-js.ts";
|
|
54
52
|
export { DBXToolsPythonProject, DBXToolsPythonWorkspace } from "./src/project-py.ts";
|
|
55
53
|
export type { PythonRepositoryOptions, PythonPackageOptions, DBXToolsPythonProjectOptions, PythonReleaseOptions, DBXToolsPythonWorkspaceOptions } from "./src/project-py.ts";
|
|
56
54
|
export { PROJEN_VERSION } from "./src/projen-version.ts";
|
|
@@ -62,7 +60,7 @@ export type { PythonNodeFunctionOverride, PythonNodeBindingsOptions, PythonNodeB
|
|
|
62
60
|
export { DBXToolsRelease } from "./src/release.ts";
|
|
63
61
|
export type { ReleaseDocsOptions, DBXToolsReleaseOptions } from "./src/release.ts";
|
|
64
62
|
export { RELEASE_VERSION } from "./src/release-context.ts";
|
|
65
|
-
export { RELEASE_PUBLISH_TARGETS, RELEASE_INSTALL_MODES } from "./src/release-options.ts";
|
|
63
|
+
export { RELEASE_PUBLISH_TARGETS, RELEASE_INSTALL_MODES, ReleaseStepSelectionSchema } from "./src/release-options.ts";
|
|
66
64
|
export type { ReleasePublishTarget, ReleaseInstallMode, ReleaseStepSelection, ReleaseSelectionOptions } from "./src/release-options.ts";
|
|
67
65
|
export { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS } from "./src/tags.ts";
|
|
68
66
|
export type { PackageTag } from "./src/tags.ts";
|
package/package.json
CHANGED
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
"post-upgrade": "projen post-upgrade",
|
|
17
17
|
"pre-compile": "projen pre-compile",
|
|
18
18
|
"test": "projen test",
|
|
19
|
-
"test:external-consumer": "projen test:external-consumer",
|
|
20
19
|
"upgrade": "projen upgrade",
|
|
21
20
|
"watch": "projen watch",
|
|
22
21
|
"projen": "projen"
|
|
@@ -24,29 +23,28 @@
|
|
|
24
23
|
"devDependencies": {
|
|
25
24
|
"@types/node": "^24.6.0",
|
|
26
25
|
"@types/semver": "^7.7.1",
|
|
27
|
-
"projen": "0.103.27"
|
|
28
|
-
"tsoa": "^6.6.0"
|
|
26
|
+
"projen": "0.103.27"
|
|
29
27
|
},
|
|
30
28
|
"peerDependencies": {
|
|
31
29
|
"projen": "^0.103.27"
|
|
32
30
|
},
|
|
33
31
|
"dependencies": {
|
|
34
32
|
"@clack/prompts": "^1.7.0",
|
|
35
|
-
"@dbx-tools/core": "^0.9.
|
|
36
|
-
"@dbx-tools/path": "^0.9.
|
|
37
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
33
|
+
"@dbx-tools/core": "^0.9.46",
|
|
34
|
+
"@dbx-tools/path": "^0.9.46",
|
|
35
|
+
"@dbx-tools/shared-core": "^0.9.46",
|
|
38
36
|
"commander": "^15.0.0",
|
|
39
37
|
"concurrently": "^10.0.3",
|
|
40
38
|
"constructs": "^10.6.0",
|
|
41
39
|
"is-identifier": "^1",
|
|
42
40
|
"node-stdlib-browser": "^1.3.1",
|
|
43
|
-
"openapi-typescript": "^7.13.0",
|
|
44
41
|
"oxc-parser": "^0.90.0",
|
|
45
42
|
"semver": "^7.7.3",
|
|
46
43
|
"smol-toml": "1.8.0",
|
|
47
44
|
"ts-to-zod": "^5.1.0",
|
|
48
45
|
"typescript": "^5.9.3",
|
|
49
|
-
"yaml": "^2.9.0"
|
|
46
|
+
"yaml": "^2.9.0",
|
|
47
|
+
"zod": "^4.3.6"
|
|
50
48
|
},
|
|
51
49
|
"devEngines": {
|
|
52
50
|
"packageManager": {
|
|
@@ -56,7 +54,7 @@
|
|
|
56
54
|
},
|
|
57
55
|
"main": "index.ts",
|
|
58
56
|
"license": "Apache-2.0",
|
|
59
|
-
"version": "0.9.
|
|
57
|
+
"version": "0.9.46",
|
|
60
58
|
"types": "index.ts",
|
|
61
59
|
"type": "module",
|
|
62
60
|
"exports": {
|
|
@@ -67,7 +65,8 @@
|
|
|
67
65
|
"files": [
|
|
68
66
|
"index.ts",
|
|
69
67
|
"src",
|
|
70
|
-
"tasks"
|
|
68
|
+
"tasks",
|
|
69
|
+
"shims/python-node"
|
|
71
70
|
],
|
|
72
71
|
"//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"bunx projen\"."
|
|
73
72
|
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
|
|
3
|
+
import { PythonHeaders } from "./headers.ts";
|
|
4
|
+
import { installPythonGlobals, pythonHost } from "./host.ts";
|
|
5
|
+
|
|
6
|
+
installPythonGlobals();
|
|
7
|
+
|
|
8
|
+
(globalThis as typeof globalThis & { Buffer?: typeof Buffer }).Buffer = Buffer;
|
|
9
|
+
|
|
10
|
+
const globals = globalThis as typeof globalThis & {
|
|
11
|
+
AbortController?: typeof AbortController;
|
|
12
|
+
Headers?: typeof Headers;
|
|
13
|
+
Request?: typeof Request;
|
|
14
|
+
Response?: typeof Response;
|
|
15
|
+
structuredClone?: typeof structuredClone;
|
|
16
|
+
TextDecoder?: typeof TextDecoder;
|
|
17
|
+
TextEncoder?: typeof TextEncoder;
|
|
18
|
+
fetch?: typeof fetch;
|
|
19
|
+
};
|
|
20
|
+
globals.AbortController ??= class AbortController {
|
|
21
|
+
readonly signal: AbortSignal;
|
|
22
|
+
readonly #listeners = new Set<() => void>();
|
|
23
|
+
|
|
24
|
+
constructor() {
|
|
25
|
+
this.signal = {
|
|
26
|
+
aborted: false,
|
|
27
|
+
addEventListener: (name: string, listener: EventListenerOrEventListenerObject) => {
|
|
28
|
+
if (name === "abort") {
|
|
29
|
+
this.#listeners.add(
|
|
30
|
+
typeof listener === "function"
|
|
31
|
+
? () => listener(new Event("abort"))
|
|
32
|
+
: () => listener.handleEvent(new Event("abort")),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
removeEventListener: () => {},
|
|
37
|
+
} as AbortSignal;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
abort(): void {
|
|
41
|
+
if (this.signal.aborted) return;
|
|
42
|
+
Object.defineProperty(this.signal, "aborted", { value: true });
|
|
43
|
+
for (const listener of this.#listeners) listener();
|
|
44
|
+
this.#listeners.clear();
|
|
45
|
+
}
|
|
46
|
+
} as typeof AbortController;
|
|
47
|
+
|
|
48
|
+
globals.Headers ??= PythonHeaders as unknown as typeof Headers;
|
|
49
|
+
|
|
50
|
+
class PythonResponse {
|
|
51
|
+
readonly headers: Headers;
|
|
52
|
+
readonly ok: boolean;
|
|
53
|
+
readonly status: number;
|
|
54
|
+
readonly #body: Uint8Array;
|
|
55
|
+
|
|
56
|
+
constructor(body: Uint8Array, init: ResponseInit = {}) {
|
|
57
|
+
this.#body = body;
|
|
58
|
+
this.status = init.status ?? 200;
|
|
59
|
+
this.ok = this.status >= 200 && this.status < 300;
|
|
60
|
+
this.headers = new Headers(init.headers);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async arrayBuffer(): Promise<ArrayBuffer> {
|
|
64
|
+
return this.#body.slice().buffer;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async text(): Promise<string> {
|
|
68
|
+
return new TextDecoder().decode(this.#body);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async json(): Promise<unknown> {
|
|
72
|
+
return JSON.parse(await this.text());
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
globals.Response ??= PythonResponse as unknown as typeof Response;
|
|
77
|
+
globals.structuredClone ??= ((value: unknown) => cloneStructured(value)) as typeof structuredClone;
|
|
78
|
+
globals.TextEncoder ??= class TextEncoder {
|
|
79
|
+
encode(value: string): Uint8Array {
|
|
80
|
+
const encoded = unescape(encodeURIComponent(String(value)));
|
|
81
|
+
return Uint8Array.from(encoded, (character) => character.charCodeAt(0));
|
|
82
|
+
}
|
|
83
|
+
} as typeof TextEncoder;
|
|
84
|
+
globals.TextDecoder ??= class TextDecoder {
|
|
85
|
+
decode(value?: ArrayBuffer | ArrayBufferView): string {
|
|
86
|
+
const bytes =
|
|
87
|
+
value instanceof ArrayBuffer
|
|
88
|
+
? new Uint8Array(value)
|
|
89
|
+
: value
|
|
90
|
+
? new Uint8Array(value.buffer, value.byteOffset, value.byteLength)
|
|
91
|
+
: new Uint8Array();
|
|
92
|
+
const encoded = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
|
|
93
|
+
return decodeURIComponent(escape(encoded));
|
|
94
|
+
}
|
|
95
|
+
} as typeof TextDecoder;
|
|
96
|
+
globals.fetch ??= (async (input: string | URL | Request, init: RequestInit = {}) => {
|
|
97
|
+
const request = typeof input === "object" && "url" in input ? input : undefined;
|
|
98
|
+
const headers = init.headers ?? request?.headers;
|
|
99
|
+
const response = await pythonHost().http.fetch(
|
|
100
|
+
request?.url ?? String(input),
|
|
101
|
+
init.method ?? request?.method,
|
|
102
|
+
headers ? Object.fromEntries(new Headers(headers).entries()) : undefined,
|
|
103
|
+
typeof init.body === "string" ? init.body : undefined,
|
|
104
|
+
);
|
|
105
|
+
return new Response(Uint8Array.from(response.body), {
|
|
106
|
+
status: response.status,
|
|
107
|
+
headers: response.headers,
|
|
108
|
+
});
|
|
109
|
+
}) as typeof fetch;
|
|
110
|
+
|
|
111
|
+
function cloneStructured(value: unknown, seen = new Map<object, unknown>()): unknown {
|
|
112
|
+
if (value === null || typeof value !== "object") return value;
|
|
113
|
+
const cached = seen.get(value);
|
|
114
|
+
if (cached !== undefined) return cached;
|
|
115
|
+
if (value instanceof Date) return new Date(value.getTime());
|
|
116
|
+
if (Array.isArray(value)) {
|
|
117
|
+
const clone: unknown[] = [];
|
|
118
|
+
seen.set(value, clone);
|
|
119
|
+
for (const item of value) clone.push(cloneStructured(item, seen));
|
|
120
|
+
return clone;
|
|
121
|
+
}
|
|
122
|
+
if (Object.getPrototypeOf(value) === Object.prototype) {
|
|
123
|
+
const clone: Record<string, unknown> = {};
|
|
124
|
+
seen.set(value, clone);
|
|
125
|
+
for (const [key, item] of Object.entries(value)) clone[key] = cloneStructured(item, seen);
|
|
126
|
+
return clone;
|
|
127
|
+
}
|
|
128
|
+
throw new TypeError(`PythonMonkey structuredClone does not support ${value.constructor.name}`);
|
|
129
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import { Readable, Writable } from "node:stream";
|
|
3
|
+
|
|
4
|
+
import { pythonHost, type PythonProcessHandle } from "./host.ts";
|
|
5
|
+
|
|
6
|
+
interface SpawnOptions {
|
|
7
|
+
env?: Record<string, string>;
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class PythonChildProcess extends EventEmitter {
|
|
12
|
+
readonly pid = undefined;
|
|
13
|
+
readonly stdin = new Writable({ write: (_chunk, _encoding, callback) => callback() });
|
|
14
|
+
readonly stdout = new Readable({ read() {} });
|
|
15
|
+
readonly stderr = new Readable({ read() {} });
|
|
16
|
+
private active = true;
|
|
17
|
+
private readonly handle: PythonProcessHandle;
|
|
18
|
+
|
|
19
|
+
constructor(command: string, args: string[], options: SpawnOptions) {
|
|
20
|
+
super();
|
|
21
|
+
this.handle = pythonHost().process.start(command, args, options.env, undefined);
|
|
22
|
+
options.signal?.addEventListener("abort", () => {
|
|
23
|
+
if (!this.active) return;
|
|
24
|
+
this.active = false;
|
|
25
|
+
// Actually terminate the Python child so an aborted call leaks nothing.
|
|
26
|
+
this.handle.kill();
|
|
27
|
+
this.stdout.push(null);
|
|
28
|
+
this.stderr.push(null);
|
|
29
|
+
this.emit(
|
|
30
|
+
"error",
|
|
31
|
+
Object.assign(new Error("Process execution was aborted"), { code: "ABORT_ERR" }),
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
void this.handle.wait().then(
|
|
35
|
+
(result) => {
|
|
36
|
+
if (!this.active) return;
|
|
37
|
+
this.active = false;
|
|
38
|
+
if (result.stdout) this.stdout.push(result.stdout);
|
|
39
|
+
if (result.stderr) this.stderr.push(result.stderr);
|
|
40
|
+
this.stdout.push(null);
|
|
41
|
+
this.stderr.push(null);
|
|
42
|
+
this.emit("close", result.exitCode);
|
|
43
|
+
this.emit("exit", result.exitCode);
|
|
44
|
+
},
|
|
45
|
+
(error) => {
|
|
46
|
+
if (!this.active) return;
|
|
47
|
+
this.active = false;
|
|
48
|
+
this.stdout.push(null);
|
|
49
|
+
this.stderr.push(null);
|
|
50
|
+
this.emit("error", error);
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
kill(): boolean {
|
|
56
|
+
this.handle.kill();
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function spawn(command: string, args: string[] = [], options: SpawnOptions = {}) {
|
|
62
|
+
return new PythonChildProcess(command, args, options);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function execFile(
|
|
66
|
+
command: string,
|
|
67
|
+
args: string[],
|
|
68
|
+
_options: object,
|
|
69
|
+
callback: (error: Error | null, result?: { stdout: string; stderr: string }) => void,
|
|
70
|
+
): void {
|
|
71
|
+
void pythonHost()
|
|
72
|
+
.process.start(command, args, undefined, undefined)
|
|
73
|
+
.wait()
|
|
74
|
+
.then(
|
|
75
|
+
(result) => {
|
|
76
|
+
if (result.exitCode === 0) {
|
|
77
|
+
callback(null, { stdout: result.stdout ?? "", stderr: result.stderr ?? "" });
|
|
78
|
+
} else {
|
|
79
|
+
callback(
|
|
80
|
+
Object.assign(new Error(result.stderr || `Command exited ${result.exitCode}`), {
|
|
81
|
+
code: result.exitCode,
|
|
82
|
+
}),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
(error) => callback(error instanceof Error ? error : new Error(String(error))),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function spawnSync(): never {
|
|
91
|
+
throw Object.assign(new Error("Synchronous subprocesses are unavailable in PythonMonkey"), {
|
|
92
|
+
code: "ENOSYS",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default { execFile, spawn, spawnSync };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { pythonHost } from "./host.ts";
|
|
2
|
+
|
|
3
|
+
class PythonHash {
|
|
4
|
+
private readonly content: number[] = [];
|
|
5
|
+
|
|
6
|
+
constructor(private readonly algorithm: string) {}
|
|
7
|
+
|
|
8
|
+
update(value: string | ArrayBuffer | ArrayBufferView): this {
|
|
9
|
+
const bytes =
|
|
10
|
+
typeof value === "string"
|
|
11
|
+
? new TextEncoder().encode(value)
|
|
12
|
+
: value instanceof ArrayBuffer
|
|
13
|
+
? new Uint8Array(value)
|
|
14
|
+
: new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
15
|
+
this.content.push(...bytes);
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
digest(encoding?: "hex" | "base64"): string | Uint8Array {
|
|
20
|
+
const digest = pythonHost().crypto.digest(this.algorithm, this.content, encoding);
|
|
21
|
+
if (typeof digest === "string") return digest;
|
|
22
|
+
const bytes = Uint8Array.from(digest) as Uint8Array & {
|
|
23
|
+
readBigInt64BE(offset: number): bigint;
|
|
24
|
+
};
|
|
25
|
+
bytes.readBigInt64BE = (offset) => {
|
|
26
|
+
let value = 0n;
|
|
27
|
+
for (const byte of bytes.slice(offset, offset + 8)) {
|
|
28
|
+
value = (value << 8n) | BigInt(byte);
|
|
29
|
+
}
|
|
30
|
+
return BigInt.asIntN(64, value);
|
|
31
|
+
};
|
|
32
|
+
return bytes;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function createHash(algorithm: string): PythonHash {
|
|
37
|
+
const normalized = algorithm.toLowerCase();
|
|
38
|
+
if (!getHashes().includes(normalized)) {
|
|
39
|
+
throw new Error(`Unsupported hash algorithm: ${algorithm}`);
|
|
40
|
+
}
|
|
41
|
+
return new PythonHash(normalized);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getHashes(): string[] {
|
|
45
|
+
return pythonHost().crypto.hashes();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function randomBytes(length: number): Uint8Array {
|
|
49
|
+
return Uint8Array.from(pythonHost().crypto.randomBytes(length));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function randomUUID(): string {
|
|
53
|
+
const bytes = randomBytes(16);
|
|
54
|
+
bytes[6] = (bytes[6]! & 0x0f) | 0x40;
|
|
55
|
+
bytes[8] = (bytes[8]! & 0x3f) | 0x80;
|
|
56
|
+
const hex = Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join("");
|
|
57
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default { createHash, getHashes, randomBytes, randomUUID };
|