@supacloud/elysia 0.22.0 → 0.23.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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Elysia, type AnyElysia, type Context, type StatusMap } from "elysia";
2
2
  import type { CommandRuntimeAudit, CommandRuntimeAuthorizer, CommandRuntimeGovernance, CommandRuntimeInvocation, CommandRuntimeMiddleware } from "@supacloud/app";
3
+ import { PendingWorkRegistry } from "@supacloud/app/runtime";
3
4
  import { type CommandPreview } from "@supacloud/contracts";
4
5
  import { type HttpPolicyRegistry } from "./http-policy";
5
6
  export { HttpPolicyConfigurationError } from "./http-policy";
@@ -247,7 +248,7 @@ export type ApplicationHttpContext<Http extends AnyElysia = Elysia> = Omit<Conte
247
248
  store: Http["~Singleton"]["store"];
248
249
  derive: Http["~Singleton"]["derive"] & Http["~Ephemeral"]["derive"];
249
250
  }>, "body" | "params" | "query" | "headers" | "cookie"> & Pick<HttpContext, "body" | "params" | "query" | "headers" | "cookie">;
250
- export type HttpRequestContextFactory<Http extends AnyElysia = Elysia, Value = unknown> = (request: Request, context: ApplicationHttpContext<Http>) => Value | Promise<Value>;
251
+ export type HttpRequestContextFactory<Http extends AnyElysia = Elysia, Value = unknown> = (request: Request, context: ApplicationHttpContext<Http>, signal?: AbortSignal) => Value | Promise<Value>;
251
252
  export interface ApplicationOptions<Http extends AnyElysia = Elysia, RequestContext = unknown> {
252
253
  name?: string;
253
254
  /** false rejects extra schema properties instead of silently removing them. */
@@ -272,6 +273,11 @@ export interface ApplicationOptions<Http extends AnyElysia = Elysia, RequestCont
272
273
  onExecution?: ExecutionObserver;
273
274
  /** Optional read-only OpenAPI and GraphQL documentation endpoints. */
274
275
  documentation?: ApplicationDocumentationOptions;
276
+ /** Bounded lifecycle work accounting; registries remain owned by each host. */
277
+ pendingWork?: {
278
+ capacity?: number;
279
+ shutdownTimeoutMs?: number;
280
+ };
275
281
  }
276
282
  /** Lifecycle controls exposed by createApplication for explicit host startup/shutdown. */
277
283
  export interface ApplicationLifecycle {
@@ -279,6 +285,11 @@ export interface ApplicationLifecycle {
279
285
  readonly destroyed: boolean;
280
286
  initialize(): Promise<void>;
281
287
  destroy(): Promise<void>;
288
+ readonly pendingWork: PendingWorkRegistry;
289
+ waitForIdle(options?: {
290
+ timeoutMs?: number;
291
+ includeBackground?: boolean;
292
+ }): Promise<void>;
282
293
  }
283
294
  export interface JobInvocation {
284
295
  job: CompiledJob;
@@ -349,7 +360,7 @@ interface HttpContext {
349
360
  * and passed to the controller input as `scope`. Request-scoped controllers
350
361
  * are looked up on that scope, everything else on `services`.
351
362
  */
352
- export declare function createModulePlugin<const Services extends Record<string, unknown>, const Http extends AnyElysia = Elysia>(compiled: CompiledModule, services: Services, ctxFactory?: HttpRequestContextFactory<Http>, options?: Pick<ApplicationOptions<Http>, "http" | "httpPolicies" | "commandGovernance" | "commandExecutor" | "errorMapper" | "onExecution" | "normalize">, imported?: Record<string, Record<string, unknown>>): import("elysia/types").LocalHookReturn<"", "local", {
363
+ export declare function createModulePlugin<const Services extends Record<string, unknown>, const Http extends AnyElysia = Elysia>(compiled: CompiledModule, services: Services, ctxFactory?: HttpRequestContextFactory<Http>, options?: Pick<ApplicationOptions<Http>, "http" | "httpPolicies" | "commandGovernance" | "commandExecutor" | "errorMapper" | "onExecution" | "normalize">, imported?: Record<string, Record<string, unknown>>, pendingWork?: PendingWorkRegistry): import("elysia/types").LocalHookReturn<"", "local", {
353
364
  decorator: {} & Http["~Singleton"]["decorator"];
354
365
  store: ({} & Http["~Singleton"]["store"] extends infer T_1 ? { [K_1 in keyof T_1]: T_1[K_1]; } : never) extends infer T ? { [K in keyof T]: T[K]; } : never;
355
366
  derive: {} & Http["~Singleton"]["derive"];
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/index.ts
2
2
  import { Elysia as Elysia3, setupTypebox, Validator as Validator2 } from "elysia";
3
+ import { PendingWorkRegistry as PendingWorkRegistry2, PendingWorkTimeoutError } from "@supacloud/app/runtime";
3
4
  import { decodeCommandPreview } from "@supacloud/contracts";
4
5
 
5
6
  // src/command-errors.ts
@@ -1426,6 +1427,7 @@ function cloneValue(value) {
1426
1427
  return structuredClone(value);
1427
1428
  }
1428
1429
  // src/worker.ts
1430
+ import { PendingWorkRegistry } from "@supacloud/app/runtime";
1429
1431
  function workerFailureMessage(error) {
1430
1432
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Worker job failed";
1431
1433
  const normalized = message.replace(/[\u0000-\u001f\u007f]/g, " ").trim();
@@ -1558,6 +1560,11 @@ class SupaCloudWorker {
1558
1560
  workerId;
1559
1561
  concurrency;
1560
1562
  pollIntervalMs;
1563
+ shutdownTimeoutMs;
1564
+ work = new PendingWorkRegistry;
1565
+ get pendingWork() {
1566
+ return this.work;
1567
+ }
1561
1568
  executor;
1562
1569
  observer;
1563
1570
  onError;
@@ -1588,6 +1595,7 @@ class SupaCloudWorker {
1588
1595
  }));
1589
1596
  this.concurrency = this.capturePositiveInteger(options.concurrency, 1, 1000, "concurrency");
1590
1597
  this.pollIntervalMs = this.captureNonNegativeInteger(options.pollIntervalMs, 1000, 300000, "pollIntervalMs");
1598
+ this.shutdownTimeoutMs = this.captureNonNegativeInteger(options.shutdownTimeoutMs, 5000, 2147483647, "shutdownTimeoutMs");
1591
1599
  this.executor = options.executor;
1592
1600
  this.observer = options.onExecution;
1593
1601
  this.onError = options.onError;
@@ -1640,11 +1648,13 @@ class SupaCloudWorker {
1640
1648
  return;
1641
1649
  if (this.stateValue === "starting" && this.startPromise)
1642
1650
  return this.startPromise;
1643
- if (this.stateValue === "stopping" && this.stopPromise)
1644
- await this.stopPromise;
1651
+ if (this.stateValue === "stopping")
1652
+ await this.stop();
1645
1653
  this.stopRequested = false;
1654
+ if (this.work.closed)
1655
+ this.work = new PendingWorkRegistry;
1646
1656
  this.stateValue = "starting";
1647
- const startPromise = Promise.resolve().then(() => this.initialize()).then(async () => {
1657
+ const startPromise = this.work.run({ name: "worker.startup", kind: "startup" }, () => Promise.resolve().then(() => this.initialize())).then(async () => {
1648
1658
  if (this.stopRequested || this.stateValue !== "starting") {
1649
1659
  await this.releaseServices();
1650
1660
  return;
@@ -1667,9 +1677,12 @@ class SupaCloudWorker {
1667
1677
  return this.stopPromise;
1668
1678
  this.stopRequested = true;
1669
1679
  this.stateValue = "stopping";
1670
- this.claimController?.abort(new DOMException("Worker stopped", "AbortError"));
1680
+ this.work.close();
1671
1681
  const startPromise = this.startPromise;
1672
- this.stopPromise = (async () => {
1682
+ this.stopPromise = Promise.resolve().then(async () => {
1683
+ this.claimController?.abort(new DOMException("Worker stopped", "AbortError"));
1684
+ this.work.dispose();
1685
+ await this.work.waitForIdle({ timeoutMs: this.shutdownTimeoutMs });
1673
1686
  await startPromise?.catch(() => {
1674
1687
  return;
1675
1688
  });
@@ -1681,7 +1694,10 @@ class SupaCloudWorker {
1681
1694
  this.loopPromise = undefined;
1682
1695
  this.startPromise = undefined;
1683
1696
  this.stopPromise = undefined;
1684
- })();
1697
+ }).catch((error) => {
1698
+ this.stopPromise = undefined;
1699
+ throw error;
1700
+ });
1685
1701
  return this.stopPromise;
1686
1702
  }
1687
1703
  async runOnce() {
@@ -1692,20 +1708,22 @@ class SupaCloudWorker {
1692
1708
  if (!controller)
1693
1709
  throw new WorkerRegistrationError("WORKER_NOT_STARTED", "Worker claim lifecycle is unavailable");
1694
1710
  controller.signal.throwIfAborted();
1695
- const claim = await this.transport.claim(controller.signal);
1696
- if (claim === null)
1697
- return null;
1698
- return this.processClaim(claim);
1711
+ return this.work.run({ name: "worker.claim", kind: "job" }, async (signal) => {
1712
+ const claim = await this.transport.claim(controller.signal);
1713
+ return claim === null ? null : this.executeClaim(claim, signal);
1714
+ });
1699
1715
  }
1700
1716
  async processClaim(rawClaim) {
1717
+ return this.work.run({ name: "worker.job", kind: "job" }, (signal) => this.executeClaim(rawClaim, signal));
1718
+ }
1719
+ async executeClaim(rawClaim, signal) {
1701
1720
  const claim = safeClaim(this.mapClaim(rawClaim));
1702
1721
  const entry = this.registry.get(claim.jobName);
1703
- const controller = new AbortController;
1704
1722
  const context = Object.freeze({
1705
1723
  jobId: claim.id,
1706
1724
  jobName: claim.jobName,
1707
1725
  attempt: claim.attempt ?? 1,
1708
- signal: controller.signal
1726
+ signal: new AbortController().signal
1709
1727
  });
1710
1728
  if (!entry) {
1711
1729
  const receipt = await this.failClaim(rawClaim, claim, new WorkerRegistrationError("WORKER_INVALID_JOB", `Job "${claim.jobName}" is not registered`), context);
@@ -1716,7 +1734,7 @@ class SupaCloudWorker {
1716
1734
  if (this.stateValue !== "running" && this.stateValue !== "stopping") {
1717
1735
  throw new WorkerRegistrationError("WORKER_NOT_STARTED", "Worker is not active");
1718
1736
  }
1719
- const requestContext = claim.requestContext ?? await this.requestContextFactory(claim, controller.signal);
1737
+ const requestContext = claim.requestContext ?? await this.requestContextFactory(claim, signal);
1720
1738
  const moduleServices = this.moduleServices.get(entry.module.name);
1721
1739
  if (!moduleServices)
1722
1740
  throw new WorkerRegistrationError("WORKER_NOT_STARTED", "Worker services are unavailable");
@@ -1776,10 +1794,12 @@ class SupaCloudWorker {
1776
1794
  await Promise.race(this.inFlight).catch((error) => this.reportError(error));
1777
1795
  continue;
1778
1796
  }
1797
+ const complete = this.work.add({ name: "worker.claim", kind: "job" });
1779
1798
  let claim;
1780
1799
  try {
1781
1800
  claim = await this.transport.claim(signal);
1782
1801
  } catch (error) {
1802
+ complete();
1783
1803
  if (signal.aborted)
1784
1804
  break;
1785
1805
  await this.reportError(error);
@@ -1789,12 +1809,13 @@ class SupaCloudWorker {
1789
1809
  continue;
1790
1810
  }
1791
1811
  if (claim === null) {
1812
+ complete();
1792
1813
  await delay(this.pollIntervalMs, signal).catch(() => {
1793
1814
  return;
1794
1815
  });
1795
1816
  continue;
1796
1817
  }
1797
- const execution = this.processClaim(claim).catch((error) => this.reportError(error));
1818
+ const execution = this.executeClaim(claim, this.work.signal).catch((error) => this.reportError(error)).finally(complete);
1798
1819
  this.inFlight.add(execution);
1799
1820
  execution.finally(() => this.inFlight.delete(execution));
1800
1821
  }
@@ -2293,7 +2314,7 @@ function assertDeclaredResponseStatus(route, value, configuredStatus) {
2293
2314
  return;
2294
2315
  throw new ApplicationError("Response validation failed", { status: 500, code: "RESPONSE_CONTRACT_UNDECLARED" });
2295
2316
  }
2296
- function createModulePlugin(compiled, services, ctxFactory = defaultRequestContext, options = {}, imported = {}) {
2317
+ function createModulePlugin(compiled, services, ctxFactory = defaultRequestContext, options = {}, imported = {}, pendingWork) {
2297
2318
  const supportedMethods = new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
2298
2319
  const supportedFields = new Set([
2299
2320
  "method",
@@ -2352,29 +2373,35 @@ function createModulePlugin(compiled, services, ctxFactory = defaultRequestConte
2352
2373
  }
2353
2374
  }
2354
2375
  const requestContexts = new WeakMap;
2376
+ const requestCompletions = new WeakMap;
2355
2377
  const governanceExecutor = options.commandGovernance ? createCommandExecutor(options.commandGovernance, options.onExecution) : undefined;
2356
2378
  const commandExecutor = options.commandExecutor && governanceExecutor ? composeCommandExecutors(options.commandExecutor, governanceExecutor) : options.commandExecutor ?? governanceExecutor;
2357
2379
  const plugin = mountHttp(options.http, `supacloud:${compiled.name}`, options.normalize ?? true).derive(async (context) => {
2358
- const requestContext = await ctxFactory(context.request, context);
2380
+ if (pendingWork?.closed)
2381
+ throw new ApplicationError("Application is stopping", { status: 503, code: "APPLICATION_STOPPING" });
2382
+ const complete = pendingWork?.add({ name: "http.request", kind: "request" });
2383
+ if (complete)
2384
+ requestCompletions.set(context.request, complete);
2385
+ const signal = pendingWork ? AbortSignal.any([context.request.signal, pendingWork.signal]) : context.request.signal;
2386
+ const requestContext = await ctxFactory(context.request, context, signal);
2359
2387
  requestContexts.set(context.request, requestContext);
2360
2388
  return { services, requestContext };
2361
2389
  });
2362
2390
  const createRequestScope = compiled.createRequestScope;
2363
2391
  const requestScopes = new WeakMap;
2364
- if (compiled.destroyRequestScope) {
2365
- const destroyRequestScope = compiled.destroyRequestScope;
2366
- plugin.afterResponse(async ({ request }) => {
2367
- const scope = requestScopes.get(request);
2368
- if (!scope)
2369
- return;
2370
- requestScopes.delete(request);
2371
- try {
2372
- await destroyRequestScope(scope);
2373
- } catch (error) {
2374
- console.error(`supacloud: request scope cleanup failed for "${compiled.name}"`, error);
2375
- }
2376
- });
2377
- }
2392
+ plugin.afterResponse(async ({ request }) => {
2393
+ const scope = requestScopes.get(request);
2394
+ requestScopes.delete(request);
2395
+ try {
2396
+ if (scope)
2397
+ await compiled.destroyRequestScope?.(scope);
2398
+ } catch (error) {
2399
+ console.error(`supacloud: request scope cleanup failed for "${compiled.name}"`, error);
2400
+ } finally {
2401
+ requestCompletions.get(request)?.();
2402
+ requestCompletions.delete(request);
2403
+ }
2404
+ });
2378
2405
  plugin.error(async ({ error, request }) => {
2379
2406
  const code = error instanceof Error && "code" in error && typeof error.code === "string" ? error.code.toUpperCase().replaceAll("-", "_") : "UNKNOWN";
2380
2407
  const context = {
@@ -2682,16 +2709,17 @@ function isPublicApplicationError(error) {
2682
2709
  return error.expose === true && typeof error.status === "number" && Number.isInteger(error.status) && error.status >= 400 && error.status <= 599 && typeof error.code === "string" && error.code.length > 0;
2683
2710
  }
2684
2711
  function createApplication(options) {
2712
+ const pendingWork = new PendingWorkRegistry2(options.pendingWork ? { capacity: options.pendingWork.capacity } : {});
2685
2713
  const app = new Elysia3({ name: options.name ?? "supacloud:app", normalize: options.normalize ?? true }).use(contextPlugin(options.http).as("global"));
2686
2714
  if (options.documentation)
2687
2715
  app.use(createDocumentationPlugin(options.documentation));
2688
2716
  const configuredContextFactory = options.requestContext ?? defaultRequestContext;
2689
2717
  const contextCache = new WeakMap;
2690
- const ctxFactory = (request, context) => {
2718
+ const ctxFactory = (request, context, signal) => {
2691
2719
  const cached = contextCache.get(request);
2692
2720
  if (cached)
2693
2721
  return cached;
2694
- const pending = Promise.resolve(configuredContextFactory(request, context));
2722
+ const pending = Promise.resolve().then(() => configuredContextFactory(request, context, signal));
2695
2723
  contextCache.set(request, pending);
2696
2724
  return pending;
2697
2725
  };
@@ -2701,7 +2729,7 @@ function createApplication(options) {
2701
2729
  const services = module.createServices(options.deps ?? {}, imported);
2702
2730
  imported[module.name] = services;
2703
2731
  moduleServices.push({ module, services });
2704
- app.use(createModulePlugin(module, services, ctxFactory, { ...options, http: undefined }, imported));
2732
+ app.use(createModulePlugin(module, services, ctxFactory, { ...options, http: undefined }, imported, pendingWork));
2705
2733
  }
2706
2734
  let initialized = false;
2707
2735
  let destroyed = false;
@@ -2714,34 +2742,47 @@ function createApplication(options) {
2714
2742
  get destroyed() {
2715
2743
  return destroyed;
2716
2744
  },
2745
+ pendingWork,
2746
+ waitForIdle(options) {
2747
+ return pendingWork.waitForIdle(options);
2748
+ },
2717
2749
  initialize() {
2718
2750
  if (destroyed)
2719
2751
  return Promise.reject(new Error("Application has already been destroyed"));
2720
2752
  if (initializationPromise)
2721
2753
  return initializationPromise;
2722
- initializationPromise = (async () => {
2754
+ const startup = pendingWork.run({ name: "application.startup", kind: "startup" }, () => Promise.resolve().then(async () => {
2755
+ for (const { module, services } of moduleServices) {
2756
+ await module.initializeServices?.(services);
2757
+ }
2758
+ initialized = !destroyed;
2759
+ })).catch(async (error) => {
2723
2760
  try {
2724
- for (const { module, services } of moduleServices) {
2725
- await module.initializeServices?.(services);
2726
- }
2727
- initialized = true;
2728
- } catch (error) {
2729
- try {
2730
- await lifecycle.destroy();
2731
- } catch (cleanupError) {
2732
- throw new AggregateError([error, cleanupError], "Application initialization failed and cleanup failed");
2733
- }
2734
- throw error;
2761
+ await lifecycle.destroy();
2762
+ } catch (cleanupError) {
2763
+ throw new AggregateError([error, cleanupError], "Application initialization failed and cleanup failed");
2735
2764
  }
2736
- })();
2737
- return initializationPromise;
2765
+ throw error;
2766
+ });
2767
+ initializationPromise = startup;
2768
+ return startup;
2738
2769
  },
2739
2770
  destroy() {
2740
2771
  if (destructionPromise)
2741
2772
  return destructionPromise;
2742
2773
  destroyed = true;
2743
- destructionPromise = (async () => {
2774
+ pendingWork.close();
2775
+ destructionPromise = Promise.resolve().then(async () => {
2776
+ pendingWork.dispose();
2744
2777
  const errors = [];
2778
+ try {
2779
+ await pendingWork.waitForIdle({ timeoutMs: options.pendingWork?.shutdownTimeoutMs ?? 5000 });
2780
+ } catch (error) {
2781
+ if (error instanceof PendingWorkTimeoutError)
2782
+ destructionPromise = undefined;
2783
+ throw error;
2784
+ }
2785
+ pendingWork.dispose();
2745
2786
  for (const { module, services } of [...moduleServices].reverse()) {
2746
2787
  try {
2747
2788
  await module.destroyServices?.(services);
@@ -2752,13 +2793,15 @@ function createApplication(options) {
2752
2793
  if (errors.length > 0) {
2753
2794
  throw new AggregateError(errors, "Application destruction failed");
2754
2795
  }
2755
- })();
2796
+ });
2756
2797
  return destructionPromise;
2757
2798
  }
2758
2799
  };
2759
2800
  Object.defineProperties(app, {
2760
2801
  initialized: { enumerable: true, configurable: false, get: () => lifecycle.initialized },
2761
2802
  destroyed: { enumerable: true, configurable: false, get: () => lifecycle.destroyed },
2803
+ pendingWork: { enumerable: true, configurable: false, value: pendingWork },
2804
+ waitForIdle: { enumerable: true, configurable: false, value: lifecycle.waitForIdle },
2762
2805
  initialize: { enumerable: true, configurable: false, value: lifecycle.initialize },
2763
2806
  destroy: { enumerable: true, configurable: false, value: lifecycle.destroy }
2764
2807
  });
package/dist/worker.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { PendingWorkRegistry } from "@supacloud/app/runtime";
1
2
  import { type CompiledJob, type CompiledModule, type ExecutionObserver, type JobExecutor } from "./index";
2
3
  export type WorkerState = "idle" | "starting" | "running" | "stopping" | "stopped";
3
4
  /** The transport-specific claim is mapped to this small execution contract. */
@@ -76,6 +77,8 @@ export interface WorkerOptions<TClaim = WorkerClaim, TReceipt = unknown> {
76
77
  workerId?: string;
77
78
  concurrency?: number;
78
79
  pollIntervalMs?: number;
80
+ /** Maximum drain observation; timeout keeps services owned until a later stop succeeds. */
81
+ shutdownTimeoutMs?: number;
79
82
  executor?: JobExecutor;
80
83
  onExecution?: ExecutionObserver;
81
84
  onError?: (error: unknown) => void | Promise<void>;
@@ -111,6 +114,9 @@ export declare class SupaCloudWorker<TClaim = WorkerClaim, TReceipt = unknown> {
111
114
  private readonly workerId;
112
115
  private readonly concurrency;
113
116
  private readonly pollIntervalMs;
117
+ private readonly shutdownTimeoutMs;
118
+ private work;
119
+ get pendingWork(): PendingWorkRegistry;
114
120
  private readonly executor;
115
121
  private readonly observer;
116
122
  private readonly onError;
@@ -139,6 +145,7 @@ export declare class SupaCloudWorker<TClaim = WorkerClaim, TReceipt = unknown> {
139
145
  runOnce(): Promise<WorkerRunResult<TReceipt> | null>;
140
146
  /** Process a host-provided claim without performing another claim operation. */
141
147
  processClaim(rawClaim: TClaim): Promise<WorkerRunResult<TReceipt>>;
148
+ private executeClaim;
142
149
  private failClaim;
143
150
  private initialize;
144
151
  private releaseServices;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/elysia",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Elysia runtime adapter for SupaCloud compiled modules: application/request scopes, route registration and validation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,22 +49,22 @@
49
49
  "directory": "packages/elysia"
50
50
  },
51
51
  "dependencies": {
52
- "@supacloud/app": "0.20.0",
53
- "@supacloud/contracts": "0.7.0",
52
+ "@supacloud/app": "0.21.0",
53
+ "@supacloud/contracts": "0.8.0",
54
54
  "exact-mirror": "1.2.6",
55
55
  "jose": "^6.2.11",
56
56
  "typebox": "1.3.34"
57
57
  },
58
58
  "overrides": {
59
59
  "@supacloud/delivery": "0.4.0",
60
- "@supacloud/contracts": "0.7.0",
60
+ "@supacloud/contracts": "0.8.0",
61
61
  "@supacloud/commands": "0.9.0"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "elysia": "2.0.0-beta.19"
65
65
  },
66
66
  "devDependencies": {
67
- "@supacloud/compiler": "0.29.0",
67
+ "@supacloud/compiler": "0.30.0",
68
68
  "@supacloud/delivery": "0.4.0",
69
69
  "@supacloud/commands": "0.9.0",
70
70
  "@supacloud/db": "0.11.0",