@happyvertical/smrt-app-runtime 0.43.9 → 0.43.10
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/AGENTS.md +31 -0
- package/README.md +96 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.js +582 -2
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/smrt-knowledge.json +5 -5
- package/package.json +11 -9
package/AGENTS.md
CHANGED
|
@@ -18,9 +18,40 @@ Application infrastructure composition for the validated runtime profiles in
|
|
|
18
18
|
- Background jobs and application-defined paid capabilities are default-off.
|
|
19
19
|
- The embedded runner reuses `TaskRunner`; it is not a second job contract.
|
|
20
20
|
|
|
21
|
+
## Deployed profiles
|
|
22
|
+
|
|
23
|
+
- `initializeDeployedApplicationRuntime()` composes only `self-hosted` and
|
|
24
|
+
`cloud`; it rejects local initialization and provider/binding mismatches.
|
|
25
|
+
- Provider bindings own credentials, vendor clients, and readiness checks.
|
|
26
|
+
Runtime diagnostics contain selectors and status only.
|
|
27
|
+
- The database binding must expose a provider-owned `close` callback before
|
|
28
|
+
`connect` can run. The runtime owns that cleanup boundary and runs the
|
|
29
|
+
application's explicit, idempotent `prepareDatabase` hook.
|
|
30
|
+
- A failed startup cleanup throws `DeployedRuntimeCleanupError`; callers retain
|
|
31
|
+
and retry its idempotent `retryCleanup()` boundary until it succeeds.
|
|
32
|
+
- Public authentication, asset storage, and secret bindings are mandatory and
|
|
33
|
+
readiness-checked before startup succeeds.
|
|
34
|
+
- `createTaskWorker()` and `createScheduleWorker()` initialize the normal jobs
|
|
35
|
+
package runners against the shared PostgreSQL database. They are intended for
|
|
36
|
+
separate processes; the web process does not start them automatically.
|
|
37
|
+
- `close()` drains in-flight readiness/session/worker initialization and
|
|
38
|
+
serialized runner start/stop operations, stops every runner returned by the
|
|
39
|
+
runtime, and then closes PostgreSQL. Returned runners must not be restarted
|
|
40
|
+
after runtime shutdown begins.
|
|
41
|
+
- A tracked operation may await a re-entrant `close()` request without
|
|
42
|
+
deadlocking; external `close()` callers still await complete cleanup.
|
|
43
|
+
- `health()` is process liveness. `readiness()` probes database/auth/assets/
|
|
44
|
+
secrets; it does not claim that an external worker fleet is running.
|
|
45
|
+
- Database-provider readiness is additive; a PostgreSQL-specific server-version
|
|
46
|
+
probe always gates startup and live readiness.
|
|
47
|
+
- Cloud must keep required tenant context and must never introduce a root or
|
|
48
|
+
unscoped tenant fallback. RLS remains an explicit deployment/migration choice.
|
|
49
|
+
|
|
21
50
|
## Invariants
|
|
22
51
|
|
|
23
52
|
- Never expose application-secret bytes or bootstrap token hashes in diagnostics.
|
|
53
|
+
- Never include provider error text, URLs, tokens, or credentials in deployed
|
|
54
|
+
errors, diagnostics, health, or readiness payloads.
|
|
24
55
|
- Keep local data, assets, database, and secrets outside the source tree.
|
|
25
56
|
- Do not reach into collection/database private fields. Add an upstream public
|
|
26
57
|
API if composition cannot be expressed through exported surfaces.
|
package/README.md
CHANGED
|
@@ -90,3 +90,99 @@ Background jobs and application-defined paid capabilities remain disabled until
|
|
|
90
90
|
explicitly enabled. With embedded job topology, `createEmbeddedJobRunner()`
|
|
91
91
|
returns the normal s-m-r-t `TaskRunner`, so the application keeps one enqueue
|
|
92
92
|
and execution contract without requiring a separate worker service.
|
|
93
|
+
|
|
94
|
+
## Self-hosted and cloud applications
|
|
95
|
+
|
|
96
|
+
The deployed initializer validates the selected profile against concrete,
|
|
97
|
+
provider-owned bindings. Database URLs, OIDC credentials, storage keys, and
|
|
98
|
+
secret-manager identifiers stay inside those adapters and are never copied into
|
|
99
|
+
the runtime snapshot.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import {
|
|
103
|
+
initializeDeployedApplicationRuntime,
|
|
104
|
+
} from '@happyvertical/smrt-app-runtime';
|
|
105
|
+
import { getDatabase } from '@happyvertical/sql';
|
|
106
|
+
|
|
107
|
+
const initialized = await initializeDeployedApplicationRuntime({
|
|
108
|
+
profile: 'self-hosted',
|
|
109
|
+
database: {
|
|
110
|
+
engine: 'postgres',
|
|
111
|
+
connect: () => getDatabase({
|
|
112
|
+
type: 'postgres',
|
|
113
|
+
url: requirePrivateSetting('DATABASE_URL'),
|
|
114
|
+
}),
|
|
115
|
+
close: async (db) => {
|
|
116
|
+
await db.close?.();
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
authentication: {
|
|
120
|
+
provider: 'oidc',
|
|
121
|
+
readiness: () => oidcProvider.assertReady(),
|
|
122
|
+
},
|
|
123
|
+
assets: {
|
|
124
|
+
provider: 's3-compatible',
|
|
125
|
+
readiness: () => assetProvider.assertReady(),
|
|
126
|
+
},
|
|
127
|
+
secrets: {
|
|
128
|
+
provider: 'environment',
|
|
129
|
+
readiness: () => secretProvider.assertReady(),
|
|
130
|
+
},
|
|
131
|
+
prepareDatabase: runApplicationMigrations,
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Startup validates every binding, including the provider-owned database cleanup
|
|
136
|
+
boundary, before opening a connection. A missing public-auth or secret binding,
|
|
137
|
+
a selector mismatch, an unavailable provider, a failed
|
|
138
|
+
PostgreSQL probe, or a failed migration rejects startup. Provider failures are
|
|
139
|
+
reported with stable component codes and omit the underlying provider message
|
|
140
|
+
so credentials cannot leak into HTTP or orchestration payloads.
|
|
141
|
+
A custom database readiness callback is additive; the runtime always runs its
|
|
142
|
+
own PostgreSQL-specific server-version probe before startup or readiness can
|
|
143
|
+
succeed.
|
|
144
|
+
If cleanup after a startup failure also fails,
|
|
145
|
+
`DeployedRuntimeCleanupError.retryCleanup()` retains the redacted, idempotent
|
|
146
|
+
ownership path until the provider closes successfully.
|
|
147
|
+
|
|
148
|
+
`health()` reports process liveness. `readiness()` rechecks PostgreSQL,
|
|
149
|
+
authentication, assets, and secrets and returns only `ready` / `not-ready`
|
|
150
|
+
component states. `diagnostics()` reports the resolved profile, explicit
|
|
151
|
+
provider selectors, tenancy posture, and worker topology without secret values.
|
|
152
|
+
|
|
153
|
+
Job producers keep using `SmrtObject.bg()` / `background().enqueue()` in every
|
|
154
|
+
profile. Deployed worker entry points initialize the ordinary runners against
|
|
155
|
+
the same PostgreSQL database:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const taskWorker = await initialized.createTaskWorker({ concurrency: 8 });
|
|
159
|
+
await taskWorker.start();
|
|
160
|
+
|
|
161
|
+
// Run this in a separate schedule-worker process, not beside the web server.
|
|
162
|
+
const scheduleWorker = await initialized.createScheduleWorker();
|
|
163
|
+
await scheduleWorker.start();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`initialized.close()` drains in-flight readiness/session/worker initialization
|
|
167
|
+
and serialized runner start/stop operations, stops every runner returned by
|
|
168
|
+
this runtime, and only then closes PostgreSQL.
|
|
169
|
+
Callers may stop a runner earlier, but must not restart a returned runner after
|
|
170
|
+
its runtime has begun shutdown; lifecycle-gated `start()` calls then reject.
|
|
171
|
+
An awaited `close()` request made from inside a tracked provider or worker
|
|
172
|
+
operation acknowledges shutdown so that operation can unwind; callers outside
|
|
173
|
+
the operation continue to await complete worker and database cleanup.
|
|
174
|
+
|
|
175
|
+
Self-hosted deployments may select OIDC or magic-link authentication, explicit
|
|
176
|
+
single- or multi-tenancy, local or S3-compatible assets, and environment,
|
|
177
|
+
local-file, or external secrets. Cloud requires hosted identity, PostgreSQL,
|
|
178
|
+
multi-tenancy with required tenant context, managed/external secrets,
|
|
179
|
+
managed/S3-compatible object storage, public TLS, and scalable workers. Cloud
|
|
180
|
+
may select application isolation instead of PostgreSQL RLS, but it can never
|
|
181
|
+
enable an unscoped/root-tenant fallback.
|
|
182
|
+
|
|
183
|
+
The application runtime does not provision databases, buckets, identity
|
|
184
|
+
providers, secret managers, worker fleets, TLS, billing, or a hosted control
|
|
185
|
+
plane. Those remain operator/managed-platform responsibilities. Enabling the
|
|
186
|
+
`database-rls` selector also requires the deployment migration to apply the
|
|
187
|
+
documented s-m-r-t PostgreSQL policies; the selector does not grant or mutate
|
|
188
|
+
database privileges at runtime.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import { ApplicationRuntimeProfile } from '@happyvertical/smrt-config';
|
|
2
|
+
import { AssetStorageProvider } from '@happyvertical/smrt-config';
|
|
3
|
+
import { AuthenticationProvider } from '@happyvertical/smrt-config';
|
|
1
4
|
import { DatabaseInterface } from '@happyvertical/sql';
|
|
2
5
|
import { ResolvedApplicationRuntime } from '@happyvertical/smrt-config';
|
|
3
6
|
import { RuntimeProviderOverrides } from '@happyvertical/smrt-config';
|
|
7
|
+
import { ScheduleRunner } from '@happyvertical/smrt-jobs';
|
|
8
|
+
import { ScheduleRunnerConfig } from '@happyvertical/smrt-jobs';
|
|
9
|
+
import { SecretProvider } from '@happyvertical/smrt-config';
|
|
4
10
|
import { SessionService } from '@happyvertical/smrt-users';
|
|
5
11
|
import { TaskRunner } from '@happyvertical/smrt-jobs';
|
|
6
12
|
import { TaskRunnerConfig } from '@happyvertical/smrt-jobs';
|
|
@@ -14,6 +20,131 @@ export declare interface ClaimLocalOwnerInput {
|
|
|
14
20
|
ipAddress?: string;
|
|
15
21
|
}
|
|
16
22
|
|
|
23
|
+
export declare interface DeployedApplicationRuntime {
|
|
24
|
+
readonly db: DatabaseInterface;
|
|
25
|
+
readonly resolvedRuntime: ResolvedApplicationRuntime;
|
|
26
|
+
restoreSession(sessionId: string): ReturnType<SessionService['loadSessionContext']>;
|
|
27
|
+
createTaskWorker(config?: TaskRunnerConfig): Promise<TaskRunner>;
|
|
28
|
+
createScheduleWorker(config?: ScheduleRunnerConfig): Promise<ScheduleRunner>;
|
|
29
|
+
diagnostics(): DeployedRuntimeDiagnostics;
|
|
30
|
+
health(): DeployedRuntimeHealth;
|
|
31
|
+
readiness(): Promise<DeployedRuntimeReadiness>;
|
|
32
|
+
close(): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export declare interface DeployedApplicationRuntimeOptions {
|
|
36
|
+
readonly profile: DeployedApplicationRuntimeProfile;
|
|
37
|
+
/** Validated selector overrides; never put credentials in this object. */
|
|
38
|
+
readonly providers?: RuntimeProviderOverrides;
|
|
39
|
+
readonly database: DeployedDatabaseBinding;
|
|
40
|
+
readonly authentication: DeployedProviderBinding<PublicAuthenticationProvider>;
|
|
41
|
+
readonly assets: DeployedProviderBinding<AssetStorageProvider>;
|
|
42
|
+
readonly secrets: DeployedProviderBinding<SecretProvider>;
|
|
43
|
+
/** Explicit, idempotent application migration hook. */
|
|
44
|
+
readonly prepareDatabase?: (db: DatabaseInterface) => Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export declare type DeployedApplicationRuntimeProfile = Exclude<ApplicationRuntimeProfile, 'local'>;
|
|
48
|
+
|
|
49
|
+
export declare interface DeployedComponentReadiness {
|
|
50
|
+
readonly status: 'ready' | 'not-ready';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** PostgreSQL connection factory. URLs and credentials stay in this adapter. */
|
|
54
|
+
export declare interface DeployedDatabaseBinding {
|
|
55
|
+
readonly engine: 'postgres';
|
|
56
|
+
readonly connect: () => Promise<DatabaseInterface>;
|
|
57
|
+
readonly readiness?: (db: DatabaseInterface) => Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Provider-owned cleanup boundary, required before a connection is opened.
|
|
60
|
+
* Keeping cleanup on the binding also covers malformed connection handles.
|
|
61
|
+
*/
|
|
62
|
+
readonly close: (db: DatabaseInterface) => Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A provider-owned readiness boundary.
|
|
67
|
+
*
|
|
68
|
+
* The callback should prove that required configuration is present and that
|
|
69
|
+
* the provider is usable. It must not return credentials or secret values.
|
|
70
|
+
*/
|
|
71
|
+
export declare interface DeployedProviderBinding<Provider extends string> {
|
|
72
|
+
readonly provider: Provider;
|
|
73
|
+
readonly readiness: () => Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export declare interface DeployedProviderDiagnostic {
|
|
77
|
+
readonly provider: string;
|
|
78
|
+
readonly configured: true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Startup failure that retains the only safe retry path for database cleanup. */
|
|
82
|
+
export declare class DeployedRuntimeCleanupError extends DeployedRuntimeError {
|
|
83
|
+
private readonly cleanup;
|
|
84
|
+
private cleaned;
|
|
85
|
+
private cleanupAttempt?;
|
|
86
|
+
constructor(cleanup: () => Promise<void>);
|
|
87
|
+
retryCleanup(): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export declare type DeployedRuntimeComponent = 'database' | 'authentication' | 'assets' | 'secrets';
|
|
91
|
+
|
|
92
|
+
export declare interface DeployedRuntimeDiagnostics {
|
|
93
|
+
readonly schemaVersion: 1;
|
|
94
|
+
readonly runtime: ResolvedApplicationRuntime;
|
|
95
|
+
readonly providers: {
|
|
96
|
+
readonly database: DeployedProviderDiagnostic;
|
|
97
|
+
readonly authentication: DeployedProviderDiagnostic;
|
|
98
|
+
readonly assets: DeployedProviderDiagnostic;
|
|
99
|
+
readonly secrets: DeployedProviderDiagnostic;
|
|
100
|
+
};
|
|
101
|
+
readonly tenancy: {
|
|
102
|
+
readonly mode: 'single-tenant' | 'multi-tenant';
|
|
103
|
+
readonly context: 'defaulted' | 'required';
|
|
104
|
+
readonly isolation: 'application' | 'database-rls';
|
|
105
|
+
readonly rootTenantFallback: 'disabled';
|
|
106
|
+
};
|
|
107
|
+
readonly workers: {
|
|
108
|
+
readonly topology: 'external' | 'scalable';
|
|
109
|
+
readonly taskProcess: 'separate';
|
|
110
|
+
readonly scheduleProcess: 'separate';
|
|
111
|
+
readonly replicas: 'operator-managed' | 'horizontal';
|
|
112
|
+
};
|
|
113
|
+
readonly secretValuesIncluded: false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Stable failure that never includes a provider's underlying error text. */
|
|
117
|
+
export declare class DeployedRuntimeError extends Error {
|
|
118
|
+
readonly code: DeployedRuntimeErrorCode;
|
|
119
|
+
readonly component?: DeployedRuntimeComponent | undefined;
|
|
120
|
+
constructor(code: DeployedRuntimeErrorCode, message: string, component?: DeployedRuntimeComponent | undefined);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export declare type DeployedRuntimeErrorCode = 'invalid_configuration' | 'provider_mismatch' | 'provider_unavailable' | 'tenant_context_required' | 'tenant_context_unauthorized' | 'runtime_stopped';
|
|
124
|
+
|
|
125
|
+
export declare interface DeployedRuntimeHealth {
|
|
126
|
+
readonly schemaVersion: 1;
|
|
127
|
+
readonly profile: DeployedApplicationRuntimeProfile;
|
|
128
|
+
readonly status: 'healthy' | 'stopped';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export declare interface DeployedRuntimeReadiness {
|
|
132
|
+
readonly schemaVersion: 1;
|
|
133
|
+
readonly profile: DeployedApplicationRuntimeProfile;
|
|
134
|
+
readonly status: 'ready' | 'not-ready';
|
|
135
|
+
readonly components: Readonly<Record<DeployedRuntimeComponent, DeployedComponentReadiness>>;
|
|
136
|
+
readonly secretValuesIncluded: false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Initialize a self-hosted or cloud application composition.
|
|
141
|
+
*
|
|
142
|
+
* Configuration and provider identity are validated before a connection is
|
|
143
|
+
* opened. Provider readiness and database migrations complete before the
|
|
144
|
+
* runtime is returned. A failed initialization closes the acquired connection.
|
|
145
|
+
*/
|
|
146
|
+
export declare function initializeDeployedApplicationRuntime(options: DeployedApplicationRuntimeOptions): Promise<DeployedApplicationRuntime>;
|
|
147
|
+
|
|
17
148
|
/**
|
|
18
149
|
* Initialize or reopen a local application runtime.
|
|
19
150
|
*
|
|
@@ -132,6 +263,8 @@ export declare interface LocalRuntimePaths {
|
|
|
132
263
|
readonly applicationSecret: string;
|
|
133
264
|
}
|
|
134
265
|
|
|
266
|
+
export declare type PublicAuthenticationProvider = Exclude<AuthenticationProvider, 'owner-bootstrap'>;
|
|
267
|
+
|
|
135
268
|
/**
|
|
136
269
|
* Resolve deterministic user-owned paths and reject source-tree placement.
|
|
137
270
|
*/
|