@stacksjs/ts-cloud 0.7.68 → 0.7.70

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 (54) hide show
  1. package/dist/bin/cli.js +1126 -1126
  2. package/dist/{chunk-349k40rw.js → chunk-1c1vwq1p.js} +52 -10
  3. package/dist/deploy/index.js +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/queue/queue.d.ts +7 -0
  6. package/dist/queue/worker.d.ts +2 -0
  7. package/dist/ui/account/automation.html +4 -4
  8. package/dist/ui/account/security.html +2 -2
  9. package/dist/ui/applications/compose.html +4 -4
  10. package/dist/ui/applications/new.html +2 -2
  11. package/dist/ui/data/backups.html +4 -4
  12. package/dist/ui/data/services.html +4 -4
  13. package/dist/ui/data/volumes.html +4 -4
  14. package/dist/ui/index.html +4 -4
  15. package/dist/ui/integrations.html +2 -2
  16. package/dist/ui/operations/alerts.html +4 -4
  17. package/dist/ui/operations/configuration.html +4 -4
  18. package/dist/ui/operations/jobs.html +4 -4
  19. package/dist/ui/operations/maintenance.html +4 -4
  20. package/dist/ui/operations/observability.html +4 -4
  21. package/dist/ui/operations/previews.html +4 -4
  22. package/dist/ui/operations/queue.html +4 -4
  23. package/dist/ui/operations/regions.html +4 -4
  24. package/dist/ui/operations/releases.html +4 -4
  25. package/dist/ui/operations/workloads.html +4 -4
  26. package/dist/ui/security.html +2 -2
  27. package/dist/ui/server/actions.html +4 -4
  28. package/dist/ui/server/activity.html +2 -2
  29. package/dist/ui/server/capacity.html +4 -4
  30. package/dist/ui/server/database.html +4 -4
  31. package/dist/ui/server/deployments.html +4 -4
  32. package/dist/ui/server/diagnostics.html +2 -2
  33. package/dist/ui/server/firewall.html +4 -4
  34. package/dist/ui/server/fleet.html +4 -4
  35. package/dist/ui/server/logs.html +4 -4
  36. package/dist/ui/server/metrics.html +4 -4
  37. package/dist/ui/server/security.html +2 -2
  38. package/dist/ui/server/services.html +2 -2
  39. package/dist/ui/server/sites.html +4 -4
  40. package/dist/ui/server/ssh-keys.html +4 -4
  41. package/dist/ui/server/team.html +4 -4
  42. package/dist/ui/server/terminal.html +2 -2
  43. package/dist/ui/serverless/alarms.html +4 -4
  44. package/dist/ui/serverless/assets.html +2 -2
  45. package/dist/ui/serverless/cost.html +2 -2
  46. package/dist/ui/serverless/data.html +4 -4
  47. package/dist/ui/serverless/firewall.html +2 -2
  48. package/dist/ui/serverless/functions.html +3 -3
  49. package/dist/ui/serverless/logs.html +4 -4
  50. package/dist/ui/serverless/queues.html +4 -4
  51. package/dist/ui/serverless/secrets.html +4 -4
  52. package/dist/ui/serverless/traces.html +4 -4
  53. package/dist/ui/serverless.html +4 -4
  54. package/package.json +3 -3
@@ -3381,6 +3381,24 @@ var DEFAULT_LIMITS = { project: 2, environment: 1, provider: 2, builds: 1 };
3381
3381
  var DEFAULT_RETENTION_DAYS = 30;
3382
3382
  var MAX_LOG_BYTES = 16 * 1024;
3383
3383
  var TERMINAL_STATES2 = ["succeeded", "failed", "cancelled", "timed_out"];
3384
+ var fileAvailabilityListeners = new Map;
3385
+ var memoryAvailabilityListeners = new WeakMap;
3386
+ function availabilityListeners(store) {
3387
+ if (store.path === ":memory:") {
3388
+ const existing2 = memoryAvailabilityListeners.get(store);
3389
+ if (existing2)
3390
+ return existing2;
3391
+ const created2 = new Set;
3392
+ memoryAvailabilityListeners.set(store, created2);
3393
+ return created2;
3394
+ }
3395
+ const existing = fileAvailabilityListeners.get(store.path);
3396
+ if (existing)
3397
+ return existing;
3398
+ const created = new Set;
3399
+ fileAvailabilityListeners.set(store.path, created);
3400
+ return created;
3401
+ }
3384
3402
  function optional(value) {
3385
3403
  return typeof value === "string" && value ? value : undefined;
3386
3404
  }
@@ -3484,7 +3502,22 @@ class DurableOperationQueue {
3484
3502
  now
3485
3503
  ]);
3486
3504
  this.appendLog(operation.id, "Queued for durable execution.", { stream: "system" });
3487
- return this.view(operation.id);
3505
+ const view = this.view(operation.id);
3506
+ this.notifyAvailable();
3507
+ return view;
3508
+ }
3509
+ onAvailable(listener) {
3510
+ const listeners = availabilityListeners(this.controlPlane);
3511
+ listeners.add(listener);
3512
+ return () => {
3513
+ listeners.delete(listener);
3514
+ if (listeners.size === 0 && this.controlPlane.path !== ":memory:")
3515
+ fileAvailabilityListeners.delete(this.controlPlane.path);
3516
+ };
3517
+ }
3518
+ notifyAvailable() {
3519
+ for (const listener of availabilityListeners(this.controlPlane))
3520
+ listener();
3488
3521
  }
3489
3522
  getJob(operationId) {
3490
3523
  const row = this.controlPlane.database.query("SELECT * FROM operation_jobs WHERE operation_id=?").get(operationId);
@@ -3497,7 +3530,7 @@ class DurableOperationQueue {
3497
3530
  return;
3498
3531
  let approximatePosition;
3499
3532
  if (operation.state === "queued") {
3500
- const ahead = Number(this.controlPlane.database.query("SELECT COUNT(*) AS count FROM operations o JOIN operation_jobs j ON j.operation_id=o.id WHERE o.state='queued' AND j.available_at<=? AND (o.priority>? OR (o.priority=? AND o.created_at<?))").get(this.now(), operation.priority, operation.priority, operation.createdAt)?.count ?? 0);
3533
+ const ahead = Number(this.controlPlane.database.query(`SELECT COUNT(*) AS count FROM operations o JOIN operation_jobs j ON j.operation_id=o.id WHERE o.state='queued' AND j.available_at<=? AND (o.priority>? OR (o.priority=? AND o.created_at<?))`).get(this.now(), operation.priority, operation.priority, operation.createdAt)?.count ?? 0);
3501
3534
  approximatePosition = { ahead, precision: "bounded" };
3502
3535
  }
3503
3536
  return { operation, job: metadata, approximatePosition };
@@ -3532,17 +3565,17 @@ class DurableOperationQueue {
3532
3565
  if (lock)
3533
3566
  return `resource_lock:${metadata.lockKey}`;
3534
3567
  }
3535
- if (operation.projectId && this.runningCount("SELECT COUNT(*) AS count FROM operations WHERE state='running' AND project_id=?", [
3568
+ if (operation.projectId && this.runningCount(`SELECT COUNT(*) AS count FROM operations WHERE state='running' AND project_id=?`, [
3536
3569
  operation.projectId
3537
3570
  ]) >= this.limits.project)
3538
3571
  return "project_concurrency";
3539
- if (operation.environmentId && this.runningCount("SELECT COUNT(*) AS count FROM operations WHERE state='running' AND environment_id=?", [
3572
+ if (operation.environmentId && this.runningCount(`SELECT COUNT(*) AS count FROM operations WHERE state='running' AND environment_id=?`, [
3540
3573
  operation.environmentId
3541
3574
  ]) >= this.limits.environment)
3542
3575
  return "environment_concurrency";
3543
- if (metadata.providerKey && this.runningCount("SELECT COUNT(*) AS count FROM operations o JOIN operation_jobs j ON j.operation_id=o.id WHERE o.state='running' AND j.provider_key=?", [metadata.providerKey]) >= this.limits.provider)
3576
+ if (metadata.providerKey && this.runningCount(`SELECT COUNT(*) AS count FROM operations o JOIN operation_jobs j ON j.operation_id=o.id WHERE o.state='running' AND j.provider_key=?`, [metadata.providerKey]) >= this.limits.provider)
3544
3577
  return "provider_concurrency";
3545
- if (metadata.buildSlot && this.runningCount("SELECT COUNT(*) AS count FROM operations o JOIN operation_jobs j ON j.operation_id=o.id WHERE o.state='running' AND j.build_slot=1", []) >= this.limits.builds)
3578
+ if (metadata.buildSlot && this.runningCount(`SELECT COUNT(*) AS count FROM operations o JOIN operation_jobs j ON j.operation_id=o.id WHERE o.state='running' AND j.build_slot=1`, []) >= this.limits.builds)
3546
3579
  return "build_concurrency";
3547
3580
  return;
3548
3581
  }
@@ -3597,7 +3630,7 @@ class DurableOperationQueue {
3597
3630
  const now = this.now();
3598
3631
  const expiry = this.leaseExpiry();
3599
3632
  const metadata = this.getJob(operationId);
3600
- const changed = this.controlPlane.database.run("UPDATE operations SET lease_expires_at=?, updated_at=?, version=version+1 WHERE id=? AND state='running' AND lease_owner=? AND version=?", [expiry, now, operationId, this.workerId, operation.version]).changes;
3633
+ const changed = this.controlPlane.database.run(`UPDATE operations SET lease_expires_at=?, updated_at=?, version=version+1 WHERE id=? AND state='running' AND lease_owner=? AND version=?`, [expiry, now, operationId, this.workerId, operation.version]).changes;
3601
3634
  if (changed !== 1)
3602
3635
  throw new Error("Operation lease changed before heartbeat");
3603
3636
  this.controlPlane.database.run("UPDATE operation_jobs SET heartbeat_at=?, current_step=COALESCE(?, current_step), updated_at=? WHERE operation_id=?", [now, step2 ?? null, now, operationId]);
@@ -3691,7 +3724,9 @@ class DurableOperationQueue {
3691
3724
  payload: { errorClass, availableAt: available, nextAttempt: operation.attempt + 1 }
3692
3725
  });
3693
3726
  this.appendLog(operationId, `Retry queued for error class ${errorClass}.`, { stream: "system" });
3694
- return this.controlPlane.getOperation(queued.id);
3727
+ const operationView = this.controlPlane.getOperation(queued.id);
3728
+ this.notifyAvailable();
3729
+ return operationView;
3695
3730
  }
3696
3731
  release(operationId) {
3697
3732
  this.controlPlane.database.run("DELETE FROM operation_locks WHERE operation_id=?", [operationId]);
@@ -3832,7 +3867,7 @@ class DurableOperationQueue {
3832
3867
  expectedVersion: operation.version,
3833
3868
  error: "Worker lease expired; safely requeued from the last checkpoint."
3834
3869
  });
3835
- this.controlPlane.database.run("UPDATE operation_jobs SET available_at=?, blocked_reason='worker_restart', updated_at=? WHERE operation_id=?", [this.now(), this.now(), operation.id]);
3870
+ this.controlPlane.database.run(`UPDATE operation_jobs SET available_at=?, blocked_reason='worker_restart', updated_at=? WHERE operation_id=?`, [this.now(), this.now(), operation.id]);
3836
3871
  this.appendLog(operation.id, "Worker lease expired; operation requeued from its persisted checkpoint.", {
3837
3872
  stream: "system"
3838
3873
  });
@@ -3885,11 +3920,12 @@ class DurableQueueWorker {
3885
3920
  waits = new Map;
3886
3921
  lanes = [];
3887
3922
  running = false;
3923
+ unsubscribeAvailability;
3888
3924
  constructor(queue, handlers, options = {}) {
3889
3925
  this.queue = queue;
3890
3926
  this.handlers = handlers;
3891
3927
  this.parallelism = bounded(options.parallelism, 4, 100);
3892
- this.pollIntervalMs = bounded(options.pollIntervalMs, 500, 60000);
3928
+ this.pollIntervalMs = bounded(options.pollIntervalMs, 5000, 60000);
3893
3929
  this.onResult = options.onResult;
3894
3930
  this.onError = options.onError;
3895
3931
  }
@@ -3900,11 +3936,17 @@ class DurableQueueWorker {
3900
3936
  if (this.running)
3901
3937
  return this;
3902
3938
  this.running = true;
3939
+ this.unsubscribeAvailability = this.queue.onAvailable(() => this.wake());
3903
3940
  this.lanes = Array.from({ length: this.parallelism }, () => this.runLane());
3904
3941
  return this;
3905
3942
  }
3906
3943
  stop() {
3907
3944
  this.running = false;
3945
+ this.unsubscribeAvailability?.();
3946
+ this.unsubscribeAvailability = undefined;
3947
+ this.wake();
3948
+ }
3949
+ wake() {
3908
3950
  for (const [timer, resolve] of this.waits) {
3909
3951
  clearTimeout(timer);
3910
3952
  resolve();
@@ -32,7 +32,7 @@ import {
32
32
  synchronizeDashboardUsers,
33
33
  trackDashboardOperation,
34
34
  verifyStaticApiOrigin
35
- } from "../chunk-349k40rw.js";
35
+ } from "../chunk-1c1vwq1p.js";
36
36
  import {
37
37
  deleteStaticSite,
38
38
  deployStaticSite,
package/dist/index.js CHANGED
@@ -285,7 +285,7 @@ import {
285
285
  volumeCapabilities,
286
286
  webhookEndpoint,
287
287
  zeroCapacity
288
- } from "./chunk-349k40rw.js";
288
+ } from "./chunk-1c1vwq1p.js";
289
289
  import {
290
290
  deleteStaticSite,
291
291
  deployStaticSite,
@@ -17,6 +17,13 @@ export declare class DurableOperationQueue {
17
17
  private now;
18
18
  private leaseExpiry;
19
19
  enqueue(input: EnqueueOperationInput): QueueOperationView;
20
+ /**
21
+ * Wake in-process workers as soon as work becomes available. Durable workers
22
+ * still poll as a cross-process/recovery fallback, but dashboard API actions
23
+ * no longer need an aggressive idle database polling interval.
24
+ */
25
+ onAvailable(listener: () => void): () => void;
26
+ private notifyAvailable;
20
27
  getJob(operationId: string): OperationJob | undefined;
21
28
  view(operationId: string): QueueOperationView | undefined;
22
29
  list(input?: {
@@ -20,10 +20,12 @@ export declare class DurableQueueWorker {
20
20
  private readonly waits;
21
21
  private lanes;
22
22
  private running;
23
+ private unsubscribeAvailability?;
23
24
  constructor(queue: DurableOperationQueue, handlers: Record<string, QueueOperationHandler>, options?: DurableQueueWorkerOptions);
24
25
  get active(): boolean;
25
26
  start(): this;
26
27
  stop(): void;
28
+ private wake;
27
29
  settled(): Promise<void>;
28
30
  /** Execute all currently claimable work, using the configured lane bound. */
29
31
  drain(): Promise<QueueRunResult[]>;