@nimbus-sh/core 0.2.0 → 0.4.0

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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/dist/_shared/tarball-stream.d.ts +93 -0
  4. package/dist/_shared/tarball-stream.d.ts.map +1 -0
  5. package/dist/_shared/tarball-stream.js +235 -0
  6. package/dist/_shared/tarball.d.ts +17 -0
  7. package/dist/_shared/tarball.d.ts.map +1 -0
  8. package/dist/_shared/tarball.js +39 -0
  9. package/dist/index.d.ts +3 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -0
  12. package/dist/runtime/clang-runner.d.ts +38 -0
  13. package/dist/runtime/clang-runner.d.ts.map +1 -0
  14. package/dist/runtime/clang-runner.js +866 -0
  15. package/dist/runtime/facet-host.d.ts +12 -0
  16. package/dist/runtime/facet-host.d.ts.map +1 -1
  17. package/dist/runtime/local-facet-host.d.ts.map +1 -1
  18. package/dist/runtime/local-facet-host.js +29 -9
  19. package/dist/runtime/ruby-gems.d.ts +30 -0
  20. package/dist/runtime/ruby-gems.d.ts.map +1 -0
  21. package/dist/runtime/ruby-gems.js +636 -0
  22. package/dist/runtime/ruby-runner.d.ts +127 -0
  23. package/dist/runtime/ruby-runner.d.ts.map +1 -0
  24. package/dist/runtime/ruby-runner.js +1357 -0
  25. package/dist/runtime/runtime-package.d.ts +63 -0
  26. package/dist/runtime/runtime-package.d.ts.map +1 -0
  27. package/dist/runtime/runtime-package.js +66 -0
  28. package/dist/runtime/runtime-registry.d.ts +3 -2
  29. package/dist/runtime/runtime-registry.d.ts.map +1 -1
  30. package/dist/runtime/runtime-registry.js +1 -1
  31. package/dist/runtime/session-process-supervisor.d.ts +15 -0
  32. package/dist/runtime/session-process-supervisor.d.ts.map +1 -1
  33. package/dist/runtime/session-process-supervisor.js +30 -0
  34. package/dist/workspace/nimbus-workspace.d.ts +102 -22
  35. package/dist/workspace/nimbus-workspace.d.ts.map +1 -1
  36. package/dist/workspace/nimbus-workspace.js +197 -52
  37. package/package.json +4 -2
  38. package/src/_shared/tarball-stream.ts +263 -0
  39. package/src/_shared/tarball.ts +46 -0
  40. package/src/index.ts +7 -0
  41. package/src/runtime/clang-runner.ts +924 -0
  42. package/src/runtime/facet-host.ts +12 -0
  43. package/src/runtime/local-facet-host.ts +28 -8
  44. package/src/runtime/ruby-gems.ts +682 -0
  45. package/src/runtime/ruby-runner.ts +1484 -0
  46. package/src/runtime/runtime-package.ts +114 -0
  47. package/src/runtime/runtime-registry.ts +4 -3
  48. package/src/runtime/session-process-supervisor.ts +27 -0
  49. package/src/workspace/nimbus-workspace.ts +268 -63
@@ -79,6 +79,18 @@ export interface FacetSpec {
79
79
  syscalls?: FacetSyscalls;
80
80
  /** Facets the host may keep warm for this spec. Default 1. */
81
81
  concurrency?: number;
82
+ /**
83
+ * Who a warm facet may be reused FOR. Default `session`.
84
+ *
85
+ * A scope keeps whatever the last call left in it, so by default it may only
86
+ * ever answer for the session that opened it. `global` is a claim the
87
+ * RUNTIME makes about itself and one only a pure function can make: no
88
+ * syscalls binding, nothing retained between calls, output decided entirely
89
+ * by the arguments. The clang toolchain is that — a compile is the same
90
+ * compile for every tenant — and one warm isolate holding its 31 MiB of
91
+ * compiled compiler serves all of them.
92
+ */
93
+ reuse?: 'session' | 'global';
82
94
  }
83
95
 
84
96
  /** The session a facet's syscalls reach, and who they reach it as. */
@@ -113,12 +113,21 @@ export function localFacetHost(): FacetHost {
113
113
  root: string,
114
114
  options?: FacetFilesystemOptions,
115
115
  ): FacetFilesystemSeed | { error: string } {
116
- return snapshotVfs(vfs, root, {
116
+ const seeded = snapshotVfs(vfs, root, {
117
117
  extraRoots: options?.extraRoots,
118
118
  skipSubdirs: [],
119
119
  maxBytes: LOCAL_SEED_MAX_BYTES,
120
120
  maxFiles: LOCAL_SEED_MAX_FILES,
121
121
  });
122
+ if ('error' in seeded) return seeded;
123
+ // The seed is also the WHOLE of what this guest has, which is a stronger
124
+ // claim than the walk's: `snapshotVfs` reports the roots it listed
125
+ // exhaustively, and this says every path outside them is absent too. It
126
+ // is — a miss out there could only be answered by suspending the guest
127
+ // mid-syscall, which this host cannot do, so the alternative to "absent"
128
+ // is not "fetched" but a read the guest can never receive. Ruby's VM
129
+ // startup stats dozens of prefixes it was never given.
130
+ return { ...seeded, snapshot: { ...seeded.snapshot, enumeratedRoots: [''] } };
122
131
  },
123
132
  open: (spec) => new LocalFacet(spec),
124
133
  };
@@ -152,8 +161,7 @@ class LocalFacet implements Facet {
152
161
 
153
162
  private async call<A, R>(fn: FacetFn<A, R>, args: A, options?: FacetSubmitOptions): Promise<R> {
154
163
  if (this.disposed) throw new Error(`Nimbus: facet '${this.spec.tag}' is disposed`);
155
- const scope = await this.scope();
156
- await this.addModules(options?.wasmModules);
164
+ const scope = await this.scope(options?.wasmModules);
157
165
  let scoped = this.scoped.get(fn as FacetFn<never, unknown>);
158
166
  if (!scoped) {
159
167
  const value = scope(`(${fn.toString()})`);
@@ -172,17 +180,29 @@ class LocalFacet implements Facet {
172
180
  * The returned closure's `eval` is a DIRECT eval inside the body the preamble
173
181
  * was evaluated in, which is what puts the preamble's top-level declarations
174
182
  * in scope for every function submitted afterwards.
183
+ *
184
+ * Both wasm tables are filled BEFORE that body runs, per-call images merged
185
+ * over the spec's. A preamble may boot its runtime as it is evaluated — Ruby
186
+ * instantiates the interpreter right there — so it reads the table at that
187
+ * moment and an image added afterwards would arrive to a facet that had
188
+ * already given up on it. workerd has the same ordering for the same reason:
189
+ * per-call images ride in the module map the inner worker is built from.
175
190
  */
176
- private async scope(): Promise<(source: string) => unknown> {
177
- if (this.evaluate) return this.evaluate;
178
- await this.addModules(this.spec.wasmModules);
191
+ private async scope(
192
+ callModules: Record<string, ArrayBuffer> | undefined,
193
+ ): Promise<(source: string) => unknown> {
194
+ const built = this.evaluate;
195
+ if (!built) await this.addModules(this.spec.wasmModules);
196
+ await this.addModules(callModules);
197
+ if (built) return built;
179
198
  const globals = { __NIMBUS_WASM: this.wasmTable };
180
199
  const build = new AsyncFunction(
181
200
  'globalThis',
182
201
  `${this.spec.preamble ?? ''}\nreturn (source) => eval(source);`,
183
202
  );
184
- this.evaluate = await build.call(globals, globals) as (source: string) => unknown;
185
- return this.evaluate;
203
+ const evaluate = await build.call(globals, globals) as (source: string) => unknown;
204
+ this.evaluate = evaluate;
205
+ return evaluate;
186
206
  }
187
207
 
188
208
  private async addModules(modules: Record<string, ArrayBuffer> | undefined): Promise<void> {