@wtfalch/foundry 0.4.0 → 0.6.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 (76) hide show
  1. package/dist/bootstrap/bootstrap.d.ts +111 -0
  2. package/dist/bootstrap/bootstrap.js +323 -0
  3. package/dist/bootstrap/declare.d.ts +36 -0
  4. package/dist/bootstrap/declare.js +99 -0
  5. package/dist/bootstrap/drive.d.ts +40 -0
  6. package/dist/bootstrap/drive.js +51 -0
  7. package/dist/bootstrap/index.d.ts +3 -0
  8. package/dist/bootstrap/index.js +1 -0
  9. package/dist/calls-sfu-deploy-support.d.ts +17 -6
  10. package/dist/calls-sfu-deploy-support.js +19 -7
  11. package/dist/calls-sfu-keys.d.ts +21 -0
  12. package/dist/calls-sfu-keys.js +27 -0
  13. package/dist/calls-sfu-ssh-adapter.d.ts +55 -0
  14. package/dist/calls-sfu-ssh-adapter.js +78 -0
  15. package/dist/calls-sfu-ssh-client.d.ts +49 -0
  16. package/dist/calls-sfu-ssh-client.js +82 -0
  17. package/dist/calls-sfu.d.ts +36 -12
  18. package/dist/calls-sfu.js +41 -9
  19. package/dist/index.d.ts +38 -7
  20. package/dist/index.js +20 -4
  21. package/dist/migrations/0016_platform.sql +61 -0
  22. package/dist/migrations/0017_requested_changes.sql +42 -0
  23. package/dist/migrations/0018_platform_apply.sql +19 -0
  24. package/dist/migrations/0019_mail_owner_mailbox.sql +15 -0
  25. package/dist/migrations/0020_mail_apps.sql +7 -0
  26. package/dist/migrations/0021_mail_role_mailboxes.sql +6 -0
  27. package/dist/org.d.ts +81 -0
  28. package/dist/org.js +96 -0
  29. package/dist/plan/agora.d.ts +77 -0
  30. package/dist/plan/agora.js +127 -0
  31. package/dist/plan/cloudflared-connector.d.ts +70 -0
  32. package/dist/plan/cloudflared-connector.js +106 -0
  33. package/dist/plan/diff.d.ts +20 -3
  34. package/dist/plan/diff.js +713 -115
  35. package/dist/plan/executor.d.ts +680 -172
  36. package/dist/plan/executor.js +755 -189
  37. package/dist/plan/index.d.ts +28 -5
  38. package/dist/plan/index.js +15 -2
  39. package/dist/plan/jobs-scheduler-http.d.ts +29 -0
  40. package/dist/plan/jobs-scheduler-http.js +50 -0
  41. package/dist/plan/live.d.ts +51 -2
  42. package/dist/plan/live.js +8 -0
  43. package/dist/plan/mail-copy.d.ts +125 -0
  44. package/dist/plan/mail-copy.js +166 -0
  45. package/dist/plan/mail-identity-allowlist.d.ts +50 -0
  46. package/dist/plan/mail-identity-allowlist.js +11 -0
  47. package/dist/plan/mail-identity.d.ts +68 -0
  48. package/dist/plan/mail-identity.js +17 -0
  49. package/dist/plan/plan-apply.d.ts +197 -3
  50. package/dist/plan/plan-apply.js +278 -9
  51. package/dist/plan/platform-diff.d.ts +487 -0
  52. package/dist/plan/platform-diff.js +1220 -0
  53. package/dist/plan/platform-org-ports.d.ts +50 -0
  54. package/dist/plan/platform-org-ports.js +70 -0
  55. package/dist/plan/platform-org.d.ts +121 -0
  56. package/dist/plan/platform-org.js +115 -0
  57. package/dist/plan/platform-providers.d.ts +615 -0
  58. package/dist/plan/platform-providers.js +4410 -0
  59. package/dist/plan/platform-sfu.d.ts +128 -0
  60. package/dist/plan/platform-sfu.js +142 -0
  61. package/dist/plan/providers-readers.js +22 -1
  62. package/dist/plan/zitadel-machine-user.d.ts +83 -0
  63. package/dist/plan/zitadel-machine-user.js +238 -0
  64. package/dist/plan.d.ts +6 -0
  65. package/dist/platform.d.ts +55 -0
  66. package/dist/platform.js +46 -0
  67. package/dist/requested-changes.d.ts +118 -0
  68. package/dist/requested-changes.js +125 -0
  69. package/dist/slug.js +15 -2
  70. package/dist/store/audit.js +4 -2
  71. package/dist/store/postgres.d.ts +29 -1
  72. package/dist/store/postgres.js +203 -4
  73. package/dist/store/schema.d.ts +755 -0
  74. package/dist/store/schema.js +70 -0
  75. package/dist/store.d.ts +53 -0
  76. package/package.json +1 -1
@@ -1,7 +1,9 @@
1
- import { eq, sql } from 'drizzle-orm';
2
- import { validateBuyerIdentity, validateDomain, validateHosting, validateOwner, validateSite, } from '../org.js';
1
+ import { asc, eq, sql } from 'drizzle-orm';
2
+ import { DEFAULT_MAIL_APPS, DEFAULT_NO_REPLY_LOCAL_PART, validateBuyerIdentity, validateDomain, validateHosting, validateMail, validateOwner, validateSite, } from '../org.js';
3
+ import { validatePlatform } from '../platform.js';
4
+ import { RequestedChangeError, classifyChange } from '../requested-changes.js';
3
5
  import { validateSlug } from '../slug.js';
4
- import { declaredOrgs, foundryAppliedState, } from './schema.js';
6
+ import { declaredOrgs, foundryAppliedState, foundryDeclaredPlatform, foundryRequestedChanges, } from './schema.js';
5
7
  const UNIQUE_VIOLATION = '23505';
6
8
  /** `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
9
  const SLUG_UNIQUE_INDEX = 'foundry_declared_orgs_slug_idx';
@@ -57,7 +59,14 @@ function rowToOrg(row) {
57
59
  slug: row.slug,
58
60
  displayName: row.displayName,
59
61
  domain: row.domain,
60
- mail: { mode: row.mailMode, usesDomain: row.mailUsesDomain },
62
+ mail: {
63
+ mode: row.mailMode,
64
+ usesDomain: row.mailUsesDomain,
65
+ ownerMailboxAddress: row.ownerMailboxAddress,
66
+ noReplyLocalPart: row.noReplyLocalPart,
67
+ mailApps: row.mailApps,
68
+ roleMailboxes: row.roleMailboxes,
69
+ },
61
70
  chat: { usesDomain: row.chatUsesDomain },
62
71
  calls: { enabled: row.callsEnabled, usesDomain: row.callsUsesDomain },
63
72
  issuer: { usesDomain: row.issuerUsesDomain },
@@ -94,6 +103,10 @@ function orgToRow(org) {
94
103
  domain: org.domain,
95
104
  mailMode: org.mail.mode,
96
105
  mailUsesDomain: org.mail.usesDomain,
106
+ ownerMailboxAddress: org.mail.ownerMailboxAddress ?? null,
107
+ noReplyLocalPart: org.mail.noReplyLocalPart ?? DEFAULT_NO_REPLY_LOCAL_PART,
108
+ mailApps: org.mail.mailApps ?? [...DEFAULT_MAIL_APPS],
109
+ roleMailboxes: org.mail.roleMailboxes ?? [],
97
110
  chatUsesDomain: org.chat.usesDomain,
98
111
  callsEnabled: org.calls.enabled,
99
112
  callsUsesDomain: org.calls.usesDomain,
@@ -254,6 +267,16 @@ export function createPostgresDeclaredOrgStore(db) {
254
267
  if (reason)
255
268
  throw new DeclaredOrgError(`owner invalid: ${reason}`);
256
269
  }
270
+ // #162: same place `requireValidOwner` checks the owner, on both `create`
271
+ // and the merged result of `update`. Deliberately does not require
272
+ // `mail.ownerMailboxAddress` just because mail is on -- `validateMail`'s
273
+ // own doc comment says why. Also covers #34's `mailApps` check, folded
274
+ // into the same `validateMail`.
275
+ function requireValidMail(org) {
276
+ const reason = validateMail(org);
277
+ if (reason)
278
+ throw new DeclaredOrgError(`mail invalid: ${reason} (#162)`);
279
+ }
257
280
  return {
258
281
  async create(org) {
259
282
  requireValidSlug(org.slug);
@@ -263,6 +286,7 @@ export function createPostgresDeclaredOrgStore(db) {
263
286
  requireValidHosting(org);
264
287
  requireValidSite(org);
265
288
  requireValidOwner(org);
289
+ requireValidMail(org);
266
290
  try {
267
291
  await db.insert(declaredOrgs).values(orgToRow(org));
268
292
  }
@@ -287,6 +311,12 @@ export function createPostgresDeclaredOrgStore(db) {
287
311
  const [row] = await db.select().from(declaredOrgs).where(eq(declaredOrgs.id, id)).limit(1);
288
312
  return row ? rowToOrg(row) : null;
289
313
  },
314
+ // #133: ordered by display name, the same `asc(...)` shape
315
+ // `createPostgresRequestedChangeStore.listForOrg` already uses below.
316
+ async list() {
317
+ const rows = await db.select().from(declaredOrgs).orderBy(asc(declaredOrgs.displayName));
318
+ return rows.map(rowToOrg);
319
+ },
290
320
  async slugTaken(slug) {
291
321
  const [row] = await db
292
322
  .select({ id: declaredOrgs.id })
@@ -336,6 +366,7 @@ export function createPostgresDeclaredOrgStore(db) {
336
366
  requireValidHosting(next);
337
367
  requireValidSite(next);
338
368
  requireValidOwner(next);
369
+ requireValidMail(next);
339
370
  try {
340
371
  await db
341
372
  .update(declaredOrgs)
@@ -405,3 +436,171 @@ export function createPostgresDeclaredOrgStore(db) {
405
436
  },
406
437
  };
407
438
  }
439
+ /**
440
+ * Thrown by `put` when `validatePlatform` (`../platform.js`) refuses the
441
+ * platform given to it -- same "throw with a reason" shape
442
+ * {@link DeclaredOrgError} gives `create`/`update`.
443
+ */
444
+ export class PlatformError extends Error {
445
+ constructor(message) {
446
+ super(message);
447
+ this.name = 'PlatformError';
448
+ }
449
+ }
450
+ /** `migrations/0016_platform.sql`'s singleton row -- `id` is always `true`, the only value its `check (id)` constraint allows. */
451
+ const PLATFORM_ROW_ID = true;
452
+ function rowToPlatform(row) {
453
+ return {
454
+ ownerOrgId: row.ownerOrgId,
455
+ apex: row.apex,
456
+ auth: { enabled: row.authEnabled, hostname: row.authHostname, gitRef: row.authGitRef },
457
+ mail: { enabled: row.mailEnabled, hostname: row.mailHostname, gitRef: row.mailGitRef },
458
+ sfu: { enabled: row.sfuEnabled, hostname: row.sfuHostname, gitRef: row.sfuGitRef },
459
+ files: { enabled: row.filesEnabled, hostname: row.filesHostname, gitRef: row.filesGitRef },
460
+ ai: { enabled: row.aiEnabled, hostname: row.aiHostname, gitRef: row.aiGitRef },
461
+ jobs: { enabled: row.jobsEnabled, hostname: row.jobsHostname, gitRef: row.jobsGitRef },
462
+ integrations: {
463
+ enabled: row.integrationsEnabled,
464
+ hostname: row.integrationsHostname,
465
+ gitRef: row.integrationsGitRef,
466
+ },
467
+ archon: {
468
+ enabled: row.archonEnabled,
469
+ hostname: row.archonHostname,
470
+ gitRef: row.archonGitRef,
471
+ },
472
+ };
473
+ }
474
+ function platformToRow(platform) {
475
+ return {
476
+ id: PLATFORM_ROW_ID,
477
+ ownerOrgId: platform.ownerOrgId,
478
+ apex: platform.apex,
479
+ authEnabled: platform.auth.enabled,
480
+ authHostname: platform.auth.hostname,
481
+ authGitRef: platform.auth.gitRef,
482
+ mailEnabled: platform.mail.enabled,
483
+ mailHostname: platform.mail.hostname,
484
+ mailGitRef: platform.mail.gitRef,
485
+ sfuEnabled: platform.sfu.enabled,
486
+ sfuHostname: platform.sfu.hostname,
487
+ sfuGitRef: platform.sfu.gitRef,
488
+ filesEnabled: platform.files.enabled,
489
+ filesHostname: platform.files.hostname,
490
+ filesGitRef: platform.files.gitRef,
491
+ aiEnabled: platform.ai.enabled,
492
+ aiHostname: platform.ai.hostname,
493
+ aiGitRef: platform.ai.gitRef,
494
+ jobsEnabled: platform.jobs.enabled,
495
+ jobsHostname: platform.jobs.hostname,
496
+ jobsGitRef: platform.jobs.gitRef,
497
+ integrationsEnabled: platform.integrations.enabled,
498
+ integrationsHostname: platform.integrations.hostname,
499
+ integrationsGitRef: platform.integrations.gitRef,
500
+ archonEnabled: platform.archon.enabled,
501
+ archonHostname: platform.archon.hostname,
502
+ archonGitRef: platform.archon.gitRef,
503
+ };
504
+ }
505
+ /**
506
+ * `PlatformStore` (`../store.ts`) on Postgres, for #166: get and put the one
507
+ * {@link DeclaredPlatform}, beside `createPostgresDeclaredOrgStore` above.
508
+ * `put` validates with `../platform.js`'s `validatePlatform` and then
509
+ * upserts onto the fixed singleton row `migrations/0016_platform.sql`
510
+ * declares -- the first call creates it, every later call replaces it, and
511
+ * the table's own `check (id)` constraint is what actually refuses a second
512
+ * row, not this function.
513
+ */
514
+ export function createPostgresPlatformStore(db) {
515
+ return {
516
+ async get() {
517
+ const [row] = await db.select().from(foundryDeclaredPlatform).limit(1);
518
+ return row ? rowToPlatform(row) : null;
519
+ },
520
+ async put(platform) {
521
+ const reason = validatePlatform(platform);
522
+ if (reason)
523
+ throw new PlatformError(reason);
524
+ const row = platformToRow(platform);
525
+ await db
526
+ .insert(foundryDeclaredPlatform)
527
+ .values(row)
528
+ .onConflictDoUpdate({ target: foundryDeclaredPlatform.id, set: row });
529
+ },
530
+ async getServiceIds() {
531
+ const [row] = await db
532
+ .select({ serviceIds: foundryDeclaredPlatform.serviceIds })
533
+ .from(foundryDeclaredPlatform)
534
+ .limit(1);
535
+ return row?.serviceIds ?? {};
536
+ },
537
+ async putServiceIds(serviceIds) {
538
+ await db.update(foundryDeclaredPlatform).set({ serviceIds });
539
+ },
540
+ };
541
+ }
542
+ /**
543
+ * `value`'s own field-specific payload, stripped of its `field` -- that is
544
+ * a column of its own (`foundryRequestedChanges.field`), not repeated
545
+ * inside the jsonb.
546
+ */
547
+ function requestedChangePayload(value) {
548
+ const { field, ...payload } = value;
549
+ return payload;
550
+ }
551
+ function rowToRequestedChange(row) {
552
+ return {
553
+ id: row.id,
554
+ orgId: row.orgId,
555
+ value: { field: row.field, ...row.value },
556
+ requestedBy: row.requestedBy,
557
+ createdAt: row.createdAt.toISOString(),
558
+ state: row.state,
559
+ };
560
+ }
561
+ /**
562
+ * `RequestedChangeStore` (`../store.ts`) on Postgres, for #178: classify and
563
+ * store a company's own requested setting change on its declared org, beside
564
+ * `createPostgresDeclaredOrgStore` and `createPostgresPlatformStore` above.
565
+ * `create` classifies with `../requested-changes.js`'s `classifyChange`
566
+ * against the `org` given to it -- this store never loads the org itself,
567
+ * the same "caller already has it" choice `PlatformStore.put`'s `platform`
568
+ * argument makes -- and stores the result as the request's initial `state`.
569
+ */
570
+ export function createPostgresRequestedChangeStore(db) {
571
+ return {
572
+ async create({ orgId, org, value, requestedBy }) {
573
+ const classification = classifyChange(org, value);
574
+ const state = classification === 'auto' ? 'auto' : 'needs_approval';
575
+ const [row] = await db
576
+ .insert(foundryRequestedChanges)
577
+ .values({
578
+ orgId,
579
+ field: value.field,
580
+ value: requestedChangePayload(value),
581
+ requestedBy,
582
+ state,
583
+ })
584
+ .returning();
585
+ if (!row)
586
+ throw new RequestedChangeError('insert returned no row');
587
+ return rowToRequestedChange(row);
588
+ },
589
+ async get(id) {
590
+ const [row] = await db
591
+ .select()
592
+ .from(foundryRequestedChanges)
593
+ .where(eq(foundryRequestedChanges.id, id))
594
+ .limit(1);
595
+ return row ? rowToRequestedChange(row) : null;
596
+ },
597
+ async listForOrg(orgId) {
598
+ const rows = await db
599
+ .select()
600
+ .from(foundryRequestedChanges)
601
+ .where(eq(foundryRequestedChanges.orgId, orgId))
602
+ .orderBy(asc(foundryRequestedChanges.createdAt));
603
+ return rows.map(rowToRequestedChange);
604
+ },
605
+ };
606
+ }