@package-broker/runtime-node 0.0.0-bootstrap → 2.0.0-rc.1

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 (100) hide show
  1. package/LICENSE +661 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/assets.d.ts +4 -0
  4. package/dist/assets.d.ts.map +1 -0
  5. package/dist/assets.js +68 -0
  6. package/dist/assets.js.map +1 -0
  7. package/dist/blob/archive-staging.d.ts +15 -0
  8. package/dist/blob/archive-staging.d.ts.map +1 -0
  9. package/dist/blob/archive-staging.js +154 -0
  10. package/dist/blob/archive-staging.js.map +1 -0
  11. package/dist/blob/filesystem.d.ts +18 -0
  12. package/dist/blob/filesystem.d.ts.map +1 -0
  13. package/dist/blob/filesystem.js +631 -0
  14. package/dist/blob/filesystem.js.map +1 -0
  15. package/dist/blob/hasher.d.ts +3 -0
  16. package/dist/blob/hasher.d.ts.map +1 -0
  17. package/dist/blob/hasher.js +26 -0
  18. package/dist/blob/hasher.js.map +1 -0
  19. package/dist/blob/s3.d.ts +40 -0
  20. package/dist/blob/s3.d.ts.map +1 -0
  21. package/dist/blob/s3.js +494 -0
  22. package/dist/blob/s3.js.map +1 -0
  23. package/dist/cache/lru.d.ts +8 -0
  24. package/dist/cache/lru.d.ts.map +1 -0
  25. package/dist/cache/lru.js +103 -0
  26. package/dist/cache/lru.js.map +1 -0
  27. package/dist/client-ip.d.ts +2 -0
  28. package/dist/client-ip.d.ts.map +1 -0
  29. package/dist/client-ip.js +51 -0
  30. package/dist/client-ip.js.map +1 -0
  31. package/dist/codec/node-codec.d.ts +3 -0
  32. package/dist/codec/node-codec.d.ts.map +1 -0
  33. package/dist/codec/node-codec.js +163 -0
  34. package/dist/codec/node-codec.js.map +1 -0
  35. package/dist/db/better-sqlite3-executor.d.ts +10 -0
  36. package/dist/db/better-sqlite3-executor.d.ts.map +1 -0
  37. package/dist/db/better-sqlite3-executor.js +129 -0
  38. package/dist/db/better-sqlite3-executor.js.map +1 -0
  39. package/dist/entry.d.ts +32 -0
  40. package/dist/entry.d.ts.map +1 -0
  41. package/dist/entry.js +158 -0
  42. package/dist/entry.js.map +1 -0
  43. package/dist/import-jobs.d.ts +18 -0
  44. package/dist/import-jobs.d.ts.map +1 -0
  45. package/dist/import-jobs.js +56 -0
  46. package/dist/import-jobs.js.map +1 -0
  47. package/dist/index.d.ts +112 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +981 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/internal/seams-key.d.ts +2 -0
  52. package/dist/internal/seams-key.d.ts.map +1 -0
  53. package/dist/internal/seams-key.js +2 -0
  54. package/dist/internal/seams-key.js.map +1 -0
  55. package/dist/jobs/loop.d.ts +38 -0
  56. package/dist/jobs/loop.d.ts.map +1 -0
  57. package/dist/jobs/loop.js +318 -0
  58. package/dist/jobs/loop.js.map +1 -0
  59. package/dist/observability/stdio.d.ts +11 -0
  60. package/dist/observability/stdio.d.ts.map +1 -0
  61. package/dist/observability/stdio.js +47 -0
  62. package/dist/observability/stdio.js.map +1 -0
  63. package/dist/organization-administration.d.ts +5 -0
  64. package/dist/organization-administration.d.ts.map +1 -0
  65. package/dist/organization-administration.js +16 -0
  66. package/dist/organization-administration.js.map +1 -0
  67. package/dist/sources/dns-guard.d.ts +33 -0
  68. package/dist/sources/dns-guard.d.ts.map +1 -0
  69. package/dist/sources/dns-guard.js +202 -0
  70. package/dist/sources/dns-guard.js.map +1 -0
  71. package/dist/sources/git-provider.d.ts +30 -0
  72. package/dist/sources/git-provider.d.ts.map +1 -0
  73. package/dist/sources/git-provider.js +213 -0
  74. package/dist/sources/git-provider.js.map +1 -0
  75. package/dist/sources/guarded-fetch.d.ts +15 -0
  76. package/dist/sources/guarded-fetch.d.ts.map +1 -0
  77. package/dist/sources/guarded-fetch.js +217 -0
  78. package/dist/sources/guarded-fetch.js.map +1 -0
  79. package/dist/sources/ssh-archive-store.d.ts +19 -0
  80. package/dist/sources/ssh-archive-store.d.ts.map +1 -0
  81. package/dist/sources/ssh-archive-store.js +284 -0
  82. package/dist/sources/ssh-archive-store.js.map +1 -0
  83. package/dist/sources/ssh-git.d.ts +46 -0
  84. package/dist/sources/ssh-git.d.ts.map +1 -0
  85. package/dist/sources/ssh-git.js +430 -0
  86. package/dist/sources/ssh-git.js.map +1 -0
  87. package/dist/sources/ssh-provider.d.ts +55 -0
  88. package/dist/sources/ssh-provider.d.ts.map +1 -0
  89. package/dist/sources/ssh-provider.js +369 -0
  90. package/dist/sources/ssh-provider.js.map +1 -0
  91. package/dist/startup.d.ts +49 -0
  92. package/dist/startup.d.ts.map +1 -0
  93. package/dist/startup.js +687 -0
  94. package/dist/startup.js.map +1 -0
  95. package/dist/token-jobs.d.ts +18 -0
  96. package/dist/token-jobs.d.ts.map +1 -0
  97. package/dist/token-jobs.js +45 -0
  98. package/dist/token-jobs.js.map +1 -0
  99. package/package.json +45 -5
  100. package/README.md +0 -3
package/dist/index.js ADDED
@@ -0,0 +1,981 @@
1
+ import { schemaMigrationsMatch } from '@package-broker/db-sqlite/migrations/runner';
2
+ import { createApiRoutes } from '@package-broker/db-sqlite/repositories/api-services';
3
+ import { createComposerRepositoryProvider } from '@package-broker/core/ingest/composer/composer-repository';
4
+ import { createMcpTools } from '@package-broker/core/mcp/tools';
5
+ import { createInProcessMcpLimiter } from '@package-broker/core/mcp/limit';
6
+ import { createMcpRoutes } from '@package-broker/core/mcp/transport';
7
+ import { openApiRoute } from '@package-broker/core/api/openapi-route';
8
+ import { openApiDocument } from '@package-broker/core/api/openapi-document';
9
+ import { createVulnerabilityRuntime, VULNERABILITY_EVENT, VULNERABILITY_REASON_CODES, } from '@package-broker/core/runtime/vulnerability';
10
+ import { createVulnerabilityRepository } from '@package-broker/db-sqlite/repositories/vulnerability';
11
+ import { createAccessServices, } from '@package-broker/db-sqlite/repositories/access-services';
12
+ import { parseSourceConfig } from '@package-broker/core/sources/config';
13
+ import { sourceCredentialOrigins } from '@package-broker/core/sources/credential-origins';
14
+ import { createProviderRegistry, } from '@package-broker/core/sources/provider';
15
+ import { createSourceSyncCounts } from '@package-broker/db-sqlite/repositories/source-sync-counts';
16
+ import { createSourceSyncReads } from '@package-broker/db-sqlite/repositories/source-sync-reads';
17
+ import { createWebhookDeliveryRepository } from '@package-broker/db-sqlite/repositories/webhook-delivery';
18
+ import { createBlobRepository } from '@package-broker/db-sqlite/repositories/blob';
19
+ import { retentionScanJob, blobPurgeJob, createRetentionScanJob, createBlobPurgeJob, } from '@package-broker/core/ingest/retention-jobs';
20
+ import { createBlobLifecycle } from '@package-broker/core/platform/blob-lifecycle';
21
+ import { jobLeaseOf } from '@package-broker/core/platform/job-fences';
22
+ import { JobError } from '@package-broker/core/platform/jobs';
23
+ import { createBlobReferenceReader } from '@package-broker/db-sqlite/repositories/blob-references';
24
+ import { jobLeaseGuard } from '@package-broker/db-sqlite/repositories/job-lease-guard';
25
+ import { AUDIT_EVENTS } from '@package-broker/core/kernel/obs-codes';
26
+ import { createObservability, filterLogLevel } from '@package-broker/core/platform/observability';
27
+ import { createImportJobHolder, importJobRegistrations } from './import-jobs.js';
28
+ import { createImportJournalStore } from '@package-broker/db-sqlite/repositories/import-journal';
29
+ import { createImportRollbackStore } from '@package-broker/db-sqlite/repositories/import-rollback';
30
+ import { createImportVerificationStore } from '@package-broker/db-sqlite/repositories/import-verification';
31
+ import { auditSealJob } from '@package-broker/core/audit/sealer';
32
+ import { auditVerifyJob } from '@package-broker/core/audit/verify';
33
+ import { createAuditSealRepository } from '@package-broker/db-sqlite/repositories/audit-seal';
34
+ import { createAuditVerifyReader } from '@package-broker/db-sqlite/repositories/audit-verify';
35
+ import { createServer } from 'node:http';
36
+ import { AsyncLocalStorage } from 'node:async_hooks';
37
+ import { getRequestListener } from '@hono/node-server';
38
+ import { deferredDispatch } from '@package-broker/core/authz/token-use';
39
+ import { createPackageAccess, } from '@package-broker/core/catalogue/package-access';
40
+ import { createVisibilityService, } from '@package-broker/core/catalogue/visibility-service';
41
+ import { buildClosureReport, } from '@package-broker/core/catalogue/closure-report';
42
+ import {} from '@package-broker/core/authz/administration';
43
+ import { createRegistryModule, } from '@package-broker/core/registry/module';
44
+ import { createAccessRecorder } from '@package-broker/core/registry/access-recorder';
45
+ import { createScopedAdmittingAccess } from '@package-broker/core/registry/scoped-access';
46
+ import { GITHUB_API } from '@package-broker/core/ingest/composer/packagist-proxy';
47
+ import { INGEST_EVENTS, INGEST_REASON_CODES } from '@package-broker/core/ingest/finalize-guard';
48
+ import { createRepositoryJobs } from '@package-broker/core/platform/jobs-port';
49
+ import { resolveTokenLifetimePolicy, TokenConfigError, } from '@package-broker/core/authz/token-lifetime';
50
+ import { createPatJobHolder, patOffboardingRegistrations, tokenWiring, } from './token-jobs.js';
51
+ import { SEAMS_KEY } from './internal/seams-key.js';
52
+ import { PlatformUnavailableError } from '@package-broker/core/platform/errors';
53
+ import { createPolicyFetch } from '@package-broker/core/sources/policy-fetch';
54
+ import { createOutboundPolicy } from '@package-broker/core/sources/url-policy';
55
+ import { bindCredential, CredentialError, credentialEchoProbe, parseCredentialMaterial, } from '@package-broker/core/sources/credentials';
56
+ import { orgContext } from '@package-broker/core/db/port';
57
+ import { createSecretCipher, SecretDecryptError } from '@package-broker/core/secrets/cipher';
58
+ import { createCredentialStore, CredentialNotFoundError, } from '@package-broker/core/secrets/credential-store';
59
+ import { scopedEnv, buildIdentityProviders } from '@package-broker/core/identity/providers';
60
+ import { systemClock, ulid } from '@package-broker/core/ids';
61
+ import { defaultLog, envelopeResponse, errorResponse } from '@package-broker/core/kernel/errors';
62
+ import { createKernel } from '@package-broker/core/kernel/kernel';
63
+ import { createSetupUiRoutes, createUiRoutes, rewriteSetupRequest, } from '@package-broker/core/kernel/ui-routes';
64
+ import { fileURLToPath } from 'node:url';
65
+ import { createNodeAssets } from './assets.js';
66
+ import { nodeClientIp } from './client-ip.js';
67
+ import { nodeCodecs } from './codec/node-codec.js';
68
+ import { reportedVcsProviders } from '@package-broker/core/sources/config';
69
+ import { createDnsGuard } from './sources/dns-guard.js';
70
+ import { nodeGitProviderFor } from './sources/git-provider.js';
71
+ import { createGuardedFetch } from './sources/guarded-fetch.js';
72
+ import { createFilesystemArchiveStore } from './sources/ssh-archive-store.js';
73
+ import { createSshGitProvider } from './sources/ssh-provider.js';
74
+ import { createOrgResolver } from '@package-broker/core/kernel/org';
75
+ import { collectRegistrations, KernelConfigError } from '@package-broker/core/kernel/registry';
76
+ import { mcpEnabled } from '@package-broker/core/config';
77
+ import { registerCoreJob } from '@package-broker/core/runtime/core-jobs';
78
+ import { KeyringMismatchError, KeyringPasswordHashUnreadableError, KeyringStoreUnavailableError, KeyringVerifierMissingError, KeyringVersionInUseError, RESEED_VARIABLE, verifyKeyring, } from '@package-broker/core/secrets/keyring-verifier';
79
+ import { createKeyringVerifierStore } from '@package-broker/db-sqlite/repositories/keyring-verifier';
80
+ import { createBlobAccessRepository } from '@package-broker/db-sqlite/repositories/blob-access';
81
+ import { createBlobReplacer } from '@package-broker/db-sqlite/repositories/blob-replacer';
82
+ import { createCacheEpochRepository } from '@package-broker/db-sqlite/repositories/cache-epoch';
83
+ import { createCatalogueReader } from '@package-broker/db-sqlite/repositories/catalogue';
84
+ import { createCredentialRepository } from '@package-broker/db-sqlite/repositories/credential';
85
+ import { createExistingVersionsReader } from '@package-broker/db-sqlite/repositories/existing-versions';
86
+ import { createFetchLeases, createFetchLeaseWriteGuard, leaseKey, } from '@package-broker/db-sqlite/repositories/fetch-lease';
87
+ import { createIngestFinalizer } from '@package-broker/db-sqlite/repositories/ingest-finalize';
88
+ import { createIngestSessions, createIngestSessionWriteGuard, } from '@package-broker/db-sqlite/repositories/ingest-session';
89
+ import { createPackageIdReads } from '@package-broker/db-sqlite/repositories/package-id';
90
+ import { createRegistryReads } from '@package-broker/db-sqlite/repositories/registry-reads';
91
+ import { createRepresentationClock } from '@package-broker/db-sqlite/repositories/representation-clock';
92
+ import { createSourceRepository } from '@package-broker/db-sqlite/repositories/source';
93
+ import { createUpstreamStateRepository } from '@package-broker/db-sqlite/repositories/upstream-state';
94
+ import { createCatalogueWriter, createVisibilityStore, } from '@package-broker/db-sqlite/repositories/catalogue-writer';
95
+ import { createScopedAdmission } from '@package-broker/db-sqlite/repositories/scoped-admission';
96
+ import { createOrganizationBootstrap, createOrganizationRepository, } from '@package-broker/db-sqlite/repositories/organization';
97
+ function retentionRegistrations(executor, storage) {
98
+ return [retentionScanJob, blobPurgeJob].map((definition) => registerCoreJob({
99
+ ...definition,
100
+ async handler(payload, context) {
101
+ const lease = jobLeaseOf(context);
102
+ if (lease === null)
103
+ throw new JobError('lease_lost');
104
+ const checkSignal = () => {
105
+ if (context.signal.aborted)
106
+ throw new JobError('aborted');
107
+ };
108
+ const guard = jobLeaseGuard(context.org, lease);
109
+ const guarded = {
110
+ driver: executor.driver,
111
+ all: (statement) => executor.all(statement),
112
+ async batch(statements) {
113
+ checkSignal();
114
+ // Guard assertions and metadata writes share one atomic batch.
115
+ const results = await executor.batch([...guard, ...statements]);
116
+ return results.slice(guard.length);
117
+ },
118
+ };
119
+ const { store, deleteTimeoutMs, leaseMs } = storage();
120
+ const metadata = createBlobRepository(guarded, { purgeLeaseSeconds: leaseMs / 1000 });
121
+ const lifecycle = {
122
+ ...metadata,
123
+ async removeBytes(org, key, generation) {
124
+ // A removal that starts after this deadline could outlive the purge lease the recheck saw.
125
+ const deadline = performance.now() + deleteTimeoutMs;
126
+ let leaseLost;
127
+ const removal = createBlobLifecycle({
128
+ metadata,
129
+ store: {
130
+ putVerified: (...args) => store.putVerified(...args),
131
+ head: (...args) => store.head(...args),
132
+ get: (...args) => store.get(...args),
133
+ async remove(storageKey) {
134
+ checkSignal();
135
+ try {
136
+ await executor.batch(guard);
137
+ }
138
+ catch (error) {
139
+ leaseLost = error;
140
+ throw error;
141
+ }
142
+ checkSignal();
143
+ if (performance.now() >= deadline)
144
+ throw new JobError('aborted');
145
+ return store.remove(storageKey);
146
+ },
147
+ },
148
+ deleteTimeoutMs,
149
+ leaseMs,
150
+ observability: context.observability,
151
+ });
152
+ const outcome = await removal.purgeBytes(org, key, generation);
153
+ if (leaseLost !== undefined)
154
+ throw leaseLost;
155
+ return outcome;
156
+ },
157
+ };
158
+ const bound = definition.name === retentionScanJob.name
159
+ ? createRetentionScanJob({ reader: createBlobReferenceReader(guarded), lifecycle })
160
+ : createBlobPurgeJob({ lifecycle });
161
+ await bound.handler(payload, context);
162
+ },
163
+ }));
164
+ }
165
+ const NO_ANCHORS = Object.freeze({
166
+ anchors: () => Promise.resolve(Object.freeze([])),
167
+ });
168
+ function rejectingExecutor() {
169
+ const unavailable = () => Promise.reject(new Error('no SQL executor configured for this runtime'));
170
+ return { driver: 'node-sqlite', all: unavailable, batch: unavailable };
171
+ }
172
+ async function assembleAccess(registration, plugins, auth, executor, log, tokens, clientIp) {
173
+ await auth.keyring.hmacKey('token-pepper', auth.keyring.currentVersion);
174
+ const providers = buildIdentityProviders(registration, plugins, {
175
+ fetch: (request) => fetch(request),
176
+ clock: systemClock,
177
+ env: scopedEnv(auth.env),
178
+ });
179
+ const built = createAccessServices({
180
+ executor,
181
+ keyring: auth.keyring,
182
+ publicOrigin: auth.publicOrigin,
183
+ providers,
184
+ clock: systemClock,
185
+ ids: { ulid },
186
+ log,
187
+ dispatch: deferredDispatch,
188
+ clientIp,
189
+ registration,
190
+ tokens,
191
+ });
192
+ return built;
193
+ }
194
+ function buildPackageReads(executor) {
195
+ const reader = createCatalogueReader(executor);
196
+ const packageAccess = createPackageAccess(reader);
197
+ const resolver = createScopedAdmittingAccess(packageAccess, createScopedAdmission(executor));
198
+ return { reader, packageAccess, resolver };
199
+ }
200
+ const SHUTDOWN_IDLE_MS = 10_000;
201
+ async function settlesWithin(work, deadlineMs) {
202
+ let timer;
203
+ const expired = new Promise((resolve) => {
204
+ timer = setTimeout(() => resolve(false), deadlineMs);
205
+ });
206
+ try {
207
+ return await Promise.race([work.then(() => true), expired]);
208
+ }
209
+ finally {
210
+ clearTimeout(timer);
211
+ }
212
+ }
213
+ const INERT_CREDENTIAL = Object.freeze({
214
+ origins: Object.freeze([]),
215
+ queryParams: Object.freeze([]),
216
+ apply: (headers, url) => ({ headers, url }),
217
+ });
218
+ function lazyArchiveStore(root, maxZipBytes) {
219
+ let opened;
220
+ const open = () => {
221
+ opened ??= createFilesystemArchiveStore(root, { maxZipBytes }).catch((error) => {
222
+ opened = undefined;
223
+ throw error;
224
+ });
225
+ return opened;
226
+ };
227
+ return {
228
+ find: async (...args) => (await open()).find(...args),
229
+ readZip: async (...args) => (await open()).readZip(...args),
230
+ store: async (...args) => (await open()).store(...args),
231
+ };
232
+ }
233
+ export function credentialOrigins(record, policy) {
234
+ return sourceCredentialOrigins(record, { runtime: 'node', policy });
235
+ }
236
+ function credentialAccess(executor, keyring, policy) {
237
+ const repository = createCredentialRepository(executor);
238
+ const store = createCredentialStore({
239
+ repository,
240
+ cipher: createSecretCipher(keyring, 'credential-encryption'),
241
+ ids: { ulid },
242
+ clock: systemClock,
243
+ });
244
+ async function material(ctx, id) {
245
+ const row = await repository.get(ctx, id);
246
+ if (row === null)
247
+ throw new CredentialNotFoundError();
248
+ const plaintext = await store.read(ctx, id);
249
+ try {
250
+ return parseCredentialMaterial(row.kind, plaintext);
251
+ }
252
+ finally {
253
+ plaintext.fill(0);
254
+ }
255
+ }
256
+ return {
257
+ async forSource(ctx, record) {
258
+ const origins = credentialOrigins(record, policy);
259
+ if (record.credentialId === null || origins === null) {
260
+ return null;
261
+ }
262
+ try {
263
+ const opened = await material(ctx, record.credentialId);
264
+ const probe = credentialEchoProbe(opened);
265
+ return {
266
+ credential: (opened.kind === 'ssh_key' && origins.length === 0) ||
267
+ (opened.kind === 'github_app' && record.type === 'vcs' && origins[0] === GITHUB_API)
268
+ ? INERT_CREDENTIAL
269
+ : bindCredential(opened, origins),
270
+ probe,
271
+ };
272
+ }
273
+ catch (error) {
274
+ if (error instanceof CredentialError ||
275
+ error instanceof SecretDecryptError ||
276
+ error instanceof CredentialNotFoundError) {
277
+ return null;
278
+ }
279
+ throw error;
280
+ }
281
+ },
282
+ async sshKey(ctx, credentialId) {
283
+ if (credentialId === null)
284
+ throw new CredentialNotFoundError();
285
+ const opened = await material(ctx, credentialId);
286
+ if (opened.kind !== 'ssh_key')
287
+ throw new CredentialError('invalid', 'kind');
288
+ return opened;
289
+ },
290
+ async githubApp(ctx, credentialId) {
291
+ if (credentialId === null)
292
+ return null;
293
+ try {
294
+ const row = await repository.get(ctx, credentialId);
295
+ if (row === null || row.projectId !== null || row.kind !== 'github_app')
296
+ return null;
297
+ const opened = await material(ctx, credentialId);
298
+ return opened.kind === 'github_app' ? opened : null;
299
+ }
300
+ catch (error) {
301
+ if (error instanceof CredentialError ||
302
+ error instanceof SecretDecryptError ||
303
+ error instanceof CredentialNotFoundError)
304
+ return null;
305
+ throw error;
306
+ }
307
+ },
308
+ };
309
+ }
310
+ function registryJobDependencies(input) {
311
+ const { executor } = input;
312
+ const sources = createSourceRepository(executor);
313
+ const { providers } = input;
314
+ const sourceFetches = new WeakMap();
315
+ const guard = (job) => {
316
+ const lease = jobLeaseOf(job);
317
+ if (lease === null)
318
+ throw new JobError('lease_lost');
319
+ return lease;
320
+ };
321
+ return {
322
+ sourceSync: {
323
+ sources,
324
+ providers: {
325
+ forSource: (source) => {
326
+ const provider = providers.forSource(source);
327
+ const fetch = sourceFetches.get(source);
328
+ if (fetch === undefined)
329
+ throw new JobError('invalid_payload');
330
+ return {
331
+ ...provider,
332
+ ...(provider.listPackages === undefined
333
+ ? {}
334
+ : {
335
+ listPackages: (context, boundSource, cursor) => provider.listPackages({ ...context, fetch }, boundSource, cursor),
336
+ }),
337
+ };
338
+ },
339
+ },
340
+ toProviderSource: (record) => {
341
+ const parsed = parseSourceConfig(record.type, record.ecosystem, record.url, record.configJson, {
342
+ runtime: 'node',
343
+ policy: input.policy,
344
+ hasCredential: record.credentialId !== null,
345
+ });
346
+ if (!parsed.ok)
347
+ throw new JobError('invalid_payload');
348
+ const base = {
349
+ id: record.id,
350
+ ecosystem: record.ecosystem,
351
+ url: record.url,
352
+ config: parsed.config,
353
+ };
354
+ const source = (record.type === 'vcs'
355
+ ? { ...base, type: 'vcs' }
356
+ : {
357
+ ...base,
358
+ type: record.type,
359
+ providerId: providers.get(record.type, record.type === 'composer_repository'
360
+ ? (input.sourceProviders.find((provider) => provider.sourceTypes.includes('composer_repository'))?.id ?? 'composer-repository')
361
+ : undefined).id,
362
+ });
363
+ sourceFetches.set(source, input.fetch(record));
364
+ return source;
365
+ },
366
+ loadCredential: async (ctx, record) => (await input.credential(ctx, record))?.credential ?? null,
367
+ providerContext: (job, credential) => ({
368
+ org: job.org,
369
+ // SourceSyncDeps supplies the source at provider dispatch, where fetch is bound.
370
+ fetch: async () => {
371
+ throw new JobError('invalid_payload');
372
+ },
373
+ credential,
374
+ codecs: nodeCodecs,
375
+ signal: job.signal,
376
+ clock: systemClock,
377
+ observability: job.observability,
378
+ }),
379
+ sessions: createIngestSessions(executor),
380
+ writer: createCatalogueWriter(executor),
381
+ finalizer: createIngestFinalizer(executor),
382
+ writeGuard: (ctx, session, job) => createIngestSessionWriteGuard(ctx, session, guard(job)),
383
+ jobGuard: guard,
384
+ ...createSourceSyncCounts(executor),
385
+ ...createSourceSyncReads(executor),
386
+ },
387
+ sourcePoll: { sources },
388
+ webhook: {
389
+ ...input.webhook,
390
+ deliveries: createWebhookDeliveryRepository(executor),
391
+ },
392
+ };
393
+ }
394
+ function assembleRegistry(input) {
395
+ const { options, seams, executor, auth, platform, observability } = input;
396
+ const { config } = options;
397
+ const policy = createOutboundPolicy({
398
+ allowPrivateHosts: config.outbound.allowPrivateHosts,
399
+ runtime: 'node',
400
+ selfOrigins: config.outbound.selfOrigins,
401
+ });
402
+ const guardOptions = seams.resolver === undefined ? {} : { resolver: seams.resolver };
403
+ const outboundFetch = seams.transport ?? createGuardedFetch(policy, guardOptions);
404
+ const credentials = credentialAccess(executor, auth.keyring, policy);
405
+ const discoveryBindings = new WeakMap();
406
+ seams.observeCredentials?.(credentials);
407
+ const sources = createSourceRepository(executor);
408
+ const reads = createRegistryReads(executor);
409
+ const writer = createCatalogueWriter(executor);
410
+ const sessions = createIngestSessions(executor);
411
+ const blobAccess = createBlobAccessRepository(executor);
412
+ const inFlight = new Set();
413
+ const background = (thunk) => {
414
+ const run = Promise.resolve()
415
+ .then(thunk)
416
+ .catch(() => undefined)
417
+ .finally(() => inFlight.delete(run));
418
+ inFlight.add(run);
419
+ };
420
+ let jobsFactory;
421
+ const unattended = { projectId: null, tokenId: null };
422
+ const coreJobs = () => {
423
+ if (jobsFactory === undefined)
424
+ throw new PlatformUnavailableError();
425
+ return jobsFactory('core', unattended);
426
+ };
427
+ const jobs = seams.jobs ?? {
428
+ enqueue: (ctx, type, payload, enqueueInput) => coreJobs().enqueue(ctx, type, payload, enqueueInput),
429
+ get: (ctx, id) => coreJobs().get(ctx, id),
430
+ };
431
+ const providers = [];
432
+ const gitStagingRoot = options.gitArchiveRoot;
433
+ const providerRegistry = createProviderRegistry();
434
+ providerRegistry.register(createComposerRepositoryProvider(policy));
435
+ const bindSourceProviders = (sourceProviders) => {
436
+ for (const provider of sourceProviders) {
437
+ providerRegistry.register(provider);
438
+ providers.push(providerRegistry.get(provider.sourceTypes[0], provider.id));
439
+ }
440
+ };
441
+ if (options.sshArchiveRoot !== undefined) {
442
+ bindSourceProviders([
443
+ createSshGitProvider({
444
+ dnsGuard: createDnsGuard(policy, guardOptions),
445
+ archives: lazyArchiveStore(options.sshArchiveRoot, config.caps.archiveBytes),
446
+ sshKey: async (source) => {
447
+ const organization = await input.findOrganization();
448
+ if (organization === null)
449
+ throw new CredentialNotFoundError();
450
+ const ctx = orgContext(organization.id);
451
+ const record = await sources.get(ctx, source.id);
452
+ return credentials.sshKey(ctx, record?.credentialId ?? null);
453
+ },
454
+ }),
455
+ ]);
456
+ }
457
+ const recorder = createAccessRecorder({
458
+ touch: (ctx, ids) => blobAccess.touch(ctx, ids),
459
+ defer: background,
460
+ now: () => Date.now(),
461
+ onError: () => undefined,
462
+ });
463
+ const policyFetch = (userAgent, context) => createPolicyFetch({ fetchImpl: outboundFetch, policy, userAgent, observability, ...context });
464
+ const module = createRegistryModule({
465
+ runtime: 'node',
466
+ registryJobs: registryJobDependencies({
467
+ executor,
468
+ providers: providerRegistry,
469
+ sourceProviders: providers,
470
+ policy,
471
+ credential: credentials.forSource,
472
+ webhook: {
473
+ loadCredential: credentials.githubApp,
474
+ verifier: {
475
+ verify: async (...args) => (await providers
476
+ .find((provider) => provider.id === 'github')
477
+ ?.webhookVerifier?.verify(...args)) ?? false,
478
+ },
479
+ },
480
+ fetch: (record) => policyFetch(record.userAgent ??
481
+ (record.ecosystem === 'composer'
482
+ ? config.userAgent.composerStyle
483
+ : config.userAgent.default)),
484
+ }),
485
+ ports: {
486
+ blob: platform.blob,
487
+ cache: platform.cache,
488
+ jobs,
489
+ observability,
490
+ },
491
+ clock: systemClock,
492
+ codecs: nodeCodecs,
493
+ publicUrl: auth.publicOrigin,
494
+ registry: config,
495
+ outboundPolicy: policy,
496
+ policyFetch,
497
+ deploymentId: input.deploymentId,
498
+ providers,
499
+ catalogue: {
500
+ reader: input.reader,
501
+ scopedAdmission: createScopedAdmission(executor),
502
+ epoch: createCacheEpochRepository(executor),
503
+ representationClock: createRepresentationClock(executor),
504
+ upstreamState: createUpstreamStateRepository(executor),
505
+ sources,
506
+ covers: reads.covers,
507
+ versionById: reads.versionById,
508
+ },
509
+ ingest: {
510
+ sessions,
511
+ apply: {
512
+ writer,
513
+ sessions,
514
+ existing: createExistingVersionsReader(executor),
515
+ finalizer: createIngestFinalizer(executor),
516
+ observability: {
517
+ log: (record) => (input.ingestScope.getStore() ?? observability).log(record),
518
+ },
519
+ },
520
+ writeGuard: (ctx, session) => createIngestSessionWriteGuard(ctx, session),
521
+ credential: credentials.forSource,
522
+ credentialUnavailable: (ctx, id) => sources.markCredentialUnavailable(ctx, id),
523
+ },
524
+ sync: {
525
+ packageId: createPackageIdReads(executor).packageId,
526
+ upstreamState: createUpstreamStateRepository(executor),
527
+ executor,
528
+ writeGuard: (ctx, session, job) => createIngestSessionWriteGuard(ctx, session, job),
529
+ },
530
+ dist: {
531
+ leases: createFetchLeases(executor),
532
+ leaseKey,
533
+ writeGuard: (ctx, handle) => createFetchLeaseWriteGuard(ctx, handle),
534
+ writer,
535
+ blobReplacer: createBlobReplacer(executor),
536
+ recorder,
537
+ },
538
+ });
539
+ return {
540
+ module,
541
+ services: Object.freeze({ outboundFetch }),
542
+ sourceProviderContext: {
543
+ runtime: 'docker',
544
+ async discoveryCredential(org, credentialId, installationId) {
545
+ if (!/^[1-9][0-9]{0,19}$/.test(installationId))
546
+ return null;
547
+ if ((await credentials.githubApp(org, credentialId)) === null)
548
+ return null;
549
+ const credential = { ...INERT_CREDENTIAL };
550
+ discoveryBindings.set(credential, { orgId: org.orgId, credentialId, installationId });
551
+ return credential;
552
+ },
553
+ async tokensFor(org, source, create) {
554
+ let credentialId;
555
+ let installationId;
556
+ if ('type' in source) {
557
+ if (source.type !== 'vcs' || source.config.provider !== 'github')
558
+ return null;
559
+ const record = await sources.get(org, source.id);
560
+ if (record === null || record.type !== 'vcs' || record.credentialId === null)
561
+ return null;
562
+ const parsed = parseSourceConfig(record.type, record.ecosystem, record.url, record.configJson, {
563
+ runtime: 'node',
564
+ policy,
565
+ hasCredential: true,
566
+ });
567
+ if (!parsed.ok ||
568
+ !('provider' in parsed.config) ||
569
+ parsed.config.provider !== 'github' ||
570
+ parsed.config.installationId !== source.config.installationId ||
571
+ parsed.config.repository !== source.config.repository ||
572
+ !Number.isSafeInteger(parsed.config.installationId) ||
573
+ (parsed.config.installationId ?? 0) <= 0)
574
+ return null;
575
+ credentialId = record.credentialId;
576
+ installationId = String(parsed.config.installationId);
577
+ }
578
+ else {
579
+ const binding = discoveryBindings.get(source);
580
+ if (binding === undefined || binding.orgId !== org.orgId)
581
+ return null;
582
+ ({ credentialId, installationId } = binding);
583
+ }
584
+ const app = await credentials.githubApp(org, credentialId);
585
+ return app === null
586
+ ? null
587
+ : create({ appId: app.appId, privateKeyPem: app.privateKeyPem }, installationId, credentialId);
588
+ },
589
+ },
590
+ async bindSourceProviders(pluginProviders) {
591
+ const bound = [...pluginProviders];
592
+ bindSourceProviders(bound);
593
+ if (gitStagingRoot === undefined ||
594
+ bound.some((provider) => provider.id === 'git' && provider.sourceTypes.includes('vcs')))
595
+ return;
596
+ const git = await nodeGitProviderFor(gitStagingRoot, (record) => observability.log(record));
597
+ if (git !== null)
598
+ bindSourceProviders([git]);
599
+ },
600
+ vcsProviders: () => reportedVcsProviders('node', providers),
601
+ bindJobs: (factory) => {
602
+ jobsFactory = factory;
603
+ },
604
+ drain: async () => {
605
+ while (inFlight.size > 0)
606
+ await Promise.all([...inFlight]);
607
+ await recorder.flush();
608
+ },
609
+ };
610
+ }
611
+ function buildCatalogue(executor, reads, anchors, log) {
612
+ return Object.freeze({
613
+ reader: reads.reader,
614
+ writer: createCatalogueWriter(executor),
615
+ packageAccess: reads.packageAccess,
616
+ visibility: createVisibilityService({
617
+ store: createVisibilityStore(executor, { providerAnchors: anchors }),
618
+ log,
619
+ }),
620
+ closureReport: (actor, organizationPermissions, ecosystem) => buildClosureReport(reads.reader, actor, organizationPermissions, ecosystem, systemClock),
621
+ });
622
+ }
623
+ function resolveTokenLifetime(input) {
624
+ try {
625
+ return resolveTokenLifetimePolicy(input);
626
+ }
627
+ catch (error) {
628
+ if (error instanceof TokenConfigError)
629
+ throw new KernelConfigError(error.problems);
630
+ throw error;
631
+ }
632
+ }
633
+ export function createNodeServer(plugins, options = {}) {
634
+ const seams = options[SEAMS_KEY] ?? {};
635
+ const shutdownMs = seams.shutdownDeadlineMs ?? SHUTDOWN_IDLE_MS;
636
+ const log = options.log ?? defaultLog;
637
+ const executor = options.executor ?? rejectingExecutor();
638
+ const lifetime = resolveTokenLifetime(options.tokens);
639
+ const remoteAddress = new AsyncLocalStorage();
640
+ const clientIp = (request) => nodeClientIp(request, remoteAddress.getStore() ?? null, options.trustedProxy === true);
641
+ const repository = createOrganizationRepository(executor);
642
+ const bootstrap = createOrganizationBootstrap(executor);
643
+ const ports = Object.freeze({
644
+ organizations: Object.freeze({ get: repository.get }),
645
+ clock: systemClock,
646
+ ids: { ulid },
647
+ });
648
+ const { auth } = options;
649
+ const importJobs = createImportJobHolder();
650
+ const patJobs = createPatJobHolder();
651
+ let drainRegistry;
652
+ const ingestScope = new AsyncLocalStorage();
653
+ let keyringVerification;
654
+ const verify = async () => {
655
+ if (auth !== undefined && options.executor !== undefined) {
656
+ try {
657
+ await verifyKeyring(auth.keyring, createKeyringVerifierStore(executor), {
658
+ reseed: auth.env[RESEED_VARIABLE] === '1',
659
+ });
660
+ }
661
+ catch (error) {
662
+ if (error instanceof KeyringMismatchError ||
663
+ error instanceof KeyringPasswordHashUnreadableError ||
664
+ error instanceof KeyringStoreUnavailableError ||
665
+ error instanceof KeyringVerifierMissingError ||
666
+ error instanceof KeyringVersionInUseError) {
667
+ throw new KernelConfigError(['PB_MASTER_KEY: keyring verification failed']);
668
+ }
669
+ throw error;
670
+ }
671
+ }
672
+ };
673
+ const verifyOnce = () => (keyringVerification ??= verify());
674
+ const application = (async () => {
675
+ // A closed migration gate defers verification until the broker may serve.
676
+ if (options.servingGate === undefined || (await options.servingGate()))
677
+ await verifyOnce();
678
+ const obsSink = options.observability?.sink ??
679
+ filterLogLevel({
680
+ emit: (record) => {
681
+ if (record.kind === 'log')
682
+ log({ ...record.record });
683
+ },
684
+ }, options.observability?.level);
685
+ function registerAuditJob(definition) {
686
+ return registerCoreJob({
687
+ ...definition,
688
+ handler: (payload, context) => definition.handler(definition.validate(payload), {
689
+ ...context,
690
+ observability: createObservability(obsSink, {
691
+ deploymentId: options.observability?.deploymentId ?? 'unconfigured',
692
+ runtime: 'node',
693
+ requestId: null,
694
+ plugin: 'core',
695
+ jobId: context.jobId,
696
+ attempt: context.attempt,
697
+ }, {
698
+ events: AUDIT_EVENTS,
699
+ reasonCodes: new Set(),
700
+ routeTemplates: new Set(),
701
+ }),
702
+ }),
703
+ });
704
+ }
705
+ function registerIngestJob(definition, registry = {
706
+ events: new Set([...INGEST_EVENTS]),
707
+ reasonCodes: INGEST_REASON_CODES,
708
+ routeTemplates: new Set(),
709
+ }) {
710
+ return registerCoreJob({
711
+ ...definition,
712
+ handler: (payload, context) => {
713
+ const observability = createObservability(obsSink, {
714
+ deploymentId: options.observability?.deploymentId ?? 'unconfigured',
715
+ runtime: 'node',
716
+ requestId: null,
717
+ plugin: 'core',
718
+ jobId: context.jobId,
719
+ attempt: context.attempt,
720
+ }, registry);
721
+ return ingestScope.run(observability, () => definition.handler(definition.validate(payload), { ...context, observability }));
722
+ },
723
+ });
724
+ }
725
+ const reads = options.executor === undefined ? undefined : buildPackageReads(executor);
726
+ const deploymentId = options.observability?.deploymentId ?? 'unconfigured';
727
+ const registry = options.registry === undefined ||
728
+ reads === undefined ||
729
+ auth === undefined ||
730
+ options.platformServices === undefined
731
+ ? undefined
732
+ : assembleRegistry({
733
+ options: options.registry,
734
+ seams,
735
+ executor,
736
+ auth,
737
+ platform: options.platformServices,
738
+ reader: reads.reader,
739
+ observability: createObservability(obsSink, { deploymentId, runtime: 'node', requestId: null, plugin: 'core' }, {
740
+ events: new Set([...AUDIT_EVENTS, ...INGEST_EVENTS]),
741
+ reasonCodes: INGEST_REASON_CODES,
742
+ routeTemplates: new Set(),
743
+ }),
744
+ deploymentId,
745
+ findOrganization: bootstrap.findSingleton,
746
+ ingestScope,
747
+ });
748
+ const vulnerabilityPolicy = createOutboundPolicy({
749
+ allowPrivateHosts: [],
750
+ runtime: 'node',
751
+ selfOrigins: auth === undefined ? [] : [auth.publicOrigin],
752
+ });
753
+ const vulnerability = createVulnerabilityRuntime({
754
+ config: {
755
+ platform: 'node',
756
+ plugins,
757
+ ...(options.vulnerability === undefined ? {} : { vulnerability: options.vulnerability }),
758
+ },
759
+ repository: createVulnerabilityRepository(executor, systemClock),
760
+ policyFetch: createPolicyFetch({
761
+ fetchImpl: seams.transport ?? createGuardedFetch(vulnerabilityPolicy),
762
+ policy: vulnerabilityPolicy,
763
+ userAgent: 'PackageBroker/2.0',
764
+ }),
765
+ clock: systemClock,
766
+ });
767
+ drainRegistry = registry?.drain;
768
+ const packageResolver = registry?.module.visibilityPort ?? reads?.resolver;
769
+ const retention = options.platformServices?.retention;
770
+ const registration = collectRegistrations(plugins, {
771
+ ...(registry === undefined ? {} : { sourceProviderContext: registry.sourceProviderContext }),
772
+ ...(packageResolver === undefined ? {} : { packageResolver }),
773
+ coreJobs: options.platformServices === undefined
774
+ ? []
775
+ : [
776
+ ...vulnerability.jobs.map((job) => registerIngestJob(job.definition, {
777
+ events: new Set([VULNERABILITY_EVENT]),
778
+ reasonCodes: new Set(VULNERABILITY_REASON_CODES),
779
+ routeTemplates: new Set(),
780
+ })),
781
+ ...(retention === undefined ? [] : retentionRegistrations(executor, () => retention)),
782
+ registerAuditJob(auditSealJob(createAuditSealRepository(executor))),
783
+ registerAuditJob(auditVerifyJob(createAuditVerifyReader(executor))),
784
+ ...(registry?.module.coreJobs ?? []).map((job) => registerIngestJob(job.definition)),
785
+ ...(registry?.module.packageSyncJob === undefined
786
+ ? []
787
+ : [registerIngestJob(registry.module.packageSyncJob)]),
788
+ ...(auth === undefined ? [] : importJobRegistrations(importJobs)),
789
+ ...(auth === undefined ? [] : patOffboardingRegistrations(executor, patJobs, log)),
790
+ ],
791
+ });
792
+ if (registry !== undefined && options.platformServices !== undefined) {
793
+ await registry.bindSourceProviders(registration.sourceProviders.values());
794
+ registry.bindJobs(createRepositoryJobs({
795
+ store: options.platformServices.jobStore,
796
+ jobs: registration.jobs,
797
+ clock: systemClock,
798
+ ids: { ulid },
799
+ }));
800
+ }
801
+ if (auth !== undefined && options.platformServices !== undefined) {
802
+ patJobs.set(createRepositoryJobs({
803
+ store: options.platformServices.jobStore,
804
+ jobs: registration.jobs,
805
+ clock: systemClock,
806
+ ids: { ulid },
807
+ })('core', { projectId: null, tokenId: null }));
808
+ }
809
+ const access = auth === undefined
810
+ ? undefined
811
+ : await assembleAccess(registration, plugins, auth, executor, log, tokenWiring(lifetime, patJobs, options.platformServices !== undefined), clientIp);
812
+ if (access !== undefined && options.platformServices !== undefined) {
813
+ importJobs.set({
814
+ verificationStore: createImportVerificationStore({
815
+ executor,
816
+ providerAnchors: access.anchors,
817
+ }),
818
+ applyStore: createImportJournalStore({ executor, providerAnchors: access.anchors }),
819
+ rollbackStore: createImportRollbackStore({ executor, providerAnchors: access.anchors }),
820
+ blobs: options.platformServices.blob,
821
+ });
822
+ }
823
+ const packages = access === undefined || reads === undefined
824
+ ? null
825
+ : buildCatalogue(executor, reads, access.anchors, log);
826
+ const apiRoutes = auth === undefined || access === undefined || packages === null
827
+ ? []
828
+ : createApiRoutes({
829
+ clientIp,
830
+ executor,
831
+ runtime: 'node',
832
+ ...(registry?.module.probePackage === undefined
833
+ ? {}
834
+ : { sourcePackageProbe: registry?.module.probePackage }),
835
+ keyring: auth.keyring,
836
+ access,
837
+ packages,
838
+ vulnerability,
839
+ policy: createOutboundPolicy({
840
+ allowPrivateHosts: options.registry?.config.outbound.allowPrivateHosts ?? [],
841
+ runtime: 'node',
842
+ selfOrigins: options.registry?.config.outbound.selfOrigins ?? [auth.publicOrigin],
843
+ }),
844
+ lifetime,
845
+ log,
846
+ blob: options.platformServices?.blob,
847
+ vcsProviders: () => registry?.vcsProviders() ?? [],
848
+ refuseVcsProvider: (provider) => provider === 'git' && !(registry?.vcsProviders() ?? []).includes('git'),
849
+ });
850
+ const authorization = options.authorization ?? access?.module.authorization;
851
+ const assets = await createNodeAssets(fileURLToPath(new URL('./assets/', import.meta.url))).catch((error) => {
852
+ if (error.code !== 'UI_ASSETS_ABSENT')
853
+ throw error;
854
+ log({ level: 'info', event: 'ui.assets_absent' });
855
+ return null;
856
+ });
857
+ const uiRoutes = createUiRoutes({
858
+ manifest: async () => assets?.manifest() ?? null,
859
+ read: async (name) => assets?.read(name) ?? null,
860
+ }, { publicOrigin: auth?.publicOrigin ?? 'http://localhost', log });
861
+ const setupRoutes = createSetupUiRoutes({
862
+ manifest: async () => assets?.manifest() ?? null,
863
+ read: async (name) => assets?.read(name) ?? null,
864
+ }, { publicOrigin: auth?.publicOrigin ?? 'http://localhost', log });
865
+ const coreRoutes = [
866
+ ...(access?.module.coreRoutes ?? []),
867
+ ...apiRoutes,
868
+ openApiRoute(openApiDocument),
869
+ ...(registry?.module.coreRoutes ?? []).map((route) => ({
870
+ ...route,
871
+ async handler(context) {
872
+ const response = await route.handler(context);
873
+ return response.status === 401 ? new Response(null, { status: 404 }) : response;
874
+ },
875
+ })),
876
+ ...(assets === null ? [] : [...setupRoutes, ...uiRoutes]),
877
+ ];
878
+ if (mcpEnabled({
879
+ platform: 'node',
880
+ plugins,
881
+ ...(options.mcp === undefined ? {} : { mcp: options.mcp }),
882
+ })) {
883
+ const publicOrigin = auth?.publicOrigin ?? 'http://localhost';
884
+ const handlers = createMcpTools({
885
+ routes: [...coreRoutes, ...registration.routes],
886
+ publicOrigin,
887
+ kernel: { fetch: (request) => kernel.fetch(request) },
888
+ });
889
+ coreRoutes.push(...createMcpRoutes({ handlers, publicOrigin, limiter: createInProcessMcpLimiter() }));
890
+ }
891
+ const migrations = options.migrations;
892
+ const kernel = await createKernel({
893
+ platform: 'node',
894
+ ...(packageResolver === undefined ? {} : { packageResolver }),
895
+ ...(registry === undefined ? {} : { ecosystemRoutes: registry.module.ecosystemRoutes }),
896
+ plugins,
897
+ publicOrigin: auth?.publicOrigin,
898
+ registration,
899
+ ports,
900
+ resolveOrg: createOrgResolver({ findSingleton: bootstrap.findSingleton }),
901
+ health: async () => (await bootstrap.findSingleton()) !== null,
902
+ ...(migrations === undefined
903
+ ? {}
904
+ : { schemaMatches: () => schemaMigrationsMatch(executor, migrations) }),
905
+ log,
906
+ ...(options.observability === undefined ? {} : { observability: options.observability }),
907
+ ...(options.platformServices === undefined
908
+ ? {}
909
+ : { platformServices: options.platformServices }),
910
+ coreRoutes,
911
+ ...(authorization === undefined ? {} : { authorization }),
912
+ });
913
+ const services = Object.freeze({
914
+ providerAnchors: access?.providerAnchors ?? NO_ANCHORS,
915
+ dispatch: deferredDispatch,
916
+ catalogue: createCatalogueReader(executor),
917
+ packages,
918
+ access: access?.services ?? null,
919
+ ...(registry === undefined ? {} : { registry: registry.services }),
920
+ });
921
+ return { kernel, services };
922
+ })();
923
+ const kernel = application.then((built) => built.kernel);
924
+ const services = application.then((built) => built.services);
925
+ kernel.catch(() => undefined);
926
+ services.catch(() => undefined);
927
+ const gate = options.servingGate;
928
+ const refuse = (request) => {
929
+ const response = envelopeResponse(503, 'not_ready', 'Not ready', crypto.randomUUID());
930
+ return request.method === 'HEAD' ? new Response(null, response) : response;
931
+ };
932
+ const serving = async (request) => gate === undefined || new URL(request.url).pathname === '/health/live' || (await gate());
933
+ const listener = getRequestListener(async (request) => !(await serving(request))
934
+ ? refuse(request)
935
+ : kernel
936
+ .then(async (ready) => {
937
+ if (new URL(request.url).pathname !== '/health/live')
938
+ await verifyOnce();
939
+ const setup = rewriteSetupRequest(request);
940
+ if (setup !== null)
941
+ return ready.fetch(setup);
942
+ return ready.fetch(request);
943
+ })
944
+ .catch((error) => {
945
+ const response = errorResponse(error, crypto.randomUUID(), log);
946
+ return request.method === 'HEAD' ? new Response(null, response) : response;
947
+ }));
948
+ const server = createServer((incoming, outgoing) => remoteAddress.run(incoming.socket.remoteAddress ?? null, () => listener(incoming, outgoing)));
949
+ let closed = false;
950
+ return {
951
+ plugins,
952
+ server,
953
+ ready: () => kernel,
954
+ services: () => services,
955
+ close: async () => {
956
+ if (closed)
957
+ return;
958
+ closed = true;
959
+ try {
960
+ if (server.listening) {
961
+ const closing = new Promise((resolve, reject) => {
962
+ server.close((error) => (error === undefined ? resolve() : reject(error)));
963
+ });
964
+ server.closeIdleConnections();
965
+ await closing;
966
+ }
967
+ const deadline = Date.now() + shutdownMs;
968
+ await kernel.then((ready) => ready.idle(shutdownMs), () => undefined);
969
+ if (drainRegistry !== undefined) {
970
+ const drained = await settlesWithin(drainRegistry(), Math.max(0, deadline - Date.now()));
971
+ if (!drained)
972
+ log({ level: 'warn', event: 'shutdown.registry_drain_timeout' });
973
+ }
974
+ }
975
+ finally {
976
+ await options.closeDatabase?.();
977
+ }
978
+ },
979
+ };
980
+ }
981
+ //# sourceMappingURL=index.js.map