@wtfalch/foundry 0.45.1 → 0.46.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 (60) hide show
  1. package/dist/index.d.ts +4 -2
  2. package/dist/index.js +2 -1
  3. package/dist/org.d.ts +37 -0
  4. package/dist/org.js +135 -10
  5. package/dist/plan/agora.d.ts +14 -0
  6. package/dist/plan/agora.js +21 -0
  7. package/dist/plan/backup-gate.d.ts +15 -0
  8. package/dist/plan/backup-gate.js +38 -0
  9. package/dist/plan/boule-owners-http.d.ts +12 -0
  10. package/dist/plan/boule-owners-http.js +59 -0
  11. package/dist/plan/boule.d.ts +38 -2
  12. package/dist/plan/boule.js +72 -1
  13. package/dist/plan/company-backups.d.ts +7 -1
  14. package/dist/plan/company-backups.js +42 -2
  15. package/dist/plan/cookie-domain.d.ts +41 -0
  16. package/dist/plan/cookie-domain.js +71 -0
  17. package/dist/plan/desktop-auth.d.ts +58 -0
  18. package/dist/plan/desktop-auth.js +144 -0
  19. package/dist/plan/desktop-callback.d.ts +26 -0
  20. package/dist/plan/desktop-callback.js +59 -0
  21. package/dist/plan/diff.d.ts +13 -4
  22. package/dist/plan/diff.js +272 -84
  23. package/dist/plan/executor.d.ts +136 -6
  24. package/dist/plan/executor.js +725 -98
  25. package/dist/plan/health-gaps.js +5 -0
  26. package/dist/plan/index.d.ts +3 -0
  27. package/dist/plan/index.js +2 -0
  28. package/dist/plan/job-app.d.ts +23 -0
  29. package/dist/plan/job-app.js +41 -0
  30. package/dist/plan/live.d.ts +6 -0
  31. package/dist/plan/live.js +16 -0
  32. package/dist/plan/login-host.d.ts +28 -0
  33. package/dist/plan/login-host.js +36 -0
  34. package/dist/plan/org-names.js +9 -1
  35. package/dist/plan/org-postgres-job.d.ts +22 -0
  36. package/dist/plan/org-postgres-job.js +91 -0
  37. package/dist/plan/org-postgres-migrate.d.ts +115 -0
  38. package/dist/plan/org-postgres-migrate.js +466 -0
  39. package/dist/plan/org-postgres.d.ts +90 -0
  40. package/dist/plan/org-postgres.js +293 -0
  41. package/dist/plan/plan-apply.d.ts +30 -2
  42. package/dist/plan/plan-apply.js +104 -7
  43. package/dist/plan/platform-diff.d.ts +45 -0
  44. package/dist/plan/platform-diff.js +159 -4
  45. package/dist/plan/platform-org.d.ts +2 -0
  46. package/dist/plan/platform-org.js +4 -0
  47. package/dist/plan/platform-providers.d.ts +12 -2
  48. package/dist/plan/platform-providers.js +279 -9
  49. package/dist/plan/post-logout-uris.d.ts +22 -0
  50. package/dist/plan/post-logout-uris.js +43 -0
  51. package/dist/plan/providers-readers.d.ts +10 -1
  52. package/dist/plan/providers-readers.js +41 -1
  53. package/dist/plan/sign-in-bindings.d.ts +4 -0
  54. package/dist/plan/sign-in-bindings.js +10 -0
  55. package/dist/plan/sign-in-org.d.ts +19 -6
  56. package/dist/plan/sign-in-org.js +35 -10
  57. package/dist/plan/sign-in.d.ts +12 -5
  58. package/dist/plan/sign-in.js +12 -5
  59. package/dist/plan/signin-db-move.js +10 -34
  60. package/package.json +5 -5
@@ -36,6 +36,16 @@ export async function editSignInBindings(coolify, input) {
36
36
  ...doc.bindings.filter((b) => !touched.has(b.host)),
37
37
  ...(input.upsert ?? []).map((b) => ({ ...b, clientIds: [...b.clientIds].sort() })),
38
38
  ].sort((a, b) => (a.host < b.host ? -1 : 1));
39
+ const orgs = input.clientOrganizations;
40
+ if (orgs && (Object.keys(orgs.upsert ?? {}).length > 0 || (orgs.remove ?? []).length > 0)) {
41
+ const merged = { ...doc.clientOrganizations, ...orgs.upsert };
42
+ const gone = new Set(orgs.remove ?? []);
43
+ const sorted = Object.fromEntries(Object.entries(merged)
44
+ .filter(([id]) => !gone.has(id))
45
+ .sort(([a], [b]) => (a < b ? -1 : 1)));
46
+ // An empty map is left out of the document, as it was before the first write.
47
+ doc.clientOrganizations = Object.keys(sorted).length > 0 ? sorted : undefined;
48
+ }
39
49
  const next = JSON.stringify(doc);
40
50
  if (live?.value === next)
41
51
  return;
@@ -7,6 +7,12 @@ export declare const ZITADEL_ORG_DOMAIN_SCOPE_KEY = "zitadel_org_domain";
7
7
  * names another org, the company plan holds the close step and no owner invite.
8
8
  */
9
9
  export declare const ZITADEL_ORG_REGISTRATION_CLOSED_SCOPE_KEY = "zitadel_org_registration_closed";
10
+ /**
11
+ * Scope id: the id of the customers org whose self-registration foundry
12
+ * closed, recorded only after `closeSelfRegistration` returned. Foundry closes
13
+ * once and never opens: a company that turns sign-up on keeps it on.
14
+ */
15
+ export declare const ZITADEL_CUSTOMERS_REGISTRATION_CLOSED_SCOPE_KEY = "zitadel_customers_registration_closed";
10
16
  /** Closes self-registration on the org (idempotent) and records it under `scopeKey`, in that order. */
11
17
  export declare function closeSignInOrgRegistration(zitadel: Pick<ZitadelPort, 'closeSelfRegistration'>, orgId: string, recordScopeId: (key: string, id: string) => void, scopeKey?: string): Promise<void>;
12
18
  /** Scope id: the domain the sign-in server still refused to make primary after foundry set its domain policy and added the domain again. Set instead of failing; the planner reports it and plans the domain again. */
@@ -27,12 +33,17 @@ export declare function signInOrgDomain(org: {
27
33
  slug: string;
28
34
  domain: string | null;
29
35
  }): string;
36
+ /** The fixed text for a domain the sign-in server already holds on another organisation. */
37
+ export declare function signInDomainTakenText(domain: string): string;
30
38
  /**
31
- * Sets the server's domain policy ({@link SIGN_IN_DOMAIN_POLICY}), adds
32
- * `domain` to the ZITADEL org and makes it primary. Every call is idempotent.
33
- * A domain added while the policy still wanted proof stays unverified, so it
34
- * is removed and added again. A failure after the add leaves the domain on
35
- * the org but not primary, and nothing is recorded, so the next run repeats.
39
+ * Adds `domain` to the ZITADEL org, sets the server's domain policy
40
+ * ({@link SIGN_IN_DOMAIN_POLICY}) and makes the domain primary. Every call is
41
+ * idempotent. The add comes first, so foundry holds the domain before the
42
+ * policy stops asking for proof of ownership. A domain added while the policy
43
+ * still wanted proof stays unverified, so it is removed and added again (the
44
+ * remove-and-add gap: the domain is off the org for up to five seconds; known,
45
+ * left as is). A failure after the add leaves the domain on the org but not
46
+ * primary, and nothing is recorded, so the next run repeats.
36
47
  */
37
48
  export declare function registerSignInDomain(zitadel: Pick<ZitadelPort, 'ensureDomainPolicy' | 'addOrgDomain' | 'removeOrgDomain' | 'setPrimaryOrgDomain'>, orgId: string, domain: string, sleep?: (ms: number) => Promise<void>): Promise<void>;
38
49
  /**
@@ -41,7 +52,7 @@ export declare function registerSignInDomain(zitadel: Pick<ZitadelPort, 'ensureD
41
52
  * `blockedScopeKey` and returns `false` instead of throwing on every apply.
42
53
  * Any other failure throws. Returns `true` when the domain is primary.
43
54
  */
44
- export declare function registerSignInDomainOrBlock(zitadel: Pick<ZitadelPort, 'ensureDomainPolicy' | 'addOrgDomain' | 'removeOrgDomain' | 'setPrimaryOrgDomain'>, orgId: string, domain: string, recordScopeId: (key: string, id: string) => void, blockedScopeKey?: string): Promise<boolean>;
55
+ export declare function registerSignInDomainOrBlock(zitadel: Pick<ZitadelPort, 'ensureDomainPolicy' | 'addOrgDomain' | 'removeOrgDomain' | 'setPrimaryOrgDomain'>, orgId: string, domain: string, recordScopeId: (key: string, id: string) => void, blockedScopeKey?: string, sleep?: (ms: number) => Promise<void>): Promise<boolean>;
45
56
  export interface EnsureSignInOrgOptions {
46
57
  zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'ensureDomainPolicy' | 'addOrgDomain' | 'removeOrgDomain' | 'setPrimaryOrgDomain' | 'closeSelfRegistration'>;
47
58
  /** Where the successful close is recorded (the org id). Absent: not recorded; the caller closes on every run. */
@@ -60,6 +71,8 @@ export interface EnsureSignInOrgOptions {
60
71
  orgScopeKey?: string;
61
72
  /** Defaults to {@link ZITADEL_ORG_DOMAIN_SCOPE_KEY}. */
62
73
  domainScopeKey?: string;
74
+ /** Waits between tries of the domain calls; a test passes a no-op. */
75
+ sleep?: (ms: number) => Promise<void>;
63
76
  }
64
77
  /**
65
78
  * Finds or creates the company's sign-in organisation by name, registers
@@ -6,6 +6,12 @@ export const ZITADEL_ORG_DOMAIN_SCOPE_KEY = 'zitadel_org_domain';
6
6
  * names another org, the company plan holds the close step and no owner invite.
7
7
  */
8
8
  export const ZITADEL_ORG_REGISTRATION_CLOSED_SCOPE_KEY = 'zitadel_org_registration_closed';
9
+ /**
10
+ * Scope id: the id of the customers org whose self-registration foundry
11
+ * closed, recorded only after `closeSelfRegistration` returned. Foundry closes
12
+ * once and never opens: a company that turns sign-up on keeps it on.
13
+ */
14
+ export const ZITADEL_CUSTOMERS_REGISTRATION_CLOSED_SCOPE_KEY = 'zitadel_customers_registration_closed';
9
15
  /** Closes self-registration on the org (idempotent) and records it under `scopeKey`, in that order. */
10
16
  export async function closeSignInOrgRegistration(zitadel, orgId, recordScopeId, scopeKey = ZITADEL_ORG_REGISTRATION_CLOSED_SCOPE_KEY) {
11
17
  await zitadel.closeSelfRegistration(orgId);
@@ -51,16 +57,35 @@ async function untilListed(work, retry, sleep) {
51
57
  }
52
58
  }
53
59
  }
60
+ /** The fixed text for a domain the sign-in server already holds on another organisation. */
61
+ export function signInDomainTakenText(domain) {
62
+ return `the sign-in domain ${domain} already exists on another organisation of the sign-in server, so foundry cannot add it; remove it there and apply again`;
63
+ }
64
+ /** Adds the domain; an "already exists" answer becomes {@link signInDomainTakenText}, which holds no cause. */
65
+ async function addDomain(zitadel, orgId, domain) {
66
+ try {
67
+ await zitadel.addOrgDomain(orgId, domain);
68
+ }
69
+ catch (error) {
70
+ if (error instanceof Error && /already[ .]?exists/i.test(error.message)) {
71
+ throw new Error(signInDomainTakenText(domain));
72
+ }
73
+ throw error;
74
+ }
75
+ }
54
76
  /**
55
- * Sets the server's domain policy ({@link SIGN_IN_DOMAIN_POLICY}), adds
56
- * `domain` to the ZITADEL org and makes it primary. Every call is idempotent.
57
- * A domain added while the policy still wanted proof stays unverified, so it
58
- * is removed and added again. A failure after the add leaves the domain on
59
- * the org but not primary, and nothing is recorded, so the next run repeats.
77
+ * Adds `domain` to the ZITADEL org, sets the server's domain policy
78
+ * ({@link SIGN_IN_DOMAIN_POLICY}) and makes the domain primary. Every call is
79
+ * idempotent. The add comes first, so foundry holds the domain before the
80
+ * policy stops asking for proof of ownership. A domain added while the policy
81
+ * still wanted proof stays unverified, so it is removed and added again (the
82
+ * remove-and-add gap: the domain is off the org for up to five seconds; known,
83
+ * left as is). A failure after the add leaves the domain on the org but not
84
+ * primary, and nothing is recorded, so the next run repeats.
60
85
  */
61
86
  export async function registerSignInDomain(zitadel, orgId, domain, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
87
+ await addDomain(zitadel, orgId, domain);
62
88
  await zitadel.ensureDomainPolicy(SIGN_IN_DOMAIN_POLICY);
63
- await zitadel.addOrgDomain(orgId, domain);
64
89
  // The sign-in server lists a domain a moment after it accepts the add, and
65
90
  // `setPrimaryOrgDomain` reads that list first: ask again, for five seconds.
66
91
  try {
@@ -75,7 +100,7 @@ export async function registerSignInDomain(zitadel, orgId, domain, sleep = (ms)
75
100
  // Until the server lists the removal, the add sees the old unverified
76
101
  // domain and skips: both answers are a moment late, so ask again.
77
102
  await untilListed(async () => {
78
- await zitadel.addOrgDomain(orgId, domain);
103
+ await addDomain(zitadel, orgId, domain);
79
104
  await zitadel.setPrimaryOrgDomain(orgId, domain);
80
105
  }, (error) => isDomainNotListed(error) || isPrimaryDomainRefused(error), sleep);
81
106
  }
@@ -85,9 +110,9 @@ export async function registerSignInDomain(zitadel, orgId, domain, sleep = (ms)
85
110
  * `blockedScopeKey` and returns `false` instead of throwing on every apply.
86
111
  * Any other failure throws. Returns `true` when the domain is primary.
87
112
  */
88
- export async function registerSignInDomainOrBlock(zitadel, orgId, domain, recordScopeId, blockedScopeKey = ZITADEL_ORG_DOMAIN_BLOCKED_SCOPE_KEY) {
113
+ export async function registerSignInDomainOrBlock(zitadel, orgId, domain, recordScopeId, blockedScopeKey = ZITADEL_ORG_DOMAIN_BLOCKED_SCOPE_KEY, sleep) {
89
114
  try {
90
- await registerSignInDomain(zitadel, orgId, domain);
115
+ await registerSignInDomain(zitadel, orgId, domain, sleep);
91
116
  return true;
92
117
  }
93
118
  catch (error) {
@@ -115,7 +140,7 @@ export async function ensureSignInOrg(options) {
115
140
  if (options.registrationClosedScopeKey) {
116
141
  recordScopeId(options.registrationClosedScopeKey, org.id);
117
142
  }
118
- const registered = await registerSignInDomainOrBlock(zitadel, org.id, domain, recordScopeId, options.domainBlockedScopeKey);
143
+ const registered = await registerSignInDomainOrBlock(zitadel, org.id, domain, recordScopeId, options.domainBlockedScopeKey, options.sleep);
119
144
  if (!registered)
120
145
  return { orgId: org.id, created, domainBlocked: true };
121
146
  recordScopeId(options.domainScopeKey ?? ZITADEL_ORG_DOMAIN_SCOPE_KEY, domain);
@@ -31,10 +31,13 @@ export declare const SIGN_IN_LOGIN_APP_SCOPE_KEY = "coolify_platform_auth_login_
31
31
  * 1. on: the login-host step trusts each app host ({@link SIGN_IN_TRUSTED_SCOPE_KEY}).
32
32
  * 2. on: the app step writes and deploys `SIGNIN_UPSTREAM` and records it live ({@link SIGN_IN_ENV_LIVE_SCOPE_PREFIX}).
33
33
  * 3. on: the login-host step binds each app host and points the base URI at `https://<app host>/sign-in/`
34
- * ({@link SIGN_IN_IN_APP_SCOPE_KEY}). The login refuses a client id held by two bindings, so the bind moves
35
- * the app's client id off the company's binding at the same write: that write and the base URI are the only
36
- * moment the old path can fail, and they are the last two calls.
37
- * Off: the login-host step moves the client id back and points the base URI back at the login host; the app
34
+ * ({@link SIGN_IN_IN_APP_SCOPE_KEY}). From auth 0.28.0 (one client may sit in two bindings of one company)
35
+ * this is three writes: the app host's binding with the client id kept on the company's, the base URI, then
36
+ * the company's binding without the id; sign-in works at every point. Before that release the login refuses
37
+ * a client id held by two bindings, so one write moves the id off the company's binding: that write and the
38
+ * base URI are the only moment the old path can fail, and they are the last two calls.
39
+ * Off: the login-host step moves the client id back and points the base URI back at the login host (0.28.0 and
40
+ * later: the company binding takes the id first and the app binding goes after the base URI); the app
38
41
  * step then drops `SIGNIN_UPSTREAM`; the login-host step then gives up the trusted domain.
39
42
  */
40
43
  /**
@@ -70,7 +73,11 @@ export declare const SIGN_IN_RATE_LIMIT_EXPRESSION = "(starts_with(http.request.
70
73
  * today's flow even when the switch is set (the declaration is refused: `org.ts`'s `validateSignIn`).
71
74
  */
72
75
  export declare function inAppSignIn(org: Pick<DeclaredOrg, 'signIn' | 'domain' | 'issuer' | 'services'>): boolean;
73
- /** True when an app's live env holds `SIGNIN_UPSTREAM` and the desired env does not: it is to be removed. */
76
+ /**
77
+ * True when an app's live env holds `SIGNIN_UPSTREAM` and the desired env does not: it is to be removed.
78
+ * Accepted behaviour: a `SIGNIN_UPSTREAM` added by hand on Agora or Boule is drift like any other env foundry
79
+ * owns, so the next apply removes it. Foundry writes the value itself (`signInUpstream`) while the switch is on.
80
+ */
74
81
  export declare function staleSignInUpstream(desired: Readonly<Record<string, string>>, live: Readonly<Record<string, string>>): boolean;
75
82
  /** The build argument (a build-time env in Coolify) that fixes the login's base path. */
76
83
  export declare const SIGN_IN_BUILD_ARG_ENV = "NEXT_PUBLIC_BASE_PATH";
@@ -31,10 +31,13 @@ export const SIGN_IN_LOGIN_APP_SCOPE_KEY = 'coolify_platform_auth_login_sign_in_
31
31
  * 1. on: the login-host step trusts each app host ({@link SIGN_IN_TRUSTED_SCOPE_KEY}).
32
32
  * 2. on: the app step writes and deploys `SIGNIN_UPSTREAM` and records it live ({@link SIGN_IN_ENV_LIVE_SCOPE_PREFIX}).
33
33
  * 3. on: the login-host step binds each app host and points the base URI at `https://<app host>/sign-in/`
34
- * ({@link SIGN_IN_IN_APP_SCOPE_KEY}). The login refuses a client id held by two bindings, so the bind moves
35
- * the app's client id off the company's binding at the same write: that write and the base URI are the only
36
- * moment the old path can fail, and they are the last two calls.
37
- * Off: the login-host step moves the client id back and points the base URI back at the login host; the app
34
+ * ({@link SIGN_IN_IN_APP_SCOPE_KEY}). From auth 0.28.0 (one client may sit in two bindings of one company)
35
+ * this is three writes: the app host's binding with the client id kept on the company's, the base URI, then
36
+ * the company's binding without the id; sign-in works at every point. Before that release the login refuses
37
+ * a client id held by two bindings, so one write moves the id off the company's binding: that write and the
38
+ * base URI are the only moment the old path can fail, and they are the last two calls.
39
+ * Off: the login-host step moves the client id back and points the base URI back at the login host (0.28.0 and
40
+ * later: the company binding takes the id first and the app binding goes after the base URI); the app
38
41
  * step then drops `SIGNIN_UPSTREAM`; the login-host step then gives up the trusted domain.
39
42
  */
40
43
  /**
@@ -76,7 +79,11 @@ export const SIGN_IN_RATE_LIMIT_EXPRESSION = '(starts_with(http.request.uri.path
76
79
  export function inAppSignIn(org) {
77
80
  return org.signIn?.inApp === true && Boolean(org.domain) && org.issuer.usesDomain;
78
81
  }
79
- /** True when an app's live env holds `SIGNIN_UPSTREAM` and the desired env does not: it is to be removed. */
82
+ /**
83
+ * True when an app's live env holds `SIGNIN_UPSTREAM` and the desired env does not: it is to be removed.
84
+ * Accepted behaviour: a `SIGNIN_UPSTREAM` added by hand on Agora or Boule is drift like any other env foundry
85
+ * owns, so the next apply removes it. Foundry writes the value itself (`signInUpstream`) while the switch is on.
86
+ */
80
87
  export function staleSignInUpstream(desired, live) {
81
88
  const held = live[SIGN_IN_UPSTREAM_ENV];
82
89
  return desired[SIGN_IN_UPSTREAM_ENV] === undefined && held !== undefined && held !== '';
@@ -1,4 +1,5 @@
1
1
  import { pinnedRef } from '../bootstrap/versions.js';
2
+ import { JobFailure, waitForJob as waitForJobApp } from './job-app.js';
2
3
  import { PLATFORM_AUTH_DB_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_SCOPE_KEY, findOrCreatePlatformApp, } from './platform-providers.js';
3
4
  import { SIGNIN_DB_TLS_SCOPE_KEY, SIGNIN_DB_ZITADEL_PENDING_SCOPE_KEY } from './signin-db-tls.js';
4
5
  import { SIGNIN_DB_NAME, SIGNIN_DB_PORT, SIGNIN_DB_USER, boundedCall, dockerfileAppCreator, ensureSigninDbCaFile, provisionSigninDb, waitHealthy, withCallTimeout, } from './signin-db.js';
@@ -94,41 +95,16 @@ export function signinDbMoveNewUrl(host, password, database) {
94
95
  class MoveFailure extends Error {
95
96
  }
96
97
  const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
97
- /** Polls of an `exited` job with no `running` seen before it counts as a stop (an `exited` read can be the container before the deploy). */
98
- const JOB_EXITED_POLLS = 6;
99
- /**
100
- * Waits for the copy job to report healthy, which it does only after the counts match. The job never
101
- * exits on success, so any exit is final: `restarting` at once, `exited` once the job was seen running
102
- * or stayed exited for {@link JOB_EXITED_POLLS} polls. It has failed too at the deadline. A healthy
103
- * status read before any other could be the container before the stop, so it counts only after a
104
- * different status was seen.
105
- */
98
+ /** Waits for the copy job to report healthy, which it does only after the counts match. */
106
99
  async function waitForJob(coolify, appId, runtime) {
107
- const now = runtime.now ?? Date.now;
108
- const sleep = runtime.sleep ?? defaultSleep;
109
- const timeout = runtime.signinDbMoveTimeoutMs ?? 1_800_000;
110
- const interval = runtime.signinDbMovePollMs ?? 10_000;
111
- const deadline = now() + timeout;
112
- let left = false;
113
- let running = false;
114
- let exitedPolls = 0;
115
- for (;;) {
116
- const { status } = await coolify.getApplication(appId);
117
- if (status === 'running:healthy' && left)
118
- return;
119
- if (status !== 'running:healthy')
120
- left = true;
121
- if (status?.startsWith('running'))
122
- running = true;
123
- exitedPolls = status?.startsWith('exited') ? exitedPolls + 1 : 0;
124
- if (status?.startsWith('restarting') ||
125
- (status?.startsWith('exited') && (running || exitedPolls >= JOB_EXITED_POLLS))) {
126
- throw new MoveFailure('the copy job stopped before the row counts matched');
127
- }
128
- if (now() >= deadline) {
129
- throw new MoveFailure(`the copy job did not finish within ${Math.round(timeout / 1000)}s (last status: ${status ?? 'unknown'})`);
130
- }
131
- await sleep(interval);
100
+ try {
101
+ await waitForJobApp(coolify, appId, runtime, {
102
+ job: 'the copy job',
103
+ stopped: 'the copy job stopped before the row counts matched',
104
+ });
105
+ }
106
+ catch (error) {
107
+ throw error instanceof JobFailure ? new MoveFailure(error.message) : error;
132
108
  }
133
109
  }
134
110
  /** Coolify queues a stop. Waits until it reports the app stopped; `pg_dump` must not run while ZITADEL can still write. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/foundry",
3
- "version": "0.45.1",
3
+ "version": "0.46.0",
4
4
  "description": "Foundry's SDK: the declared org's types, the plan and apply contracts, and the state store's interface.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -38,15 +38,15 @@
38
38
  "status": "node scripts/status.mjs"
39
39
  },
40
40
  "peerDependencies": {
41
- "@wtfalch/providers": ">=0.10.0",
41
+ "@wtfalch/providers": ">=0.12.0",
42
42
  "drizzle-orm": ">=0.45.2"
43
43
  },
44
44
  "dependencies": {
45
45
  "@wtfalch/audit": "0.6.0",
46
- "@wtfalch/auth": "0.16.0",
47
- "@wtfalch/edge": "0.13.0",
46
+ "@wtfalch/auth": "^0.26.0",
47
+ "@wtfalch/edge": "0.15.1",
48
48
  "@wtfalch/email": "^0.20.0",
49
- "@wtfalch/providers": "0.10.0",
49
+ "@wtfalch/providers": "0.12.0",
50
50
  "ssh2": "1.17.0",
51
51
  "zod": "4.6.5"
52
52
  },