@wtfalch/foundry 0.45.1 → 0.47.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/build-json.d.ts +28 -0
- package/dist/build-json.js +95 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.js +6 -2
- package/dist/migrations/0058_app_requests.sql +32 -0
- package/dist/org.d.ts +68 -5
- package/dist/org.js +287 -135
- package/dist/plan/agora.d.ts +14 -0
- package/dist/plan/agora.js +21 -0
- package/dist/plan/backup-gate.d.ts +15 -0
- package/dist/plan/backup-gate.js +38 -0
- package/dist/plan/boule-owners-http.d.ts +12 -0
- package/dist/plan/boule-owners-http.js +59 -0
- package/dist/plan/boule.d.ts +38 -2
- package/dist/plan/boule.js +72 -1
- package/dist/plan/company-backups.d.ts +7 -1
- package/dist/plan/company-backups.js +42 -2
- package/dist/plan/cookie-domain.d.ts +41 -0
- package/dist/plan/cookie-domain.js +71 -0
- package/dist/plan/desktop-auth.d.ts +58 -0
- package/dist/plan/desktop-auth.js +144 -0
- package/dist/plan/desktop-callback.d.ts +26 -0
- package/dist/plan/desktop-callback.js +59 -0
- package/dist/plan/diff.d.ts +13 -4
- package/dist/plan/diff.js +277 -88
- package/dist/plan/executor.d.ts +136 -6
- package/dist/plan/executor.js +732 -105
- package/dist/plan/health-gaps.js +5 -0
- package/dist/plan/health-targets.js +1 -2
- package/dist/plan/hostnames.d.ts +2 -2
- package/dist/plan/hostnames.js +6 -6
- package/dist/plan/index.d.ts +4 -1
- package/dist/plan/index.js +3 -1
- package/dist/plan/job-app.d.ts +23 -0
- package/dist/plan/job-app.js +41 -0
- package/dist/plan/live.d.ts +6 -0
- package/dist/plan/live.js +16 -0
- package/dist/plan/login-host.d.ts +28 -0
- package/dist/plan/login-host.js +36 -0
- package/dist/plan/org-names.js +9 -1
- package/dist/plan/org-postgres-job.d.ts +22 -0
- package/dist/plan/org-postgres-job.js +91 -0
- package/dist/plan/org-postgres-migrate.d.ts +115 -0
- package/dist/plan/org-postgres-migrate.js +466 -0
- package/dist/plan/org-postgres.d.ts +90 -0
- package/dist/plan/org-postgres.js +293 -0
- package/dist/plan/plan-apply.d.ts +60 -3
- package/dist/plan/plan-apply.js +174 -8
- package/dist/plan/platform-diff.d.ts +45 -0
- package/dist/plan/platform-diff.js +159 -4
- package/dist/plan/platform-org.d.ts +2 -0
- package/dist/plan/platform-org.js +4 -0
- package/dist/plan/platform-providers.d.ts +12 -2
- package/dist/plan/platform-providers.js +282 -9
- package/dist/plan/post-logout-uris.d.ts +22 -0
- package/dist/plan/post-logout-uris.js +43 -0
- package/dist/plan/providers-readers.d.ts +10 -1
- package/dist/plan/providers-readers.js +56 -5
- package/dist/plan/sign-in-bindings.d.ts +4 -0
- package/dist/plan/sign-in-bindings.js +10 -0
- package/dist/plan/sign-in-org.d.ts +19 -6
- package/dist/plan/sign-in-org.js +35 -10
- package/dist/plan/sign-in.d.ts +12 -5
- package/dist/plan/sign-in.js +12 -5
- package/dist/plan/signin-db-move.js +10 -34
- package/dist/plan/worker-app.js +3 -1
- package/dist/refuse-org.d.ts +7 -0
- package/dist/refuse-org.js +10 -0
- package/dist/requested-changes.d.ts +50 -4
- package/dist/requested-changes.js +9 -1
- package/dist/store/postgres.d.ts +5 -1
- package/dist/store/postgres.js +67 -8
- package/dist/store/schema.d.ts +72 -0
- package/dist/store/schema.js +4 -0
- package/dist/store.d.ts +24 -1
- package/dist/worker-app-config.d.ts +8 -2
- package/dist/worker-app-config.js +20 -6
- package/package.json +5 -5
package/dist/store/postgres.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { and, asc, desc, eq, inArray, isNull, ne, sql } from 'drizzle-orm';
|
|
2
|
-
import { APP_ENTRY_KEY, DEFAULT_MAIL_APPS, DEFAULT_NO_REPLY_LOCAL_PART, validateBlueGreen, validateBuyerIdentity, validateCalls,
|
|
2
|
+
import { APP_ENTRY_KEY, DEFAULT_MAIL_APPS, DEFAULT_NO_REPLY_LOCAL_PART, refuseCustomers, refuseHosting, validateBlueGreen, validateBuyerIdentity, validateCalls, validateDomain, validateDomainClaim, validateMail, validateOwners, validateServices, validateSignIn, validateSite, validateVersionPin, } from '../org.js';
|
|
3
3
|
import { hostingNames } from '../plan/locked-hostnames.js';
|
|
4
4
|
import { findNameClashes, nameClashMessage } from '../plan/org-names.js';
|
|
5
5
|
import { validatePlatform } from '../platform.js';
|
|
@@ -77,9 +77,16 @@ export function uniqueViolationConstraint(error) {
|
|
|
77
77
|
* is that throw.
|
|
78
78
|
*/
|
|
79
79
|
export class DeclaredOrgError extends Error {
|
|
80
|
-
|
|
80
|
+
/** Set when a hosting or customers validator refused the write: which rule, and where in the org. */
|
|
81
|
+
code;
|
|
82
|
+
path;
|
|
83
|
+
constructor(message, refusal) {
|
|
81
84
|
super(message);
|
|
82
85
|
this.name = 'DeclaredOrgError';
|
|
86
|
+
if (refusal) {
|
|
87
|
+
this.code = refusal.code;
|
|
88
|
+
this.path = refusal.path;
|
|
89
|
+
}
|
|
83
90
|
}
|
|
84
91
|
}
|
|
85
92
|
/**
|
|
@@ -357,9 +364,9 @@ export function createPostgresDeclaredOrgStore(db) {
|
|
|
357
364
|
// #71: same place `requireValidBuyer` checks the buyer identity (#67), on
|
|
358
365
|
// both `create` and the merged result of `update`.
|
|
359
366
|
function requireValidHosting(org) {
|
|
360
|
-
const
|
|
361
|
-
if (
|
|
362
|
-
throw new DeclaredOrgError(`hosting invalid: ${
|
|
367
|
+
const refusal = refuseHosting(org.hosting, hostingNames(org, []));
|
|
368
|
+
if (refusal)
|
|
369
|
+
throw new DeclaredOrgError(`hosting invalid: ${refusal.message} (#71)`, refusal);
|
|
363
370
|
}
|
|
364
371
|
// #101: same place `requireValidHosting` checks hosting, on both `create`
|
|
365
372
|
// and the merged result of `update`.
|
|
@@ -411,9 +418,10 @@ export function createPostgresDeclaredOrgStore(db) {
|
|
|
411
418
|
const signInReason = validateSignIn(org);
|
|
412
419
|
if (signInReason)
|
|
413
420
|
throw new DeclaredOrgError(`signIn invalid: ${signInReason}`);
|
|
414
|
-
const
|
|
415
|
-
if (
|
|
416
|
-
throw new DeclaredOrgError(`customers invalid: ${
|
|
421
|
+
const customersRefusal = refuseCustomers(org);
|
|
422
|
+
if (customersRefusal) {
|
|
423
|
+
throw new DeclaredOrgError(`customers invalid: ${customersRefusal.message}`, customersRefusal);
|
|
424
|
+
}
|
|
417
425
|
const slot = org.services?.sfuSlot ?? null;
|
|
418
426
|
if (slot === null)
|
|
419
427
|
return;
|
|
@@ -855,6 +863,10 @@ function rowToRequestedChange(row) {
|
|
|
855
863
|
requestedBy: row.requestedBy,
|
|
856
864
|
createdAt: row.createdAt.toISOString(),
|
|
857
865
|
state: row.state,
|
|
866
|
+
requester: row.requester ?? null,
|
|
867
|
+
idempotencyKey: row.idempotencyKey ?? null,
|
|
868
|
+
reason: row.reason ?? null,
|
|
869
|
+
updatedAt: row.updatedAt.toISOString(),
|
|
858
870
|
};
|
|
859
871
|
}
|
|
860
872
|
/**
|
|
@@ -885,6 +897,53 @@ export function createPostgresRequestedChangeStore(db) {
|
|
|
885
897
|
throw new RequestedChangeError('insert returned no row');
|
|
886
898
|
return rowToRequestedChange(row);
|
|
887
899
|
},
|
|
900
|
+
async createOrGet({ orgId, value, requestedBy, requester, idempotencyKey }) {
|
|
901
|
+
if (value.field !== 'app' && value.field !== 'build') {
|
|
902
|
+
throw new RequestedChangeError(`'${value.field}' is not an app or build request`);
|
|
903
|
+
}
|
|
904
|
+
if (idempotencyKey === '')
|
|
905
|
+
throw new RequestedChangeError('an idempotency key is required');
|
|
906
|
+
const [inserted] = await db
|
|
907
|
+
.insert(foundryRequestedChanges)
|
|
908
|
+
.values({
|
|
909
|
+
orgId,
|
|
910
|
+
field: value.field,
|
|
911
|
+
value: requestedChangePayload(value),
|
|
912
|
+
requestedBy,
|
|
913
|
+
state: 'applying',
|
|
914
|
+
requester: { ...requester },
|
|
915
|
+
idempotencyKey,
|
|
916
|
+
})
|
|
917
|
+
.onConflictDoNothing()
|
|
918
|
+
.returning();
|
|
919
|
+
if (inserted)
|
|
920
|
+
return { request: rowToRequestedChange(inserted), created: true };
|
|
921
|
+
const [existing] = await db
|
|
922
|
+
.select()
|
|
923
|
+
.from(foundryRequestedChanges)
|
|
924
|
+
.where(and(eq(foundryRequestedChanges.orgId, orgId), eq(foundryRequestedChanges.idempotencyKey, idempotencyKey)))
|
|
925
|
+
.limit(1);
|
|
926
|
+
if (!existing)
|
|
927
|
+
throw new RequestedChangeError('insert returned no row');
|
|
928
|
+
return { request: rowToRequestedChange(existing), created: false };
|
|
929
|
+
},
|
|
930
|
+
async transition(id, from, to, reason) {
|
|
931
|
+
const [row] = await db
|
|
932
|
+
.update(foundryRequestedChanges)
|
|
933
|
+
.set({ state: to, reason: reason ? { ...reason } : null, updatedAt: new Date() })
|
|
934
|
+
.where(and(eq(foundryRequestedChanges.id, id), inArray(foundryRequestedChanges.state, [...from])))
|
|
935
|
+
.returning();
|
|
936
|
+
return row ? rowToRequestedChange(row) : null;
|
|
937
|
+
},
|
|
938
|
+
async openForApp(orgId, key) {
|
|
939
|
+
const [row] = await db
|
|
940
|
+
.select()
|
|
941
|
+
.from(foundryRequestedChanges)
|
|
942
|
+
.where(and(eq(foundryRequestedChanges.orgId, orgId), eq(foundryRequestedChanges.state, 'applying'), inArray(foundryRequestedChanges.field, ['app', 'build']), sql `${foundryRequestedChanges.value} ->> 'key' = ${key}`))
|
|
943
|
+
.orderBy(desc(foundryRequestedChanges.id))
|
|
944
|
+
.limit(1);
|
|
945
|
+
return row ? rowToRequestedChange(row) : null;
|
|
946
|
+
},
|
|
888
947
|
async get(id) {
|
|
889
948
|
const [row] = await db
|
|
890
949
|
.select()
|
package/dist/store/schema.d.ts
CHANGED
|
@@ -2470,6 +2470,78 @@ export declare const foundryRequestedChanges: import("drizzle-orm/pg-core").PgTa
|
|
|
2470
2470
|
identity: undefined;
|
|
2471
2471
|
generated: undefined;
|
|
2472
2472
|
}, {}, {}>;
|
|
2473
|
+
requester: import("drizzle-orm/pg-core").PgColumn<{
|
|
2474
|
+
name: "requester";
|
|
2475
|
+
tableName: "foundry_requested_changes";
|
|
2476
|
+
dataType: "json";
|
|
2477
|
+
columnType: "PgJsonb";
|
|
2478
|
+
data: Record<string, unknown>;
|
|
2479
|
+
driverParam: unknown;
|
|
2480
|
+
notNull: false;
|
|
2481
|
+
hasDefault: false;
|
|
2482
|
+
isPrimaryKey: false;
|
|
2483
|
+
isAutoincrement: false;
|
|
2484
|
+
hasRuntimeDefault: false;
|
|
2485
|
+
enumValues: undefined;
|
|
2486
|
+
baseColumn: never;
|
|
2487
|
+
identity: undefined;
|
|
2488
|
+
generated: undefined;
|
|
2489
|
+
}, {}, {
|
|
2490
|
+
$type: Record<string, unknown>;
|
|
2491
|
+
}>;
|
|
2492
|
+
idempotencyKey: import("drizzle-orm/pg-core").PgColumn<{
|
|
2493
|
+
name: "idempotency_key";
|
|
2494
|
+
tableName: "foundry_requested_changes";
|
|
2495
|
+
dataType: "string";
|
|
2496
|
+
columnType: "PgText";
|
|
2497
|
+
data: string;
|
|
2498
|
+
driverParam: string;
|
|
2499
|
+
notNull: false;
|
|
2500
|
+
hasDefault: false;
|
|
2501
|
+
isPrimaryKey: false;
|
|
2502
|
+
isAutoincrement: false;
|
|
2503
|
+
hasRuntimeDefault: false;
|
|
2504
|
+
enumValues: [string, ...string[]];
|
|
2505
|
+
baseColumn: never;
|
|
2506
|
+
identity: undefined;
|
|
2507
|
+
generated: undefined;
|
|
2508
|
+
}, {}, {}>;
|
|
2509
|
+
reason: import("drizzle-orm/pg-core").PgColumn<{
|
|
2510
|
+
name: "reason";
|
|
2511
|
+
tableName: "foundry_requested_changes";
|
|
2512
|
+
dataType: "json";
|
|
2513
|
+
columnType: "PgJsonb";
|
|
2514
|
+
data: Record<string, unknown>;
|
|
2515
|
+
driverParam: unknown;
|
|
2516
|
+
notNull: false;
|
|
2517
|
+
hasDefault: false;
|
|
2518
|
+
isPrimaryKey: false;
|
|
2519
|
+
isAutoincrement: false;
|
|
2520
|
+
hasRuntimeDefault: false;
|
|
2521
|
+
enumValues: undefined;
|
|
2522
|
+
baseColumn: never;
|
|
2523
|
+
identity: undefined;
|
|
2524
|
+
generated: undefined;
|
|
2525
|
+
}, {}, {
|
|
2526
|
+
$type: Record<string, unknown>;
|
|
2527
|
+
}>;
|
|
2528
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
2529
|
+
name: "updated_at";
|
|
2530
|
+
tableName: "foundry_requested_changes";
|
|
2531
|
+
dataType: "date";
|
|
2532
|
+
columnType: "PgTimestamp";
|
|
2533
|
+
data: Date;
|
|
2534
|
+
driverParam: string;
|
|
2535
|
+
notNull: true;
|
|
2536
|
+
hasDefault: true;
|
|
2537
|
+
isPrimaryKey: false;
|
|
2538
|
+
isAutoincrement: false;
|
|
2539
|
+
hasRuntimeDefault: false;
|
|
2540
|
+
enumValues: undefined;
|
|
2541
|
+
baseColumn: never;
|
|
2542
|
+
identity: undefined;
|
|
2543
|
+
generated: undefined;
|
|
2544
|
+
}, {}, {}>;
|
|
2473
2545
|
};
|
|
2474
2546
|
dialect: "pg";
|
|
2475
2547
|
}>;
|
package/dist/store/schema.js
CHANGED
|
@@ -234,6 +234,10 @@ export const foundryRequestedChanges = pgTable('foundry_requested_changes', {
|
|
|
234
234
|
requestedBy: text('requested_by').notNull(),
|
|
235
235
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
236
236
|
state: text('state').notNull().default('needs_approval'),
|
|
237
|
+
requester: jsonb('requester').$type(),
|
|
238
|
+
idempotencyKey: text('idempotency_key'),
|
|
239
|
+
reason: jsonb('reason').$type(),
|
|
240
|
+
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
|
237
241
|
});
|
|
238
242
|
/** Mirrors `migrations/0055_release_requests.sql`: an operator's release action, consumed once by the runner. */
|
|
239
243
|
export const foundryReleaseRequests = pgTable('foundry_release_requests', {
|
package/dist/store.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DeclaredOrg, OrgId } from './org.js';
|
|
2
2
|
import type { DeclaredPlatform } from './platform.js';
|
|
3
|
-
import type { RequestedChange, RequestedChangeValue } from './requested-changes.js';
|
|
3
|
+
import type { RequestedBy, RequestedChange, RequestedChangeState, RequestedChangeValue } from './requested-changes.js';
|
|
4
4
|
import type { AppliedState } from './state.js';
|
|
5
5
|
/** `DeclaredOrgStore.update`'s options (#351). */
|
|
6
6
|
export interface UpdateOptions {
|
|
@@ -190,6 +190,29 @@ export interface RequestedChangeStore {
|
|
|
190
190
|
value: RequestedChangeValue;
|
|
191
191
|
requestedBy: string;
|
|
192
192
|
}): Promise<RequestedChange>;
|
|
193
|
+
/**
|
|
194
|
+
* Stores an `app` or `build` request in `applying`, or returns the one already stored under the
|
|
195
|
+
* same (orgId, idempotencyKey) with `created: false` and writes nothing. Safe under a race.
|
|
196
|
+
* Throws `RequestedChangeError` for any other kind.
|
|
197
|
+
*/
|
|
198
|
+
createOrGet(input: {
|
|
199
|
+
orgId: OrgId;
|
|
200
|
+
org: DeclaredOrg;
|
|
201
|
+
value: RequestedChangeValue;
|
|
202
|
+
requestedBy: string;
|
|
203
|
+
requester: RequestedBy;
|
|
204
|
+
idempotencyKey: string;
|
|
205
|
+
}): Promise<{
|
|
206
|
+
request: RequestedChange;
|
|
207
|
+
created: boolean;
|
|
208
|
+
}>;
|
|
209
|
+
/**
|
|
210
|
+
* Compare-and-set: moves request `id` to `to` only when it is in one of `from`, and returns the
|
|
211
|
+
* new row; null when it was not (or does not exist). `reason` is stored (null when omitted).
|
|
212
|
+
*/
|
|
213
|
+
transition(id: number, from: readonly RequestedChangeState[], to: RequestedChangeState, reason?: RequestedChange['reason']): Promise<RequestedChange | null>;
|
|
214
|
+
/** The `applying` app or build request for entry `key` of that org, or null. */
|
|
215
|
+
openForApp(orgId: OrgId, key: string): Promise<RequestedChange | null>;
|
|
193
216
|
get(id: number): Promise<RequestedChange | null>;
|
|
194
217
|
/** Every request an org has made, oldest first. */
|
|
195
218
|
listForOrg(orgId: OrgId): Promise<RequestedChange[]>;
|
|
@@ -117,13 +117,19 @@ export declare function validateArtifact(at: string, artifactUrl: string, sha256
|
|
|
117
117
|
* The one exact name a Worker app is served at: `<host>.<domain>` or the apex
|
|
118
118
|
* on the org's own domain, else `<slug>-<key>.oikos.host` (one label under
|
|
119
119
|
* `oikos.host`, so the free Universal certificate covers it), and
|
|
120
|
-
* `<slug>.oikos.host` for the entry
|
|
120
|
+
* `<slug>.oikos.host` for the main entry.
|
|
121
121
|
*/
|
|
122
122
|
export declare function workerAppHostnameOf(org: {
|
|
123
123
|
slug: string;
|
|
124
124
|
domain: string | null;
|
|
125
125
|
usesDomain: boolean;
|
|
126
|
-
},
|
|
126
|
+
}, entry: Pick<HostingEntry, 'key' | 'main'>, host: string): string;
|
|
127
|
+
/**
|
|
128
|
+
* A Worker app declared before its first build has an empty `artifactUrl` and an empty
|
|
129
|
+
* `artifactSha256`. It validates, and the planner deploys nothing until both are set. One empty
|
|
130
|
+
* and one set is still refused.
|
|
131
|
+
*/
|
|
132
|
+
export declare function workerAppHasBuild(app: Pick<WorkerAppConfig, 'artifactUrl' | 'artifactSha256'>): boolean;
|
|
127
133
|
/**
|
|
128
134
|
* The Durable Object classes the migrations leave behind, folded in order:
|
|
129
135
|
* created, then removed, then renamed. A rename of a class that does not exist
|
|
@@ -106,12 +106,24 @@ export function validateArtifact(at, artifactUrl, sha256, repo = null) {
|
|
|
106
106
|
* The one exact name a Worker app is served at: `<host>.<domain>` or the apex
|
|
107
107
|
* on the org's own domain, else `<slug>-<key>.oikos.host` (one label under
|
|
108
108
|
* `oikos.host`, so the free Universal certificate covers it), and
|
|
109
|
-
* `<slug>.oikos.host` for the entry
|
|
109
|
+
* `<slug>.oikos.host` for the main entry.
|
|
110
110
|
*/
|
|
111
|
-
export function workerAppHostnameOf(org,
|
|
111
|
+
export function workerAppHostnameOf(org, entry, host) {
|
|
112
112
|
if (org.usesDomain && org.domain)
|
|
113
113
|
return host === '@' ? org.domain : `${host}.${org.domain}`;
|
|
114
|
-
return
|
|
114
|
+
return isMain(entry) ? `${org.slug}.oikos.host` : `${org.slug}-${entry.key}.oikos.host`;
|
|
115
|
+
}
|
|
116
|
+
/** `org.ts`'s `isMainEntry`, restated: that file imports this one. */
|
|
117
|
+
function isMain(entry) {
|
|
118
|
+
return entry.main ?? entry.key === 'app';
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A Worker app declared before its first build has an empty `artifactUrl` and an empty
|
|
122
|
+
* `artifactSha256`. It validates, and the planner deploys nothing until both are set. One empty
|
|
123
|
+
* and one set is still refused.
|
|
124
|
+
*/
|
|
125
|
+
export function workerAppHasBuild(app) {
|
|
126
|
+
return app.artifactUrl !== '' || app.artifactSha256 !== '';
|
|
115
127
|
}
|
|
116
128
|
/** True for a real calendar date written `YYYY-MM-DD`. */
|
|
117
129
|
function isCalendarDate(value) {
|
|
@@ -197,7 +209,9 @@ export function validateEntryWorkerApp(entry, hosting, names) {
|
|
|
197
209
|
if (entry.key.length > WORKER_NAME_MAX_LENGTH - WORKER_NAME_FIXED_LENGTH) {
|
|
198
210
|
return `${at} entry key is too long: the deployed Worker's name (an 8-character org hash, the key, and "${WORKER_APP_NAME_SUFFIX}") must fit Cloudflare's ${WORKER_NAME_MAX_LENGTH}-character limit`;
|
|
199
211
|
}
|
|
200
|
-
const invalidArtifact =
|
|
212
|
+
const invalidArtifact = workerAppHasBuild(app)
|
|
213
|
+
? validateArtifact(at, app.artifactUrl, app.artifactSha256, entry.repo)
|
|
214
|
+
: null;
|
|
201
215
|
if (invalidArtifact)
|
|
202
216
|
return invalidArtifact;
|
|
203
217
|
if (app.compatibilityDate !== undefined && !isCalendarDate(app.compatibilityDate)) {
|
|
@@ -224,14 +238,14 @@ export function validateEntryWorkerApp(entry, hosting, names) {
|
|
|
224
238
|
slug: names.slug,
|
|
225
239
|
domain: names.domain,
|
|
226
240
|
usesDomain: hosting.usesDomain,
|
|
227
|
-
}, entry
|
|
241
|
+
}, entry, app.host);
|
|
228
242
|
if (names.locked.has(served))
|
|
229
243
|
return `${at} host is a name foundry owns: ${served}`;
|
|
230
244
|
// Without a domain the name is `<slug>-<key>.oikos.host`, the shape of
|
|
231
245
|
// every environment and service host of the org (`-auth`, `-agora`,
|
|
232
246
|
// `-boule`, `-stage`, `-pr-<n>`, ...). `slug.ts` already refuses a slug
|
|
233
247
|
// that ends in one of those suffixes, so the same check refuses the key.
|
|
234
|
-
if (!domain && entry
|
|
248
|
+
if (!domain && !isMain(entry) && validateSlug(`${names.slug}-${entry.key}`)) {
|
|
235
249
|
return `${at} entry key '${entry.key}' would serve ${served}, a name foundry reserves for the org's own services and environments`;
|
|
236
250
|
}
|
|
237
251
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wtfalch/foundry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.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.
|
|
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.
|
|
47
|
-
"@wtfalch/edge": "0.
|
|
46
|
+
"@wtfalch/auth": "^0.26.0",
|
|
47
|
+
"@wtfalch/edge": "0.15.1",
|
|
48
48
|
"@wtfalch/email": "^0.20.0",
|
|
49
|
-
"@wtfalch/providers": "0.
|
|
49
|
+
"@wtfalch/providers": "0.12.0",
|
|
50
50
|
"ssh2": "1.17.0",
|
|
51
51
|
"zod": "4.6.5"
|
|
52
52
|
},
|