@wtfalch/foundry 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/calls-sfu.d.ts +17 -0
- package/dist/calls-sfu.js +17 -0
- package/dist/index.d.ts +1 -1
- package/dist/migrations/0014_calls_enabled.sql +7 -0
- package/dist/migrations/0015_domain_unique.sql +39 -0
- package/dist/org.d.ts +18 -2
- package/dist/org.js +48 -0
- package/dist/plan/calls-matrix.d.ts +135 -0
- package/dist/plan/calls-matrix.js +275 -0
- package/dist/plan/diff.d.ts +8 -4
- package/dist/plan/diff.js +105 -6
- package/dist/plan/executor.d.ts +390 -2
- package/dist/plan/executor.js +799 -28
- package/dist/plan/index.d.ts +1 -1
- package/dist/plan/plan-apply.js +12 -5
- package/dist/slug.js +8 -2
- package/dist/store/postgres.d.ts +11 -6
- package/dist/store/postgres.js +85 -27
- package/dist/store/schema.d.ts +17 -0
- package/dist/store/schema.js +1 -0
- package/dist/store.d.ts +10 -0
- package/package.json +2 -2
package/dist/plan/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export type { AuditActor, AuditLog, AuthzGate, StepExecutor, StepDestroyer, Dest
|
|
|
6
6
|
export { FoundryPlanApply, DeclaredOrgNotFoundError, StalePlanError, ConfirmationRequiredError, NotAuthorizedError, NotATestOrgError, } from './plan-apply.js';
|
|
7
7
|
export type { ScopeResolver, RecordedIds } from './providers-readers.js';
|
|
8
8
|
export { ProvidersCloudflareReader, ProvidersCmsReader, ProvidersCoolifyReader, ProvidersZitadelReader, storeScopeResolver, storeRecordedIds, } from './providers-readers.js';
|
|
9
|
-
export type { CoolifyPort, CloudflarePort, ZitadelPort, FilesOrgLimits, AiOrgLimits, FilesOrganisationPort, AiOrganisationPort, FoundryStepExecutorPorts, } from './executor.js';
|
|
9
|
+
export type { CoolifyPort, CloudflarePort, ZitadelPort, CallsSfuPort, FilesOrgLimits, AiOrgLimits, FilesOrganisationPort, AiOrganisationPort, FoundryStepExecutorPorts, } from './executor.js';
|
|
10
10
|
export { FoundryStepExecutor, NotBuiltError, healthUrls } from './executor.js';
|
package/dist/plan/plan-apply.js
CHANGED
|
@@ -333,6 +333,8 @@ const STEP_FIELDS = {
|
|
|
333
333
|
'zitadel:customers': 'hosting.issuerApps',
|
|
334
334
|
'coolify:mail': 'mail',
|
|
335
335
|
'coolify:chat': 'chat.usesDomain',
|
|
336
|
+
'coolify:calls': 'calls.enabled',
|
|
337
|
+
'coolify:calls-service': 'calls.enabled',
|
|
336
338
|
'files:storage': 'storage',
|
|
337
339
|
'ai:budget': 'ai',
|
|
338
340
|
'cms:site': 'site',
|
|
@@ -356,10 +358,12 @@ function stepField(action) {
|
|
|
356
358
|
*
|
|
357
359
|
* Three sources, in this order:
|
|
358
360
|
* - fields no planner step exists for yet, so no apply can make them true:
|
|
359
|
-
* SSO, guests and solo when turned on,
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
361
|
+
* SSO, guests and solo when turned on, a bespoke app repo, an adopted app
|
|
362
|
+
* in the `repo` form (#7's `#15` comment: "declared but refused by
|
|
363
|
+
* `apply`"), and calls or hosting on a domain the org has not recorded
|
|
364
|
+
* (#145: calls on the org's own domain now has a planner step, the same
|
|
365
|
+
* way hosting's own domain does, so it only fails for that one missing-
|
|
366
|
+
* domain reason, not unconditionally);
|
|
363
367
|
* - chat's reverse re-key, which the planner surfaces but which is not built
|
|
364
368
|
* (#7 amendment 3), so it counts as failed even when no step threw;
|
|
365
369
|
* - on a failed apply, the field of every planned step from the one that
|
|
@@ -377,7 +381,10 @@ function failedFields(org, detail, completed) {
|
|
|
377
381
|
failed.add('bespokeAppRepo');
|
|
378
382
|
if (org.adoptedApp?.form === 'repo')
|
|
379
383
|
failed.add('adoptedApp');
|
|
380
|
-
|
|
384
|
+
// `planCallsService`/`planTunnel`/`planZone` all share this same domain
|
|
385
|
+
// requirement (#145): a declared `usesDomain: true` with no recorded
|
|
386
|
+
// domain plans no step for any of them either.
|
|
387
|
+
if (org.calls.usesDomain && org.domain === null)
|
|
381
388
|
failed.add('calls.usesDomain');
|
|
382
389
|
// `planZone` plans nothing without a domain to put the zone on.
|
|
383
390
|
if (org.hosting.usesDomain && org.domain === null)
|
package/dist/slug.js
CHANGED
|
@@ -32,11 +32,15 @@ const RESERVED_ESTATE_SERVICES = [
|
|
|
32
32
|
'keys',
|
|
33
33
|
'chat',
|
|
34
34
|
'calls',
|
|
35
|
+
'calls-sfu',
|
|
35
36
|
'calendar',
|
|
36
37
|
'threads',
|
|
37
38
|
'audit',
|
|
38
39
|
'authz',
|
|
40
|
+
// Manage was renamed Boule on every surface (#147); 'manage' stays
|
|
41
|
+
// reserved so no org can take it while the old hostname still redirects.
|
|
39
42
|
'manage',
|
|
43
|
+
'boule',
|
|
40
44
|
'operator',
|
|
41
45
|
'mailbox',
|
|
42
46
|
'oikos',
|
|
@@ -112,6 +116,8 @@ const DNS_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
|
112
116
|
const PR_SUFFIX = /-pr-\d+$/;
|
|
113
117
|
/** `<something>-stage` — the stage-hostname shape (#28). */
|
|
114
118
|
const STAGE_SUFFIX = /-stage$/;
|
|
119
|
+
/** `<something>-calls` — a level-1 org's own calls service hostname (foundry#145's "Decided", 2026-09-27). */
|
|
120
|
+
const CALLS_SUFFIX = /-calls$/;
|
|
115
121
|
/**
|
|
116
122
|
* Validates a candidate org slug against every rule #28 decided. Returns
|
|
117
123
|
* `null` when the slug is acceptable, or a {@link SlugRejection} naming
|
|
@@ -132,10 +138,10 @@ export function validateSlug(slug) {
|
|
|
132
138
|
message: `slug '${slug}' is reserved by the estate (#28) and cannot be taken by an org`,
|
|
133
139
|
};
|
|
134
140
|
}
|
|
135
|
-
if (STAGE_SUFFIX.test(slug) || PR_SUFFIX.test(slug)) {
|
|
141
|
+
if (STAGE_SUFFIX.test(slug) || PR_SUFFIX.test(slug) || CALLS_SUFFIX.test(slug)) {
|
|
136
142
|
return {
|
|
137
143
|
reason: 'environment-suffix',
|
|
138
|
-
message: `slug '${slug}' ends with an environment suffix ('-stage'
|
|
144
|
+
message: `slug '${slug}' ends with an environment suffix ('-stage', '-pr-<digits>' or '-calls') reserved for another org's own hostname (#28, #145) — 'acme-calls' is only legal as acme's calls hostname, never as an org slug in its own right`,
|
|
139
145
|
};
|
|
140
146
|
}
|
|
141
147
|
return null;
|
package/dist/store/postgres.d.ts
CHANGED
|
@@ -14,7 +14,10 @@ export type FoundryStoreDb = PgDatabase<PgQueryResultHKT, any, any>;
|
|
|
14
14
|
* environment-suffix shape), a slug already taken by another org (#28:
|
|
15
15
|
* checked by the unique index `0004_slug_unique.sql` adds, whether caught
|
|
16
16
|
* ahead of time by a pre-check or by a race this store maps back to the
|
|
17
|
-
* same error)
|
|
17
|
+
* same error), a domain that fails `../org.js`'s `validateDomain` or is
|
|
18
|
+
* already taken by another org (#131, the same shape as the slug: format
|
|
19
|
+
* checked ahead of time, uniqueness checked by `0015_domain_unique.sql`'s
|
|
20
|
+
* index) or — the one impossible combination #18 implements — a slug
|
|
18
21
|
* change once the org has mail. `store.ts`'s `update` doc requires a caller
|
|
19
22
|
* to be "ready for this to throw rather than silently drop a field"; this
|
|
20
23
|
* is that throw.
|
|
@@ -36,10 +39,12 @@ export declare class DeclaredOrgError extends Error {
|
|
|
36
39
|
* (or `null` for uncapped), checked the same way on both `create` and the
|
|
37
40
|
* merged result of `update`. #67 adds a fourth (`org.ts`'s
|
|
38
41
|
* `validateBuyerIdentity`) and #71 a fifth (`org.ts`'s `validateHosting`),
|
|
39
|
-
* checked the same way.
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
42
|
+
* checked the same way. #131 adds a sixth, in the same shape as the slug: the
|
|
43
|
+
* domain, when set, is a validated (`org.ts`'s `validateDomain`), globally
|
|
44
|
+
* unique DNS name, checked on both `create` and `update`. The broader
|
|
45
|
+
* "combinations foundry knows are impossible are rejected with a reason"
|
|
46
|
+
* line needs a ruleset (domain requirements per service, `adoptedApp.form`,
|
|
47
|
+
* mail-level transitions…) that later tickets, not #18, #27, #28 or #131,
|
|
48
|
+
* decide; `update` still merges and stores any other patch as given.
|
|
44
49
|
*/
|
|
45
50
|
export declare function createPostgresDeclaredOrgStore(db: FoundryStoreDb): DeclaredOrgStore;
|
package/dist/store/postgres.js
CHANGED
|
@@ -1,25 +1,34 @@
|
|
|
1
1
|
import { eq, sql } from 'drizzle-orm';
|
|
2
|
-
import { validateBuyerIdentity, validateHosting, validateOwner, validateSite } from '../org.js';
|
|
2
|
+
import { validateBuyerIdentity, validateDomain, validateHosting, validateOwner, validateSite, } from '../org.js';
|
|
3
3
|
import { validateSlug } from '../slug.js';
|
|
4
4
|
import { declaredOrgs, foundryAppliedState, } from './schema.js';
|
|
5
5
|
const UNIQUE_VIOLATION = '23505';
|
|
6
|
+
/** `migrations/0004_slug_unique.sql` / `migrations/0015_domain_unique.sql`'s index names, read back off a caught unique-violation to tell which one fired. */
|
|
7
|
+
const SLUG_UNIQUE_INDEX = 'foundry_declared_orgs_slug_idx';
|
|
8
|
+
const DOMAIN_UNIQUE_INDEX = 'foundry_declared_orgs_domain_idx';
|
|
6
9
|
/** #27: a bigint-safe decimal string of a non-negative integer -- no sign, no decimal point, no leading zeros (so "010" never diffs against a live "10"). */
|
|
7
10
|
const NON_NEGATIVE_INTEGER = /^(0|[1-9]\d*)$/;
|
|
8
11
|
/**
|
|
9
|
-
*
|
|
10
|
-
* directly (PGlite in this package's own tests) or wrapped in
|
|
11
|
-
* own `DrizzleQueryError`, whose `cause` carries the raw driver
|
|
12
|
-
* (postgres-js in a host)
|
|
13
|
-
*
|
|
12
|
+
* The Postgres unique-violation constraint name, however the driver hands the
|
|
13
|
+
* error back -- directly (PGlite in this package's own tests) or wrapped in
|
|
14
|
+
* drizzle-orm's own `DrizzleQueryError`, whose `cause` carries the raw driver
|
|
15
|
+
* error (postgres-js in a host) -- or `undefined` when this is not a unique
|
|
16
|
+
* violation. Checked by Postgres error code, never by message text, since
|
|
17
|
+
* message text is not a contract. Same code-check pattern as
|
|
14
18
|
* `../runner/runner.ts`'s own `isUniqueViolation` -- kept as a local copy
|
|
15
|
-
* here rather than shared, since neither module depends on the other
|
|
19
|
+
* here rather than shared, since neither module depends on the other; this
|
|
20
|
+
* one additionally reads `constraint` so `create`/`update` can tell a slug
|
|
21
|
+
* collision from a domain collision (#131) rather than reporting both the
|
|
22
|
+
* same way.
|
|
16
23
|
*/
|
|
17
|
-
function
|
|
18
|
-
const
|
|
19
|
-
if (code === UNIQUE_VIOLATION)
|
|
20
|
-
return
|
|
24
|
+
function uniqueViolationConstraint(error) {
|
|
25
|
+
const direct = error;
|
|
26
|
+
if (direct?.code === UNIQUE_VIOLATION)
|
|
27
|
+
return direct.constraint;
|
|
21
28
|
const cause = error?.cause;
|
|
22
|
-
|
|
29
|
+
if (cause?.code === UNIQUE_VIOLATION)
|
|
30
|
+
return cause.constraint;
|
|
31
|
+
return undefined;
|
|
23
32
|
}
|
|
24
33
|
/**
|
|
25
34
|
* Thrown by `create` and `update` when the store refuses a write: an id
|
|
@@ -28,7 +37,10 @@ function isUniqueViolation(error) {
|
|
|
28
37
|
* environment-suffix shape), a slug already taken by another org (#28:
|
|
29
38
|
* checked by the unique index `0004_slug_unique.sql` adds, whether caught
|
|
30
39
|
* ahead of time by a pre-check or by a race this store maps back to the
|
|
31
|
-
* same error)
|
|
40
|
+
* same error), a domain that fails `../org.js`'s `validateDomain` or is
|
|
41
|
+
* already taken by another org (#131, the same shape as the slug: format
|
|
42
|
+
* checked ahead of time, uniqueness checked by `0015_domain_unique.sql`'s
|
|
43
|
+
* index) or — the one impossible combination #18 implements — a slug
|
|
32
44
|
* change once the org has mail. `store.ts`'s `update` doc requires a caller
|
|
33
45
|
* to be "ready for this to throw rather than silently drop a field"; this
|
|
34
46
|
* is that throw.
|
|
@@ -47,7 +59,7 @@ function rowToOrg(row) {
|
|
|
47
59
|
domain: row.domain,
|
|
48
60
|
mail: { mode: row.mailMode, usesDomain: row.mailUsesDomain },
|
|
49
61
|
chat: { usesDomain: row.chatUsesDomain },
|
|
50
|
-
calls: { usesDomain: row.callsUsesDomain },
|
|
62
|
+
calls: { enabled: row.callsEnabled, usesDomain: row.callsUsesDomain },
|
|
51
63
|
issuer: { usesDomain: row.issuerUsesDomain },
|
|
52
64
|
site: { enabled: row.siteEnabled, usesDomain: row.siteUsesDomain },
|
|
53
65
|
hosting: {
|
|
@@ -83,6 +95,7 @@ function orgToRow(org) {
|
|
|
83
95
|
mailMode: org.mail.mode,
|
|
84
96
|
mailUsesDomain: org.mail.usesDomain,
|
|
85
97
|
chatUsesDomain: org.chat.usesDomain,
|
|
98
|
+
callsEnabled: org.calls.enabled,
|
|
86
99
|
callsUsesDomain: org.calls.usesDomain,
|
|
87
100
|
issuerUsesDomain: org.issuer.usesDomain,
|
|
88
101
|
siteEnabled: org.site.enabled,
|
|
@@ -166,11 +179,13 @@ function rowToOwner(row) {
|
|
|
166
179
|
* (or `null` for uncapped), checked the same way on both `create` and the
|
|
167
180
|
* merged result of `update`. #67 adds a fourth (`org.ts`'s
|
|
168
181
|
* `validateBuyerIdentity`) and #71 a fifth (`org.ts`'s `validateHosting`),
|
|
169
|
-
* checked the same way.
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
182
|
+
* checked the same way. #131 adds a sixth, in the same shape as the slug: the
|
|
183
|
+
* domain, when set, is a validated (`org.ts`'s `validateDomain`), globally
|
|
184
|
+
* unique DNS name, checked on both `create` and `update`. The broader
|
|
185
|
+
* "combinations foundry knows are impossible are rejected with a reason"
|
|
186
|
+
* line needs a ruleset (domain requirements per service, `adoptedApp.form`,
|
|
187
|
+
* mail-level transitions…) that later tickets, not #18, #27, #28 or #131,
|
|
188
|
+
* decide; `update` still merges and stores any other patch as given.
|
|
174
189
|
*/
|
|
175
190
|
export function createPostgresDeclaredOrgStore(db) {
|
|
176
191
|
async function requireRow(id) {
|
|
@@ -184,6 +199,18 @@ export function createPostgresDeclaredOrgStore(db) {
|
|
|
184
199
|
if (rejection)
|
|
185
200
|
throw new DeclaredOrgError(rejection.message);
|
|
186
201
|
}
|
|
202
|
+
// #131: same place `requireValidSlug` checks the slug's format (#28) --
|
|
203
|
+
// `domain` is nullable (a brand new org has none), so this is only called
|
|
204
|
+
// once it is set. Uniqueness itself is not pre-checked here, the same
|
|
205
|
+
// choice `requireValidSlug` makes for the slug: the unique index
|
|
206
|
+
// `0015_domain_unique.sql` adds is the source of truth, caught below.
|
|
207
|
+
function requireValidDomain(domain) {
|
|
208
|
+
if (domain === null)
|
|
209
|
+
return;
|
|
210
|
+
const reason = validateDomain(domain);
|
|
211
|
+
if (reason)
|
|
212
|
+
throw new DeclaredOrgError(`domain invalid: ${reason} (#131)`);
|
|
213
|
+
}
|
|
187
214
|
function requireValidLimit(fieldName, value) {
|
|
188
215
|
if (value !== null && !NON_NEGATIVE_INTEGER.test(value)) {
|
|
189
216
|
throw new DeclaredOrgError(`${fieldName} '${value}' must be a bigint-safe decimal string of a non-negative integer, or null for uncapped (#27)`);
|
|
@@ -230,6 +257,7 @@ export function createPostgresDeclaredOrgStore(db) {
|
|
|
230
257
|
return {
|
|
231
258
|
async create(org) {
|
|
232
259
|
requireValidSlug(org.slug);
|
|
260
|
+
requireValidDomain(org.domain);
|
|
233
261
|
requireValidLimits(org);
|
|
234
262
|
requireValidBuyer(org);
|
|
235
263
|
requireValidHosting(org);
|
|
@@ -239,13 +267,19 @@ export function createPostgresDeclaredOrgStore(db) {
|
|
|
239
267
|
await db.insert(declaredOrgs).values(orgToRow(org));
|
|
240
268
|
}
|
|
241
269
|
catch (error) {
|
|
242
|
-
// The pre-
|
|
243
|
-
// taken (nothing does, on a fresh insert) -- this catches
|
|
244
|
-
// the
|
|
245
|
-
// caught by `0004_slug_unique.sql`'s
|
|
246
|
-
// through the same error a pre-check
|
|
247
|
-
|
|
270
|
+
// The pre-checks above already refuse a slug or domain this process
|
|
271
|
+
// knows is taken (nothing does, on a fresh insert) -- this catches
|
|
272
|
+
// the race they cannot: two concurrent creates for the same slug or
|
|
273
|
+
// domain, caught by `0004_slug_unique.sql`/`0015_domain_unique.sql`'s
|
|
274
|
+
// unique indexes and reported through the same error a pre-check
|
|
275
|
+
// would have thrown (#28, #131).
|
|
276
|
+
const constraint = uniqueViolationConstraint(error);
|
|
277
|
+
if (constraint === DOMAIN_UNIQUE_INDEX) {
|
|
278
|
+
throw new DeclaredOrgError(`domain '${org.domain}' is already taken by another org (#131)`);
|
|
279
|
+
}
|
|
280
|
+
if (constraint === SLUG_UNIQUE_INDEX || constraint !== undefined) {
|
|
248
281
|
throw new DeclaredOrgError(`slug '${org.slug}' is already taken by another org`);
|
|
282
|
+
}
|
|
249
283
|
throw error;
|
|
250
284
|
}
|
|
251
285
|
},
|
|
@@ -261,6 +295,23 @@ export function createPostgresDeclaredOrgStore(db) {
|
|
|
261
295
|
.limit(1);
|
|
262
296
|
return row !== undefined;
|
|
263
297
|
},
|
|
298
|
+
// #131: a plain scan, not an indexed lookup -- `serviceIds` is jsonb
|
|
299
|
+
// keyed by resource, not by provider id, and foundry's own org count is
|
|
300
|
+
// small enough that this never needs to be more than that (same
|
|
301
|
+
// reasoning `getAppliedState`/`putAppliedState` already lean on: one row
|
|
302
|
+
// per org).
|
|
303
|
+
async findOrgIdForZone(zoneId) {
|
|
304
|
+
const rows = await db
|
|
305
|
+
.select({ orgId: foundryAppliedState.orgId, serviceIds: foundryAppliedState.serviceIds })
|
|
306
|
+
.from(foundryAppliedState);
|
|
307
|
+
for (const row of rows) {
|
|
308
|
+
for (const [key, id] of Object.entries(row.serviceIds)) {
|
|
309
|
+
if (id === zoneId && key.startsWith('cloudflare:zone:'))
|
|
310
|
+
return row.orgId;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return null;
|
|
314
|
+
},
|
|
264
315
|
async update(id, patch) {
|
|
265
316
|
const current = rowToOrg(await requireRow(id));
|
|
266
317
|
// The slug freeze (#7 corrections 3): once mail exists, the mail
|
|
@@ -279,6 +330,7 @@ export function createPostgresDeclaredOrgStore(db) {
|
|
|
279
330
|
if (patch.slug !== undefined && patch.slug !== current.slug)
|
|
280
331
|
requireValidSlug(patch.slug);
|
|
281
332
|
const next = { ...current, ...patch, id: current.id };
|
|
333
|
+
requireValidDomain(next.domain);
|
|
282
334
|
requireValidLimits(next);
|
|
283
335
|
requireValidBuyer(next);
|
|
284
336
|
requireValidHosting(next);
|
|
@@ -292,9 +344,15 @@ export function createPostgresDeclaredOrgStore(db) {
|
|
|
292
344
|
}
|
|
293
345
|
catch (error) {
|
|
294
346
|
// Same race as `create`: another org's concurrent write took this
|
|
295
|
-
// slug between the pre-check above and this statement
|
|
296
|
-
|
|
347
|
+
// slug or domain between the pre-check above and this statement
|
|
348
|
+
// (#28, #131).
|
|
349
|
+
const constraint = uniqueViolationConstraint(error);
|
|
350
|
+
if (constraint === DOMAIN_UNIQUE_INDEX) {
|
|
351
|
+
throw new DeclaredOrgError(`domain '${next.domain}' is already taken by another org (#131)`);
|
|
352
|
+
}
|
|
353
|
+
if (constraint === SLUG_UNIQUE_INDEX || constraint !== undefined) {
|
|
297
354
|
throw new DeclaredOrgError(`slug '${next.slug}' is already taken by another org`);
|
|
355
|
+
}
|
|
298
356
|
throw error;
|
|
299
357
|
}
|
|
300
358
|
return next;
|
package/dist/store/schema.d.ts
CHANGED
|
@@ -130,6 +130,23 @@ export declare const declaredOrgs: import("drizzle-orm/pg-core").PgTableWithColu
|
|
|
130
130
|
identity: undefined;
|
|
131
131
|
generated: undefined;
|
|
132
132
|
}, {}, {}>;
|
|
133
|
+
callsEnabled: import("drizzle-orm/pg-core").PgColumn<{
|
|
134
|
+
name: "calls_enabled";
|
|
135
|
+
tableName: "foundry_declared_orgs";
|
|
136
|
+
dataType: "boolean";
|
|
137
|
+
columnType: "PgBoolean";
|
|
138
|
+
data: boolean;
|
|
139
|
+
driverParam: boolean;
|
|
140
|
+
notNull: true;
|
|
141
|
+
hasDefault: true;
|
|
142
|
+
isPrimaryKey: false;
|
|
143
|
+
isAutoincrement: false;
|
|
144
|
+
hasRuntimeDefault: false;
|
|
145
|
+
enumValues: undefined;
|
|
146
|
+
baseColumn: never;
|
|
147
|
+
identity: undefined;
|
|
148
|
+
generated: undefined;
|
|
149
|
+
}, {}, {}>;
|
|
133
150
|
callsUsesDomain: import("drizzle-orm/pg-core").PgColumn<{
|
|
134
151
|
name: "calls_uses_domain";
|
|
135
152
|
tableName: "foundry_declared_orgs";
|
package/dist/store/schema.js
CHANGED
|
@@ -13,6 +13,7 @@ export const declaredOrgs = pgTable('foundry_declared_orgs', {
|
|
|
13
13
|
mailMode: text('mail_mode').notNull().default('off'),
|
|
14
14
|
mailUsesDomain: boolean('mail_uses_domain').notNull().default(false),
|
|
15
15
|
chatUsesDomain: boolean('chat_uses_domain').notNull().default(false),
|
|
16
|
+
callsEnabled: boolean('calls_enabled').notNull().default(false),
|
|
16
17
|
callsUsesDomain: boolean('calls_uses_domain').notNull().default(false),
|
|
17
18
|
issuerUsesDomain: boolean('issuer_uses_domain').notNull().default(false),
|
|
18
19
|
// SiteConfig (org.ts, T10). A brand new org has no cms site.
|
package/dist/store.d.ts
CHANGED
|
@@ -14,6 +14,16 @@ export interface DeclaredOrgStore {
|
|
|
14
14
|
get(id: OrgId): Promise<DeclaredOrg | null>;
|
|
15
15
|
/** Whether any org already holds this slug. `slugFromName` asks it. */
|
|
16
16
|
slugTaken(slug: string): Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* The org id, if any, whose applied state already records `zoneId` under a
|
|
19
|
+
* `cloudflare:zone:*` key (#131). `plan/executor.ts`'s `cloudflareZone`
|
|
20
|
+
* asks this before adopting a zone `findZoneByApex` finds, the same
|
|
21
|
+
* foreign-resource refusal `cloudflareAccess` makes by reading Cloudflare's
|
|
22
|
+
* own Access-app name -- a Cloudflare zone carries no org-identifying
|
|
23
|
+
* field of its own for that live check, so this asks the store's own
|
|
24
|
+
* record instead.
|
|
25
|
+
*/
|
|
26
|
+
findOrgIdForZone(zoneId: string): Promise<OrgId | null>;
|
|
17
27
|
/**
|
|
18
28
|
* Combinations foundry knows are impossible are rejected with a reason
|
|
19
29
|
* (#18 "Done when"), so a caller must be ready for this to throw rather
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wtfalch/foundry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@wtfalch/auth": "0.16.0",
|
|
41
41
|
"@wtfalch/email": "0.15.0",
|
|
42
|
-
"@wtfalch/providers": "0.
|
|
42
|
+
"@wtfalch/providers": "0.6.0",
|
|
43
43
|
"ssh2": "1.17.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|