@alchemy.run/boat 0.0.0 → 2.0.0-beta.87

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 (154) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +66 -4
  3. package/lib/AiProxy.d.ts +89 -0
  4. package/lib/AiProxy.d.ts.map +1 -0
  5. package/lib/AiProxy.js +125 -0
  6. package/lib/AiProxy.js.map +1 -0
  7. package/lib/ApiKey.d.ts +105 -0
  8. package/lib/ApiKey.d.ts.map +1 -0
  9. package/lib/ApiKey.js +134 -0
  10. package/lib/ApiKey.js.map +1 -0
  11. package/lib/AuthProvider.d.ts +71 -0
  12. package/lib/AuthProvider.d.ts.map +1 -0
  13. package/lib/AuthProvider.js +322 -0
  14. package/lib/AuthProvider.js.map +1 -0
  15. package/lib/BoatBindings.d.ts +52 -0
  16. package/lib/BoatBindings.d.ts.map +1 -0
  17. package/lib/BoatBindings.js +104 -0
  18. package/lib/BoatBindings.js.map +1 -0
  19. package/lib/Bootstrap.d.ts +8 -0
  20. package/lib/Bootstrap.d.ts.map +1 -0
  21. package/lib/Bootstrap.js +28 -0
  22. package/lib/Bootstrap.js.map +1 -0
  23. package/lib/ConnectSandbox.d.ts +56 -0
  24. package/lib/ConnectSandbox.d.ts.map +1 -0
  25. package/lib/ConnectSandbox.js +3 -0
  26. package/lib/ConnectSandbox.js.map +1 -0
  27. package/lib/ConnectSandboxHttp.d.ts +21 -0
  28. package/lib/ConnectSandboxHttp.d.ts.map +1 -0
  29. package/lib/ConnectSandboxHttp.js +46 -0
  30. package/lib/ConnectSandboxHttp.js.map +1 -0
  31. package/lib/Contributions.d.ts +7 -0
  32. package/lib/Contributions.d.ts.map +1 -0
  33. package/lib/Contributions.js +6 -0
  34. package/lib/Contributions.js.map +1 -0
  35. package/lib/CreateSandbox.d.ts +134 -0
  36. package/lib/CreateSandbox.d.ts.map +1 -0
  37. package/lib/CreateSandbox.js +3 -0
  38. package/lib/CreateSandbox.js.map +1 -0
  39. package/lib/CreateSandboxHttp.d.ts +21 -0
  40. package/lib/CreateSandboxHttp.d.ts.map +1 -0
  41. package/lib/CreateSandboxHttp.js +93 -0
  42. package/lib/CreateSandboxHttp.js.map +1 -0
  43. package/lib/CredentialProfile.d.ts +157 -0
  44. package/lib/CredentialProfile.d.ts.map +1 -0
  45. package/lib/CredentialProfile.js +245 -0
  46. package/lib/CredentialProfile.js.map +1 -0
  47. package/lib/Credentials.d.ts +15 -0
  48. package/lib/Credentials.d.ts.map +1 -0
  49. package/lib/Credentials.js +23 -0
  50. package/lib/Credentials.js.map +1 -0
  51. package/lib/Environment.d.ts +219 -0
  52. package/lib/Environment.d.ts.map +1 -0
  53. package/lib/Environment.js +273 -0
  54. package/lib/Environment.js.map +1 -0
  55. package/lib/Errors.d.ts +50 -0
  56. package/lib/Errors.d.ts.map +1 -0
  57. package/lib/Errors.js +17 -0
  58. package/lib/Errors.js.map +1 -0
  59. package/lib/Providers.d.ts +33 -0
  60. package/lib/Providers.d.ts.map +1 -0
  61. package/lib/Providers.js +47 -0
  62. package/lib/Providers.js.map +1 -0
  63. package/lib/Sandbox.d.ts +205 -0
  64. package/lib/Sandbox.d.ts.map +1 -0
  65. package/lib/Sandbox.js +368 -0
  66. package/lib/Sandbox.js.map +1 -0
  67. package/lib/SandboxClient.d.ts +159 -0
  68. package/lib/SandboxClient.d.ts.map +1 -0
  69. package/lib/SandboxClient.js +1 -0
  70. package/lib/SandboxClient.js.map +1 -0
  71. package/lib/SandboxEvents.d.ts +111 -0
  72. package/lib/SandboxEvents.d.ts.map +1 -0
  73. package/lib/SandboxEvents.js +60 -0
  74. package/lib/SandboxEvents.js.map +1 -0
  75. package/lib/SandboxEventsLive.d.ts +13 -0
  76. package/lib/SandboxEventsLive.d.ts.map +1 -0
  77. package/lib/SandboxEventsLive.js +63 -0
  78. package/lib/SandboxEventsLive.js.map +1 -0
  79. package/lib/Snapshot.d.ts +123 -0
  80. package/lib/Snapshot.d.ts.map +1 -0
  81. package/lib/Snapshot.js +173 -0
  82. package/lib/Snapshot.js.map +1 -0
  83. package/lib/Webhook.d.ts +56 -0
  84. package/lib/Webhook.d.ts.map +1 -0
  85. package/lib/Webhook.js +98 -0
  86. package/lib/Webhook.js.map +1 -0
  87. package/lib/index.d.ts +19 -0
  88. package/lib/index.d.ts.map +1 -0
  89. package/lib/index.js +19 -0
  90. package/lib/index.js.map +1 -0
  91. package/lib/internal/Declared.d.ts +34 -0
  92. package/lib/internal/Declared.d.ts.map +1 -0
  93. package/lib/internal/Declared.js +38 -0
  94. package/lib/internal/Declared.js.map +1 -0
  95. package/lib/internal/Org.d.ts +22 -0
  96. package/lib/internal/Org.d.ts.map +1 -0
  97. package/lib/internal/Org.js +23 -0
  98. package/lib/internal/Org.js.map +1 -0
  99. package/lib/internal/Ownership.d.ts +29 -0
  100. package/lib/internal/Ownership.d.ts.map +1 -0
  101. package/lib/internal/Ownership.js +50 -0
  102. package/lib/internal/Ownership.js.map +1 -0
  103. package/lib/internal/Program.d.ts +69 -0
  104. package/lib/internal/Program.d.ts.map +1 -0
  105. package/lib/internal/Program.js +178 -0
  106. package/lib/internal/Program.js.map +1 -0
  107. package/lib/internal/Ref.d.ts +14 -0
  108. package/lib/internal/Ref.d.ts.map +1 -0
  109. package/lib/internal/Ref.js +7 -0
  110. package/lib/internal/Ref.js.map +1 -0
  111. package/lib/internal/SandboxHandle.d.ts +17 -0
  112. package/lib/internal/SandboxHandle.d.ts.map +1 -0
  113. package/lib/internal/SandboxHandle.js +238 -0
  114. package/lib/internal/SandboxHandle.js.map +1 -0
  115. package/lib/internal/SavedSnapshot.d.ts +10 -0
  116. package/lib/internal/SavedSnapshot.d.ts.map +1 -0
  117. package/lib/internal/SavedSnapshot.js +22 -0
  118. package/lib/internal/SavedSnapshot.js.map +1 -0
  119. package/lib/internal/WebhookDelivery.d.ts +16 -0
  120. package/lib/internal/WebhookDelivery.d.ts.map +1 -0
  121. package/lib/internal/WebhookDelivery.js +109 -0
  122. package/lib/internal/WebhookDelivery.js.map +1 -0
  123. package/package.json +67 -3
  124. package/src/AiProxy.ts +171 -0
  125. package/src/ApiKey.ts +250 -0
  126. package/src/AuthProvider.ts +472 -0
  127. package/src/BoatBindings.ts +136 -0
  128. package/src/Bootstrap.ts +55 -0
  129. package/src/ConnectSandbox.ts +60 -0
  130. package/src/ConnectSandboxHttp.ts +59 -0
  131. package/src/Contributions.ts +9 -0
  132. package/src/CreateSandbox.ts +143 -0
  133. package/src/CreateSandboxHttp.ts +115 -0
  134. package/src/CredentialProfile.ts +350 -0
  135. package/src/Credentials.ts +33 -0
  136. package/src/Environment.ts +416 -0
  137. package/src/Errors.ts +34 -0
  138. package/src/Providers.ts +71 -0
  139. package/src/Sandbox.ts +647 -0
  140. package/src/SandboxClient.ts +200 -0
  141. package/src/SandboxEvents.ts +125 -0
  142. package/src/SandboxEventsLive.ts +88 -0
  143. package/src/Snapshot.ts +264 -0
  144. package/src/Webhook.ts +163 -0
  145. package/src/globals.d.ts +2 -0
  146. package/src/index.ts +18 -0
  147. package/src/internal/Declared.ts +87 -0
  148. package/src/internal/Org.ts +31 -0
  149. package/src/internal/Ownership.ts +75 -0
  150. package/src/internal/Program.ts +250 -0
  151. package/src/internal/Ref.ts +15 -0
  152. package/src/internal/SandboxHandle.ts +407 -0
  153. package/src/internal/SavedSnapshot.ts +34 -0
  154. package/src/internal/WebhookDelivery.ts +179 -0
@@ -0,0 +1,173 @@
1
+ import * as boat from "@distilled.cloud/boat-dev/boat";
2
+ import { Unowned } from "alchemy/AdoptPolicy";
3
+ import { isResolved } from "alchemy/Diff";
4
+ import * as Provider from "alchemy/Provider";
5
+ import { Resource } from "alchemy/Resource";
6
+ import { sha256Object } from "alchemy/Util/sha256";
7
+ import * as Effect from "effect/Effect";
8
+ import { SnapshotSetupFailed } from "./Errors.js";
9
+ import { declarable } from "./internal/Declared.js";
10
+ import { boatName, isGeneratedName, ownershipLabels } from "./internal/Ownership.js";
11
+ import { awaitSaved, whileSaving } from "./internal/SavedSnapshot.js";
12
+ /**
13
+ * A warm base for sandboxes: a sandbox set up with your commands and its
14
+ * disk saved under a name. Sandboxes started from it begin exactly where
15
+ * the setup left off: packages installed, a repository cloned, a build
16
+ * cache warm.
17
+ *
18
+ * Use a snapshot for work that is too slow to repeat per sandbox. Alchemy
19
+ * runs the commands in a sandbox of its own, saves it, and deletes the
20
+ * sandbox. A deploy re-runs the setup only when the commands or the base
21
+ * change, and a rebuild replaces the declared sandboxes started from it.
22
+ *
23
+ * ### Creating a Snapshot
24
+ * **Example:** A repository with its dependencies installed
25
+ * ```typescript
26
+ * export const Golden = Boat.Snapshot("Golden", {
27
+ * run: [
28
+ * "git clone https://github.com/alchemy-run/alchemy ~/alchemy",
29
+ * "cd ~/alchemy && pnpm install",
30
+ * ],
31
+ * });
32
+ * ```
33
+ *
34
+ * **Example:** Layer one snapshot on another
35
+ * ```typescript
36
+ * export const Tools = Boat.Snapshot("Tools", { run: ["npm i -g pnpm"] });
37
+ * export const Golden = Boat.Snapshot("Golden", { from: Tools, run: ["pnpm dlx playwright install"] });
38
+ * ```
39
+ *
40
+ * ### Starting sandboxes from a Snapshot
41
+ * **Example:** A declared sandbox
42
+ * ```typescript
43
+ * export const Box = Boat.Sandbox("Box", { snapshot: Golden });
44
+ * ```
45
+ *
46
+ * **Example:** Sandboxes created at run time
47
+ * ```typescript
48
+ * const sandboxes = yield* Boat.CreateSandbox(Golden);
49
+ * const sandbox = yield* sandboxes.create({ autoStop: "10 minutes" });
50
+ * ```
51
+ *
52
+ * ### Referencing a snapshot the stack doesn't manage
53
+ * **Example:** Look one up by name
54
+ * ```typescript
55
+ * const shared = Boat.Snapshot.lookup({ name: "team-base" });
56
+ * const sandboxes = yield* Boat.CreateSandbox(shared);
57
+ * ```
58
+ *
59
+ * @resource
60
+ * @product Snapshot
61
+ */
62
+ export const Snapshot = declarable()(Resource("Boat.Snapshot"));
63
+ /** Boat snapshot names: lowercase letters, digits and dashes. */
64
+ const snapshotName = (id, name) => Effect.map(boatName(id, name, 63), (value) => name !== undefined ? value : value.replace(/[^a-z0-9-]/g, "-"));
65
+ const setupHashOf = (props) => Effect.map(sha256Object({
66
+ from: props.from === undefined ? null : [props.from.name, props.from.setupHash ?? null],
67
+ run: props.run,
68
+ env: props.env ?? {},
69
+ cwd: props.cwd ?? null,
70
+ type: props.type ?? "default",
71
+ }), (hash) => hash.slice(0, 16));
72
+ const observeSnapshot = (name) => boat.getSavedSnapshot({ name }).pipe(Effect.map((got) => got.snapshot), Effect.catchTag("NotFound", () => Effect.undefined));
73
+ const toAttributes = (snapshot, setupHash) => ({
74
+ name: snapshot.name,
75
+ snapshotId: snapshot.snapshotId,
76
+ type: snapshot.type,
77
+ sizeBytes: snapshot.sizeBytes,
78
+ setupHash,
79
+ });
80
+ /** POSIX single quotes: the value verbatim. */
81
+ const quote = (value) => `'${value.replaceAll("'", `'"'"'`)}'`;
82
+ /**
83
+ * Starts a setup sandbox (from `from`, with none of the account's secrets),
84
+ * runs the commands, saves it as `name`, and always deletes the sandbox.
85
+ */
86
+ const takeSnapshot = Effect.fn(function* (id, name, props) {
87
+ const labels = yield* ownershipLabels(id);
88
+ return yield* Effect.acquireUseRelease(boat.createSandbox({
89
+ noEnv: true,
90
+ ttlSeconds: 1800,
91
+ ...(props.type === undefined ? {} : { type: props.type }),
92
+ ...(props.from === undefined ? {} : { from: props.from.name }),
93
+ labels,
94
+ }), ({ sandbox }) => Effect.gen(function* () {
95
+ const exports = Object.entries(props.env ?? {})
96
+ .map(([key, value]) => `export ${key}=${quote(value)}; `)
97
+ .join("");
98
+ for (const command of props.run) {
99
+ const result = yield* boat.executeSandboxCommand({
100
+ sandboxId: sandbox.id,
101
+ command: `${exports}${command}`,
102
+ timeoutSeconds: 600,
103
+ ...(props.cwd === undefined ? {} : { cwd: props.cwd }),
104
+ });
105
+ const exitCode = "exitCode" in result ? (result.exitCode ?? -1) : -1;
106
+ const timedOut = "timedOut" in result && result.timedOut === true;
107
+ if (exitCode !== 0 || timedOut) {
108
+ return yield* new SnapshotSetupFailed({
109
+ message: timedOut
110
+ ? `snapshot ${name}: \`${command}\` ran past 10 minutes`
111
+ : `snapshot ${name}: \`${command}\` exited with ${exitCode}`,
112
+ command,
113
+ exitCode,
114
+ stderr: "stderr" in result ? result.stderr.slice(-4000) : "",
115
+ });
116
+ }
117
+ }
118
+ // An earlier save under this name may still be running.
119
+ yield* whileSaving(boat.saveSavedSnapshot({ sandboxId: sandbox.id, name }));
120
+ return yield* awaitSaved(name);
121
+ }), ({ sandbox }) => Effect.ignore(boat.deleteSandbox({ sandboxId: sandbox.id, xAsciiConfirmDelete: sandbox.id })));
122
+ });
123
+ export const SnapshotProvider = () => Provider.effect(Snapshot, Effect.gen(function* () {
124
+ return {
125
+ stables: ["name"],
126
+ diff: Effect.fn(function* ({ id, news, olds, output }) {
127
+ if (!isResolved(news) || olds === undefined)
128
+ return undefined;
129
+ const name = yield* snapshotName(id, news.name ?? output?.name);
130
+ if (output !== undefined && name !== output.name)
131
+ return { action: "replace" };
132
+ if (output !== undefined && (yield* setupHashOf(news)) !== output.setupHash) {
133
+ return { action: "update" };
134
+ }
135
+ return undefined;
136
+ }),
137
+ read: Effect.fn(function* ({ id, olds, output }) {
138
+ const name = output?.name ?? (yield* snapshotName(id, olds?.name));
139
+ const snapshot = yield* observeSnapshot(name);
140
+ if (snapshot === undefined)
141
+ return undefined;
142
+ const attrs = toAttributes(snapshot, output?.setupHash ?? "");
143
+ return output !== undefined || isGeneratedName(snapshot.name) ? attrs : Unowned(attrs);
144
+ }),
145
+ lookup: Effect.fn(function* ({ name }) {
146
+ const snapshot = yield* observeSnapshot(name);
147
+ return snapshot === undefined ? undefined : toAttributes(snapshot, "");
148
+ }),
149
+ reconcile: Effect.fn(function* ({ id, news, output, session }) {
150
+ const name = yield* snapshotName(id, news.name ?? output?.name);
151
+ const setupHash = yield* setupHashOf(news);
152
+ // Observe — a ready snapshot of this exact setup is kept.
153
+ const observed = yield* observeSnapshot(name);
154
+ if (observed?.status === "ready" && output?.setupHash === setupHash) {
155
+ return toAttributes(observed, setupHash);
156
+ }
157
+ // Ensure — saving under an existing name replaces its disk.
158
+ yield* session.note(`Building snapshot ${name}...`);
159
+ return toAttributes(yield* takeSnapshot(id, name, news), setupHash);
160
+ }),
161
+ delete: Effect.fn(function* ({ output }) {
162
+ // A save under this name (an interrupted deploy's) must settle first.
163
+ yield* whileSaving(boat.deleteSavedSnapshot({ name: output.name })).pipe(Effect.catchTag("NotFound", () => Effect.void));
164
+ }),
165
+ list: Effect.fn(function* () {
166
+ const listed = yield* boat.listSavedSnapshots({});
167
+ return listed.snapshots
168
+ .filter((snapshot) => isGeneratedName(snapshot.name))
169
+ .map((snapshot) => toAttributes(snapshot, ""));
170
+ }),
171
+ };
172
+ }));
173
+ //# sourceMappingURL=Snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Snapshot.js","sources":["../src/Snapshot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import * as boat from "@distilled.cloud/boat-dev/boat";
2
+ import * as Provider from "alchemy/Provider";
3
+ import { Resource } from "alchemy/Resource";
4
+ import * as Redacted from "effect/Redacted";
5
+ import type { Providers } from "./Providers.ts";
6
+ /** A sandbox lifecycle event Boat delivers. */
7
+ export type WebhookEvent = "sandbox.ready" | "sandbox.hydrated" | "sandbox.error" | "sandbox.archived" | "sandbox.degraded" | "sandbox.recovered";
8
+ /** Every sandbox lifecycle event Boat sends. */
9
+ export declare const ALL_SANDBOX_EVENTS: ReadonlyArray<WebhookEvent>;
10
+ export interface WebhookProps {
11
+ /** The public HTTPS endpoint (port 443) Boat POSTs events to. Updated in place. */
12
+ url: string;
13
+ /** The events to deliver. Updated in place. */
14
+ events: WebhookEvent[];
15
+ /**
16
+ * Display name. If omitted, a unique name is generated from the stack,
17
+ * stage and logical ID. Renamed in place.
18
+ */
19
+ name?: string;
20
+ }
21
+ export interface WebhookAttributes {
22
+ /** Boat's webhook id (`wh_…`). */
23
+ webhookId: string;
24
+ name: string;
25
+ url: string;
26
+ events: WebhookEvent[];
27
+ /**
28
+ * The secret Boat signs deliveries with. Boat shows it once, at creation;
29
+ * Alchemy keeps it in state.
30
+ */
31
+ secret: Redacted.Redacted<string>;
32
+ }
33
+ export interface Webhook extends Resource<"Boat.Webhook", WebhookProps, WebhookAttributes, never, Providers> {
34
+ }
35
+ /**
36
+ * A Boat webhook: Boat POSTs sandbox lifecycle events (`sandbox.ready`,
37
+ * `sandbox.error`, `sandbox.archived`, `sandbox.hydrated`,
38
+ * `sandbox.degraded`, `sandbox.recovered`) to your endpoint, signed with
39
+ * `secret`.
40
+ *
41
+ * ### Receiving sandbox events
42
+ * **Example:** Deliver events to a Worker
43
+ * ```typescript
44
+ * const receiver = yield* Receiver;
45
+ * const hook = yield* Boat.Webhook("Events", {
46
+ * url: Output.interpolate`${receiver.url}/boat`,
47
+ * events: ["sandbox.ready", "sandbox.archived"],
48
+ * });
49
+ * ```
50
+ *
51
+ * @resource
52
+ * @product Webhook
53
+ */
54
+ export declare const Webhook: import("alchemy/Resource").ResourceClass<Webhook>;
55
+ export declare const WebhookProvider: () => import("effect/Layer").Layer<Provider.Provider<Webhook>, never, import("alchemy/Stack").Stack | import("alchemy/Stage").Stage | boat.BoatOpContext>;
56
+ //# sourceMappingURL=Webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Webhook.d.ts","sources":["../src/Webhook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,gCAAgC,CAAC;AAEvD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,+CAA+C;AAC/C,MAAM,MAAM,YAAY,GACpB,eAAe,GACf,kBAAkB,GAClB,eAAe,GACf,kBAAkB,GAClB,kBAAkB,GAClB,mBAAmB,CAAC;AAExB,gDAAgD;AAChD,eAAO,MAAM,kBAAkB,EAAE,aAAa,CAAC,YAAY,CAO1D,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,mFAAmF;IACnF,GAAG,EAAE,MAAM,CAAC;IACZ,+CAA+C;IAC/C,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB;;;OAGG;IACH,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,OAAQ,SAAQ,QAAQ,CACvC,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,SAAS,CACV;CAAG;AAEJ;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,OAAO,mDAAoC,CAAC;AAyBzD,eAAO,MAAM,eAAe,2JAyDzB,CAAC"}
package/lib/Webhook.js ADDED
@@ -0,0 +1,98 @@
1
+ import * as boat from "@distilled.cloud/boat-dev/boat";
2
+ import { Unowned } from "alchemy/AdoptPolicy";
3
+ import * as Provider from "alchemy/Provider";
4
+ import { Resource } from "alchemy/Resource";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Redacted from "effect/Redacted";
7
+ import { boatName, isGeneratedName } from "./internal/Ownership.js";
8
+ /** Every sandbox lifecycle event Boat sends. */
9
+ export const ALL_SANDBOX_EVENTS = [
10
+ "sandbox.ready",
11
+ "sandbox.hydrated",
12
+ "sandbox.error",
13
+ "sandbox.archived",
14
+ "sandbox.degraded",
15
+ "sandbox.recovered",
16
+ ];
17
+ /**
18
+ * A Boat webhook: Boat POSTs sandbox lifecycle events (`sandbox.ready`,
19
+ * `sandbox.error`, `sandbox.archived`, `sandbox.hydrated`,
20
+ * `sandbox.degraded`, `sandbox.recovered`) to your endpoint, signed with
21
+ * `secret`.
22
+ *
23
+ * ### Receiving sandbox events
24
+ * **Example:** Deliver events to a Worker
25
+ * ```typescript
26
+ * const receiver = yield* Receiver;
27
+ * const hook = yield* Boat.Webhook("Events", {
28
+ * url: Output.interpolate`${receiver.url}/boat`,
29
+ * events: ["sandbox.ready", "sandbox.archived"],
30
+ * });
31
+ * ```
32
+ *
33
+ * @resource
34
+ * @product Webhook
35
+ */
36
+ export const Webhook = Resource("Boat.Webhook");
37
+ const redact = (value) => Redacted.isRedacted(value) ? value : Redacted.make(value);
38
+ const observeWebhook = (webhookId) => boat.getWebhook({ webhookId }).pipe(Effect.map((got) => got.webhook), Effect.catchTag("NotFound", () => Effect.undefined));
39
+ const toAttributes = (webhook, secret) => ({
40
+ webhookId: webhook.id,
41
+ name: webhook.name ?? "",
42
+ url: webhook.url,
43
+ events: [...webhook.events],
44
+ secret,
45
+ });
46
+ const sameEvents = (a, b) => JSON.stringify([...a].sort()) === JSON.stringify([...b].sort());
47
+ export const WebhookProvider = () => Provider.effect(Webhook, Effect.gen(function* () {
48
+ return {
49
+ stables: ["webhookId"],
50
+ read: Effect.fn(function* ({ olds, output }) {
51
+ // Boat shows the signing secret only at creation, so a webhook is
52
+ // only usable through state.
53
+ if (output === undefined)
54
+ return undefined;
55
+ const webhook = yield* observeWebhook(output.webhookId);
56
+ if (webhook === undefined)
57
+ return undefined;
58
+ const attrs = toAttributes(webhook, output.secret);
59
+ return olds !== undefined || isGeneratedName(webhook.name) ? attrs : Unowned(attrs);
60
+ }),
61
+ reconcile: Effect.fn(function* ({ id, news, output, session }) {
62
+ const name = yield* boatName(id, news.name ?? output?.name, 100);
63
+ // Observe
64
+ let webhook = output === undefined ? undefined : yield* observeWebhook(output.webhookId);
65
+ // Ensure
66
+ if (webhook === undefined || output === undefined) {
67
+ yield* session.note(`Creating webhook ${name}...`);
68
+ const created = yield* boat.createWebhook({ name, url: news.url, events: news.events });
69
+ return toAttributes(created.webhook, redact(created.secret));
70
+ }
71
+ // Sync — name, URL and events, against what Boat reports.
72
+ if (webhook.name !== name ||
73
+ webhook.url !== news.url ||
74
+ !sameEvents(webhook.events, news.events)) {
75
+ const updated = yield* boat.updateWebhook({
76
+ webhookId: webhook.id,
77
+ name,
78
+ url: news.url,
79
+ events: news.events,
80
+ });
81
+ webhook = updated.webhook;
82
+ }
83
+ return toAttributes(webhook, output.secret);
84
+ }),
85
+ delete: Effect.fn(function* ({ output }) {
86
+ yield* boat
87
+ .deleteWebhook({ webhookId: output.webhookId })
88
+ .pipe(Effect.catchTag("NotFound", () => Effect.void));
89
+ }),
90
+ list: Effect.fn(function* () {
91
+ const listed = yield* boat.listWebhooks({});
92
+ return listed.webhooks
93
+ .filter((webhook) => isGeneratedName(webhook.name))
94
+ .map((webhook) => toAttributes(webhook, Redacted.make("")));
95
+ }),
96
+ };
97
+ }));
98
+ //# sourceMappingURL=Webhook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Webhook.js","sources":["../src/Webhook.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ export * from "./AiProxy.ts";
2
+ export * from "./ApiKey.ts";
3
+ export * from "./AuthProvider.ts";
4
+ export * from "./ConnectSandbox.ts";
5
+ export * from "./ConnectSandboxHttp.ts";
6
+ export * from "./CreateSandbox.ts";
7
+ export * from "./CreateSandboxHttp.ts";
8
+ export * from "./CredentialProfile.ts";
9
+ export * from "./Credentials.ts";
10
+ export * from "./Environment.ts";
11
+ export * from "./Errors.ts";
12
+ export * from "./Providers.ts";
13
+ export * from "./Sandbox.ts";
14
+ export * from "./SandboxClient.ts";
15
+ export * from "./SandboxEvents.ts";
16
+ export * from "./SandboxEventsLive.ts";
17
+ export * from "./Snapshot.ts";
18
+ export * from "./Webhook.ts";
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
package/lib/index.js ADDED
@@ -0,0 +1,19 @@
1
+ export * from "./AiProxy.js";
2
+ export * from "./ApiKey.js";
3
+ export * from "./AuthProvider.js";
4
+ export * from "./ConnectSandbox.js";
5
+ export * from "./ConnectSandboxHttp.js";
6
+ export * from "./CreateSandbox.js";
7
+ export * from "./CreateSandboxHttp.js";
8
+ export * from "./CredentialProfile.js";
9
+ export * from "./Credentials.js";
10
+ export * from "./Environment.js";
11
+ export * from "./Errors.js";
12
+ export * from "./Providers.js";
13
+ export * from "./Sandbox.js";
14
+ export * from "./SandboxClient.js";
15
+ export * from "./SandboxEvents.js";
16
+ export * from "./SandboxEventsLive.js";
17
+ export * from "./Snapshot.js";
18
+ export * from "./Webhook.js";
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { PropsInput } from "alchemy/Input";
2
+ import * as Output from "alchemy/Output";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Redacted from "effect/Redacted";
5
+ import type { Providers } from "../Providers.ts";
6
+ type Leaf = string | number | boolean | bigint | symbol | null | undefined | Redacted.Redacted<any> | Uint8Array | Date | ((...args: any[]) => any);
7
+ /**
8
+ * A prop value, or the declaration of a resource that has it: `template: Node`
9
+ * instead of `template: yield* Node`, at any depth.
10
+ */
11
+ export type Declared<T> = T extends Leaf ? T : T extends Output.Output<any> ? T : T extends ReadonlyArray<infer U> ? Declared<U>[] | Effect.Effect<T, never, Providers> : {
12
+ [K in keyof T]: Declared<T[K]>;
13
+ } | Effect.Effect<T, never, Providers>;
14
+ /** Props where any value may be a resource declaration. */
15
+ export type DeclaredProps<P> = {
16
+ [K in keyof PropsInput<P>]: Declared<PropsInput<P>[K]>;
17
+ };
18
+ /**
19
+ * A declaration still to be yielded: an Effect that isn't an Output, a
20
+ * resource, or a Config (which the engine resolves as a prop value).
21
+ */
22
+ export declare const isDeclaration: (value: unknown) => value is Effect.Effect<unknown, never, Providers>;
23
+ /** Yield every declaration in `value`, at any depth. */
24
+ export declare const resolveDeclarations: (value: unknown) => Effect.Effect<unknown, never, Providers>;
25
+ /**
26
+ * Let a resource's props hold declarations of other resources, which are
27
+ * yielded when the resource is: `Boat.Sandbox("Box", { snapshot: Golden })`.
28
+ * Props without declarations are passed through unchanged.
29
+ */
30
+ export declare const declarable: <R extends {
31
+ readonly Props: any;
32
+ }>() => <C extends object>(resource: C) => C & ((id: string, props: DeclaredProps<R["Props"]>) => Effect.Effect<R, never, Providers>);
33
+ export {};
34
+ //# sourceMappingURL=Declared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Declared.d.ts","sources":["../../src/internal/Declared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAGzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,KAAK,IAAI,GACL,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,MAAM,GACN,IAAI,GACJ,SAAS,GACT,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,GACtB,UAAU,GACV,IAAI,GACJ,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC;AAE9B;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,GACpC,CAAC,GACD,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,GAC1B,CAAC,GACD,CAAC,SAAS,aAAa,CAAC,MAAM,CAAC,CAAC,GAC9B,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,GAClD;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;AAEhF,2DAA2D;AAC3D,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC;AAO1F;;;GAGG;AACH,eAAO,MAAM,aAAa,UAAW,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAItE,CAAC;AAO1B,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,UAAW,OAAO,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAY7D,CAAC;AAEhC;;;;GAIG;AACH,eAAO,MAAM,UAAU,GACpB,CAAC,SAAS;IAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAA;CAAE,QACjC,CAAC,SAAS,MAAM,YACL,CAAC,KACV,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAM/E,CAAC"}
@@ -0,0 +1,38 @@
1
+ import * as Output from "alchemy/Output";
2
+ import { isResource } from "alchemy/Resource";
3
+ import * as Config from "effect/Config";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Redacted from "effect/Redacted";
6
+ const isPlain = (value) => typeof value === "object" &&
7
+ value !== null &&
8
+ (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
9
+ /**
10
+ * A declaration still to be yielded: an Effect that isn't an Output, a
11
+ * resource, or a Config (which the engine resolves as a prop value).
12
+ */
13
+ export const isDeclaration = (value) => Effect.isEffect(value) &&
14
+ !Output.isOutput(value) &&
15
+ !isResource(value) &&
16
+ !Config.isConfig(value);
17
+ const hasDeclarations = (value) => isDeclaration(value) ||
18
+ (Array.isArray(value) && value.some(hasDeclarations)) ||
19
+ (isPlain(value) && !isResource(value) && Object.values(value).some(hasDeclarations));
20
+ /** Yield every declaration in `value`, at any depth. */
21
+ export const resolveDeclarations = (value) => isDeclaration(value)
22
+ ? value
23
+ : Array.isArray(value)
24
+ ? Effect.forEach(value, resolveDeclarations)
25
+ : isPlain(value) && !isResource(value) && hasDeclarations(value)
26
+ ? Effect.map(Effect.forEach(Object.entries(value), ([key, entry]) => Effect.map(resolveDeclarations(entry), (resolved) => [key, resolved])), Object.fromEntries)
27
+ : Effect.succeed(value);
28
+ /**
29
+ * Let a resource's props hold declarations of other resources, which are
30
+ * yielded when the resource is: `Boat.Sandbox("Box", { snapshot: Golden })`.
31
+ * Props without declarations are passed through unchanged.
32
+ */
33
+ export const declarable = () => (resource) => new Proxy(resource, {
34
+ apply: (target, self, args) => typeof args[0] === "string" && hasDeclarations(args[1])
35
+ ? Reflect.apply(target, self, [args[0], resolveDeclarations(args[1])])
36
+ : Reflect.apply(target, self, args),
37
+ });
38
+ //# sourceMappingURL=Declared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Declared.js","sources":["../../src/internal/Declared.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AA+B5C,MAAM,OAAO,GAAG,CAAC,KAAc,EAAoC,EAAE,CACnE,OAAO,KAAK,KAAK,QAAQ;IACzB,KAAK,KAAK,IAAI;IACd,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;AAE/F;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAc,EAAqD,EAAE,CACjG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;IACtB,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvB,CAAC,UAAU,CAAC,KAAK,CAAC;IAClB,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAE1B,MAAM,eAAe,GAAG,CAAC,KAAc,EAAW,EAAE,CAClD,aAAa,CAAC,KAAK,CAAC;IACpB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACrD,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;AAEvF,wDAAwD;AACxD,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,KAAc,EAA4C,EAAE,CAC9F,aAAa,CAAC,KAAK,CAAC;IAClB,CAAC,CAAC,KAAK;IACP,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACpB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,mBAAmB,CAAC;QAC5C,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC;YAC9D,CAAC,CAAC,MAAM,CAAC,GAAG,CACR,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CACrD,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAU,CAAC,CAC/E,EACD,MAAM,CAAC,WAAW,CACnB;YACH,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEhC;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GACrB,GAAsC,EAAE,CACxC,CACE,QAAW,EACiF,EAAE,CAC9F,IAAI,KAAK,CAAC,QAAiD,EAAE;IAC3D,KAAK,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,IAAe,EAAE,EAAE,CACvC,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrD,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC;CACxC,CAAU,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { Credentials } from "@distilled.cloud/boat-dev/Credentials";
2
+ import * as Effect from "effect/Effect";
3
+ /**
4
+ * The stack's org (`Boat.providers({ org })`, or the profile's), or
5
+ * `undefined` for none, and for `personal`: both mean what is yours.
6
+ * `/environments` routes ignore `X-Boat-Org`, so the org the protocol sends
7
+ * on every other call is passed there as the `org` query.
8
+ */
9
+ export declare const stackOrg: Effect.Effect<string | undefined, import("@distilled.cloud/boat-dev").ConfigError, Credentials>;
10
+ /** The same organization, however it was spelled (Boat ignores case). */
11
+ export declare const sameOrg: (a: string | undefined, b: string | undefined) => boolean;
12
+ /** The `org` query parameter, when there is an org. */
13
+ export declare const inOrg: (org: string | undefined) => {
14
+ org: string;
15
+ } | {};
16
+ /** Whether an account (a person or an organization) is the one `org` names. */
17
+ export declare const isOrgAccount: (account: {
18
+ id: string;
19
+ kind: "personal" | "org" | (string & {});
20
+ name: string;
21
+ }, org: string | undefined) => boolean;
22
+ //# sourceMappingURL=Org.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Org.d.ts","sources":["../../src/internal/Org.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACpE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,iGAInB,CAAC;AAEH,yEAAyE;AACzE,eAAO,MAAM,OAAO,MAAO,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,SAAS,YACG,CAAC;AAExE,uDAAuD;AACvD,eAAO,MAAM,KAAK,QAAS,MAAM,GAAG,SAAS;;MAAyB,CAAC;AAEvE,+EAA+E;AAC/E,eAAO,MAAM,YAAY,YACd;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,GAAG,KAAK,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,OAC1E,MAAM,GAAG,SAAS,YAKqD,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { Credentials } from "@distilled.cloud/boat-dev/Credentials";
2
+ import * as Effect from "effect/Effect";
3
+ /**
4
+ * The stack's org (`Boat.providers({ org })`, or the profile's), or
5
+ * `undefined` for none, and for `personal`: both mean what is yours.
6
+ * `/environments` routes ignore `X-Boat-Org`, so the org the protocol sends
7
+ * on every other call is passed there as the `org` query.
8
+ */
9
+ export const stackOrg = Effect.gen(function* () {
10
+ const { org } = yield* yield* Credentials;
11
+ const named = org?.trim();
12
+ return named && named.toLowerCase() !== "personal" ? named : undefined;
13
+ });
14
+ /** The same organization, however it was spelled (Boat ignores case). */
15
+ export const sameOrg = (a, b) => (a?.toLowerCase() ?? "personal") === (b?.toLowerCase() ?? "personal");
16
+ /** The `org` query parameter, when there is an org. */
17
+ export const inOrg = (org) => (org ? { org } : {});
18
+ /** Whether an account (a person or an organization) is the one `org` names. */
19
+ export const isOrgAccount = (account, org) => org === undefined
20
+ ? account.kind === "personal"
21
+ : account.kind === "org" &&
22
+ (account.id === org || account.name.toLowerCase() === org.toLowerCase());
23
+ //# sourceMappingURL=Org.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Org.js","sources":["../../src/internal/Org.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACpE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC1C,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC;IAC1C,MAAM,KAAK,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC;IAC1B,OAAO,KAAK,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACzE,CAAC,CAAC,CAAC;AAEH,yEAAyE;AACzE,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAqB,EAAE,CAAqB,EAAE,EAAE,CACtE,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,UAAU,CAAC,CAAC;AAExE,uDAAuD;AACvD,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,GAAuB,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAEvE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,OAA+E,EAC/E,GAAuB,EACvB,EAAE,CACF,GAAG,KAAK,SAAS;IACf,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,UAAU;IAC7B,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,KAAK;QACtB,CAAC,OAAO,CAAC,EAAE,KAAK,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC"}
@@ -0,0 +1,29 @@
1
+ import * as Effect from "effect/Effect";
2
+ /** Labels as Boat returns them. */
3
+ export type Labels = Readonly<Record<string, string | undefined>> | null | undefined;
4
+ /** The prefix Alchemy's ownership labels share. */
5
+ export declare const ALCHEMY_LABEL_PREFIX = "alchemy.";
6
+ /** `alchemy::stack` → `alchemy.stack`. */
7
+ export declare const toLabelKey: (key: string) => string;
8
+ /** This resource's ownership labels: `alchemy.stack`, `alchemy.stage`, `alchemy.id`. */
9
+ export declare const ownershipLabels: (id: string) => Effect.Effect<Record<string, string>, never, import("alchemy/Stack").Stack | import("alchemy/Stage").Stage>;
10
+ /** Whether labels carry this resource's ownership labels. */
11
+ export declare const hasOwnershipLabels: (id: string, labels: Labels) => Effect.Effect<boolean, never, import("alchemy/Stack").Stack | import("alchemy/Stage").Stage>;
12
+ /** Whether labels were set by any Alchemy stack or binding (for nuke). */
13
+ export declare const hasAnyAlchemyLabel: (labels: Labels) => boolean;
14
+ /** Labels without Alchemy's own. */
15
+ export declare const userLabels: (labels: Labels) => Record<string, string>;
16
+ /** `key=value` filters for `listSandboxes`. */
17
+ export declare const labelFilter: (labels: Readonly<Record<string, string>>) => string[];
18
+ /**
19
+ * A Boat name: the user's, or one generated from the stack, stage and
20
+ * logical id. Lowercase letters, digits and dashes fit every Boat name rule
21
+ * (snapshot names allow nothing else).
22
+ */
23
+ export declare const boatName: (id: string, name: string | undefined, maxLength?: number) => Effect.Effect<string, never, import("alchemy/ResourceContext").ResourceContext | import("alchemy/Stack").Stack | import("alchemy/Stage").Stage>;
24
+ /**
25
+ * Whether a name looks generated by `createPhysicalName` (lowercase, ending
26
+ * in a base32 instance suffix). `list` reports only these, for nuke.
27
+ */
28
+ export declare const isGeneratedName: (name: string | null | undefined) => boolean;
29
+ //# sourceMappingURL=Ownership.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Ownership.d.ts","sources":["../../src/internal/Ownership.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAIxC,mCAAmC;AACnC,MAAM,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAErF,mDAAmD;AACnD,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAE/C,0CAA0C;AAC1C,eAAO,MAAM,UAAU,QAAS,MAAM,WAG7B,CAAC;AAEV,wFAAwF;AACxF,eAAO,MAAM,eAAe,6HAK1B,CAAC;AAEH,6DAA6D;AAC7D,eAAO,MAAM,kBAAkB,8HAG7B,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB,WAAY,MAAM,YACoC,CAAC;AAEtF,oCAAoC;AACpC,eAAO,MAAM,UAAU,WAAY,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAM9D,CAAC;AAEJ,+CAA+C;AAC/C,eAAO,MAAM,WAAW,WAAY,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,aACH,CAAC;AAElE;;;;GAIG;AACH,eAAO,MAAM,QAAQ,OAAQ,MAAM,QAAQ,MAAM,GAAG,SAAS,wKAGe,CAAC;AAE7E;;;GAGG;AACH,eAAO,MAAM,eAAe,SAAU,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,OACa,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Naming and ownership helpers shared by every Boat resource and binding.
3
+ *
4
+ * Boat sandboxes carry labels, set once at create. Label keys can't hold
5
+ * `:`, so Alchemy's `alchemy::stack` / `alchemy::stage` / `alchemy::id`
6
+ * ownership tags are written as `alchemy.stack` / `alchemy.stage` /
7
+ * `alchemy.id`. Environments, snapshots, webhooks and API keys carry only a
8
+ * name, so a resource whose name the engine generated is recognised as ours
9
+ * by that name; an explicit name found without prior state is `Unowned`.
10
+ */
11
+ import { createPhysicalName } from "alchemy/PhysicalName";
12
+ import { createInternalTags } from "alchemy/Tags";
13
+ import * as Effect from "effect/Effect";
14
+ const INTERNAL_PREFIX = "alchemy::";
15
+ /** The prefix Alchemy's ownership labels share. */
16
+ export const ALCHEMY_LABEL_PREFIX = "alchemy.";
17
+ /** `alchemy::stack` → `alchemy.stack`. */
18
+ export const toLabelKey = (key) => key.startsWith(INTERNAL_PREFIX)
19
+ ? `${ALCHEMY_LABEL_PREFIX}${key.slice(INTERNAL_PREFIX.length)}`
20
+ : key;
21
+ /** This resource's ownership labels: `alchemy.stack`, `alchemy.stage`, `alchemy.id`. */
22
+ export const ownershipLabels = Effect.fn(function* (id) {
23
+ const tags = yield* createInternalTags(id);
24
+ return Object.fromEntries(Object.entries(tags).map(([key, value]) => [toLabelKey(key), value]));
25
+ });
26
+ /** Whether labels carry this resource's ownership labels. */
27
+ export const hasOwnershipLabels = Effect.fn(function* (id, labels) {
28
+ const expected = yield* ownershipLabels(id);
29
+ return Object.entries(expected).every(([key, value]) => labels?.[key] === value);
30
+ });
31
+ /** Whether labels were set by any Alchemy stack or binding (for nuke). */
32
+ export const hasAnyAlchemyLabel = (labels) => labels !== undefined && labels !== null && `${ALCHEMY_LABEL_PREFIX}stack` in labels;
33
+ /** Labels without Alchemy's own. */
34
+ export const userLabels = (labels) => Object.fromEntries(Object.entries(labels ?? {}).filter((entry) => entry[1] !== undefined && !entry[0].startsWith(ALCHEMY_LABEL_PREFIX)));
35
+ /** `key=value` filters for `listSandboxes`. */
36
+ export const labelFilter = (labels) => Object.entries(labels).map(([key, value]) => `${key}=${value}`);
37
+ /**
38
+ * A Boat name: the user's, or one generated from the stack, stage and
39
+ * logical id. Lowercase letters, digits and dashes fit every Boat name rule
40
+ * (snapshot names allow nothing else).
41
+ */
42
+ export const boatName = (id, name, maxLength = 63) => name !== undefined
43
+ ? Effect.succeed(name)
44
+ : createPhysicalName({ id, lowercase: true, maxLength, delimiter: "-" });
45
+ /**
46
+ * Whether a name looks generated by `createPhysicalName` (lowercase, ending
47
+ * in a base32 instance suffix). `list` reports only these, for nuke.
48
+ */
49
+ export const isGeneratedName = (name) => typeof name === "string" && /^[a-z0-9][a-z0-9_-]*-[a-z2-7]{8,16}$/.test(name);
50
+ //# sourceMappingURL=Ownership.js.map