anhedral 0.10.0 → 0.11.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/README.md +78 -39
- package/assets/{anhedral-cli-init-technical.svg → anhedral-stack-technical.svg} +6 -6
- package/assets/{anhedral-cli-init.svg → anhedral-stack.svg} +7 -7
- package/assets/security/audit-deps.mjs +6 -1
- package/dist/bin.js +271 -154
- package/dist/bounded-file.d.ts +1 -0
- package/dist/bounded-file.js +1 -0
- package/dist/capabilities.d.ts +4 -4
- package/dist/capabilities.js +1 -1
- package/dist/capability-contract.d.ts +47 -0
- package/dist/capability-contract.js +136 -0
- package/dist/cli-arguments.d.ts +16 -0
- package/dist/cli-arguments.js +115 -0
- package/dist/cli-output.d.ts +8 -0
- package/dist/cli-output.js +211 -0
- package/dist/cloudflare.d.ts +7 -2
- package/dist/cloudflare.js +166 -36
- package/dist/command-schema.d.ts +21 -0
- package/dist/command-schema.js +374 -0
- package/dist/delivery.d.ts +2 -2
- package/dist/delivery.js +21 -4
- package/dist/dependencies.d.ts +32 -0
- package/dist/dependencies.js +47 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +15 -1
- package/dist/inspector-model.d.ts +115 -0
- package/dist/inspector-model.js +1 -0
- package/dist/lifecycle-model.d.ts +15 -0
- package/dist/lifecycle-model.js +20 -0
- package/dist/lifecycle.d.ts +12 -0
- package/dist/lifecycle.js +92 -0
- package/dist/mobile-delivery.d.ts +100 -0
- package/dist/mobile-delivery.js +319 -0
- package/dist/platforms/desktop.js +4 -5
- package/dist/platforms/extension.js +9 -73
- package/dist/platforms/mobile.js +14 -52
- package/dist/project-inspector.d.ts +4 -0
- package/dist/project-inspector.js +646 -0
- package/dist/project-state.d.ts +17 -0
- package/dist/project-state.js +239 -11
- package/dist/provider-auth.d.ts +41 -0
- package/dist/provider-auth.js +210 -0
- package/dist/providers/adapters.d.ts +17 -0
- package/dist/providers/adapters.js +269 -0
- package/dist/providers/api.d.ts +28 -0
- package/dist/providers/api.js +252 -0
- package/dist/providers/config.d.ts +15 -0
- package/dist/providers/config.js +184 -0
- package/dist/providers/definitions.d.ts +318 -0
- package/dist/providers/definitions.js +127 -0
- package/dist/providers/provision.d.ts +24 -0
- package/dist/providers/provision.js +76 -0
- package/dist/providers/receipts.d.ts +20 -0
- package/dist/providers/receipts.js +66 -0
- package/dist/providers/recovery-verification.d.ts +7 -0
- package/dist/providers/recovery-verification.js +112 -0
- package/dist/providers/recovery.d.ts +30 -0
- package/dist/providers/recovery.js +34 -0
- package/dist/providers/runtime.d.ts +10 -0
- package/dist/providers/runtime.js +231 -0
- package/dist/providers/schema.d.ts +28 -0
- package/dist/providers/schema.js +49 -0
- package/dist/providers/state.d.ts +56 -0
- package/dist/providers/state.js +329 -0
- package/dist/provision.d.ts +1 -1
- package/dist/provision.js +61 -3
- package/dist/readiness.js +11 -3
- package/dist/registry/add.d.ts +6 -0
- package/dist/registry/add.js +35 -4
- package/dist/registry/commands.d.ts +3 -2
- package/dist/registry/commands.js +53 -14
- package/dist/registry/options.js +3 -2
- package/dist/registry/preview.d.ts +11 -0
- package/dist/registry/preview.js +91 -0
- package/dist/registry/receipt.js +1 -0
- package/dist/registry/resolve.js +19 -13
- package/dist/registry/types.d.ts +1 -0
- package/dist/registry/usage.d.ts +10 -0
- package/dist/registry/usage.js +22 -37
- package/dist/render.d.ts +2 -0
- package/dist/render.js +10 -0
- package/dist/scaffold/api.js +20 -3
- package/dist/scaffold/auth.js +68 -28
- package/dist/scaffold/bootstrap.d.ts +2 -0
- package/dist/scaffold/bootstrap.js +9 -0
- package/dist/scaffold/database.js +12 -6
- package/dist/scaffold/documentation.js +11 -8
- package/dist/scaffold/environment.d.ts +2 -0
- package/dist/scaffold/environment.js +18 -1
- package/dist/scaffold/files.d.ts +1 -0
- package/dist/scaffold/files.js +15 -4
- package/dist/scaffold/services.js +37 -13
- package/dist/scaffold/stack-documentation.d.ts +3 -0
- package/dist/scaffold/stack-documentation.js +52 -0
- package/dist/scaffold/styling.d.ts +3 -0
- package/dist/scaffold/styling.js +84 -0
- package/dist/scaffold/web.js +31 -11
- package/dist/scaffold/workspace.js +49 -22
- package/dist/ship.d.ts +320 -0
- package/dist/ship.js +211 -0
- package/dist/stack-registry.d.ts +2 -2
- package/dist/stack-registry.js +44 -11
- package/dist/standard.d.ts +2 -1
- package/dist/standard.js +13 -12
- package/dist/vercel.d.ts +3 -3
- package/dist/vercel.js +27 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/vinext.js +9 -18
- package/dist/workspace-graph.js +3 -2
- package/docs/application-stack-standard.md +51 -80
- package/docs/cli-design.md +67 -0
- package/docs/delivery-workflow.md +29 -15
- package/docs/plugin-ui.md +61 -0
- package/docs/provider-configuration.md +117 -0
- package/package.json +16 -12
- package/dist/managed-hosting.d.ts +0 -49
- package/dist/managed-hosting.js +0 -373
- package/docs/managed-hosting-architecture.md +0 -87
- package/docs/sponsorships.md +0 -25
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
3
|
+
import { readBoundedFile, MAX_PROJECT_JSON_BYTES } from '../bounded-file.js';
|
|
4
|
+
import { validateSetupPlan } from '../readiness.js';
|
|
5
|
+
import { INTEGRATION_PROVIDERS, PROVIDER_DEFINITIONS, PROVIDER_EVENT_PATTERN, PROVIDER_IDENTITY_PATTERN, PROVIDER_TEXT_PATTERN, PROVIDER_WEBHOOK_PATTERN, } from './definitions.js';
|
|
6
|
+
export { INTEGRATION_PROVIDERS } from './definitions.js';
|
|
7
|
+
/** Validate untrusted JSON before assigning provider-specific types. */
|
|
8
|
+
export function validateProviderConfiguration(value, selected = INTEGRATION_PROVIDERS) {
|
|
9
|
+
if (!value ||
|
|
10
|
+
typeof value !== 'object' ||
|
|
11
|
+
Array.isArray(value) ||
|
|
12
|
+
!('schemaVersion' in value) ||
|
|
13
|
+
value.schemaVersion !== 1 ||
|
|
14
|
+
!('providers' in value) ||
|
|
15
|
+
!value.providers ||
|
|
16
|
+
typeof value.providers !== 'object' ||
|
|
17
|
+
Array.isArray(value.providers) ||
|
|
18
|
+
Object.keys(value).some((key) => !['schemaVersion', 'providers'].includes(key)))
|
|
19
|
+
throw new Error('Invalid provider configuration schema.');
|
|
20
|
+
for (const [id, settings] of Object.entries(value.providers)) {
|
|
21
|
+
if (!INTEGRATION_PROVIDERS.includes(id) ||
|
|
22
|
+
!selected.includes(id))
|
|
23
|
+
throw new Error('Provider configuration includes an unsupported or unselected provider.');
|
|
24
|
+
if (!settings || typeof settings !== 'object' || Array.isArray(settings))
|
|
25
|
+
throw new Error(`Invalid ${id} settings.`);
|
|
26
|
+
const definition = PROVIDER_DEFINITIONS[id];
|
|
27
|
+
for (const [key, entry] of Object.entries(settings)) {
|
|
28
|
+
const field = definition.fields[key];
|
|
29
|
+
if (!Object.hasOwn(definition.fields, key) || !field)
|
|
30
|
+
throw new Error(`Unsupported ${id} setting. Credentials belong in the environment.`);
|
|
31
|
+
if (field.kind === 'events') {
|
|
32
|
+
if (!Array.isArray(entry) ||
|
|
33
|
+
entry.length < 1 ||
|
|
34
|
+
entry.length > 100 ||
|
|
35
|
+
new Set(entry).size !== entry.length ||
|
|
36
|
+
entry.some((event) => typeof event !== 'string' || !new RegExp(PROVIDER_EVENT_PATTERN).test(event)))
|
|
37
|
+
throw new Error('Stripe webhook events must be explicit unique event names.');
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (typeof entry !== 'string' || entry.length > 255 || /[\x00-\x1f\x7f]/.test(entry))
|
|
41
|
+
throw new Error(`Invalid ${id} setting.`);
|
|
42
|
+
if (field.choices && !field.choices.includes(entry))
|
|
43
|
+
throw new Error(`Invalid ${id} ${key} choice.`);
|
|
44
|
+
if (field.kind === 'https-url') {
|
|
45
|
+
let url;
|
|
46
|
+
try {
|
|
47
|
+
url = new URL(entry);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
throw new Error('Webhook URL must use HTTPS.');
|
|
51
|
+
}
|
|
52
|
+
if (!new RegExp(PROVIDER_WEBHOOK_PATTERN).test(entry) ||
|
|
53
|
+
url.protocol !== 'https:' ||
|
|
54
|
+
!url.hostname ||
|
|
55
|
+
url.username ||
|
|
56
|
+
url.password ||
|
|
57
|
+
url.hash ||
|
|
58
|
+
url.search)
|
|
59
|
+
throw new Error('Webhook URL must use HTTPS without credentials, query or fragment.');
|
|
60
|
+
}
|
|
61
|
+
else if (!new RegExp(field.kind === 'identity' ? PROVIDER_IDENTITY_PATTERN : PROVIDER_TEXT_PATTERN).test(entry))
|
|
62
|
+
throw new Error(`Invalid ${id} ${field.kind === 'identity' ? 'resource identity' : 'setting'}.`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function providerConfiguration(root) {
|
|
67
|
+
root = realpathSync(root);
|
|
68
|
+
const file = path.join(root, 'anhedral.providers.json');
|
|
69
|
+
const selected = selectedIntegrations(root);
|
|
70
|
+
if (!existsSync(file))
|
|
71
|
+
return { schemaVersion: 1, providers: {} };
|
|
72
|
+
let value;
|
|
73
|
+
try {
|
|
74
|
+
value = JSON.parse(readBoundedFile(file, MAX_PROJECT_JSON_BYTES).toString('utf8'));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
throw new Error('Invalid anhedral.providers.json.');
|
|
78
|
+
}
|
|
79
|
+
validateProviderConfiguration(value, selected);
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
export function setting(settings, key) {
|
|
83
|
+
const value = settings[key];
|
|
84
|
+
return typeof value === 'string' ? value : '';
|
|
85
|
+
}
|
|
86
|
+
/** No identities, domains, prices or organizations are inferred from a user's last session. */
|
|
87
|
+
export function providerRequirements(id, config) {
|
|
88
|
+
const missing = [];
|
|
89
|
+
const need = (key) => {
|
|
90
|
+
if (!setting(config, key))
|
|
91
|
+
missing.push(key);
|
|
92
|
+
};
|
|
93
|
+
const existingOrNew = () => {
|
|
94
|
+
if (!setting(config, 'projectId'))
|
|
95
|
+
need('name');
|
|
96
|
+
};
|
|
97
|
+
switch (id) {
|
|
98
|
+
case 'neon':
|
|
99
|
+
existingOrNew();
|
|
100
|
+
need('organizationId');
|
|
101
|
+
need('database');
|
|
102
|
+
need('role');
|
|
103
|
+
if (!setting(config, 'projectId'))
|
|
104
|
+
need('region');
|
|
105
|
+
break;
|
|
106
|
+
case 'clerk':
|
|
107
|
+
need('workspaceId');
|
|
108
|
+
if (!setting(config, 'applicationId'))
|
|
109
|
+
need('name');
|
|
110
|
+
if (!['development', 'production'].includes(setting(config, 'environment')))
|
|
111
|
+
missing.push('environment');
|
|
112
|
+
break;
|
|
113
|
+
case 'resend':
|
|
114
|
+
need('domain');
|
|
115
|
+
break;
|
|
116
|
+
case 'stripe':
|
|
117
|
+
need('accountId');
|
|
118
|
+
if (!['test', 'live'].includes(setting(config, 'mode')))
|
|
119
|
+
missing.push('mode');
|
|
120
|
+
if (setting(config, 'webhookUrl') && !config.webhookEvents)
|
|
121
|
+
missing.push('webhookEvents');
|
|
122
|
+
if (config.webhookEvents && !setting(config, 'webhookUrl'))
|
|
123
|
+
missing.push('webhookUrl');
|
|
124
|
+
break;
|
|
125
|
+
case 'revenuecat':
|
|
126
|
+
existingOrNew();
|
|
127
|
+
if (![
|
|
128
|
+
'iosAppId',
|
|
129
|
+
'androidAppId',
|
|
130
|
+
'webAppId',
|
|
131
|
+
'stripeAccountId',
|
|
132
|
+
'bundleId',
|
|
133
|
+
'packageName',
|
|
134
|
+
].some((key) => setting(config, key)))
|
|
135
|
+
missing.push('store app identifiers or a connected stripeAccountId');
|
|
136
|
+
break;
|
|
137
|
+
case 'sentry':
|
|
138
|
+
need('organization');
|
|
139
|
+
need('team');
|
|
140
|
+
need('project');
|
|
141
|
+
need('platform');
|
|
142
|
+
break;
|
|
143
|
+
case 'posthog':
|
|
144
|
+
existingOrNew();
|
|
145
|
+
need('organizationId');
|
|
146
|
+
if (!['us', 'eu'].includes(setting(config, 'region')))
|
|
147
|
+
missing.push('region');
|
|
148
|
+
break;
|
|
149
|
+
case 'openai':
|
|
150
|
+
existingOrNew();
|
|
151
|
+
need('serviceAccountName');
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
return missing;
|
|
155
|
+
}
|
|
156
|
+
export function selectedIntegrations(root) {
|
|
157
|
+
const setup = JSON.parse(readBoundedFile(path.join(root, 'anhedral.setup.json'), MAX_PROJECT_JSON_BYTES).toString('utf8'));
|
|
158
|
+
validateSetupPlan(setup);
|
|
159
|
+
return INTEGRATION_PROVIDERS.filter((id) => setup.products.includes(id) || (id === 'openai' && setup.products.includes('ai-sdk')));
|
|
160
|
+
}
|
|
161
|
+
export function providerConfigurationTemplate(products, name) {
|
|
162
|
+
const templates = {
|
|
163
|
+
neon: { name, organizationId: '', region: '', database: 'app', role: 'app_owner' },
|
|
164
|
+
clerk: { name, workspaceId: '', environment: 'development' },
|
|
165
|
+
resend: { domain: '' },
|
|
166
|
+
stripe: { accountId: '', mode: 'test' },
|
|
167
|
+
revenuecat: products.includes('expo')
|
|
168
|
+
? { name, bundleId: '', packageName: '' }
|
|
169
|
+
: { name, stripeAccountId: '' },
|
|
170
|
+
sentry: {
|
|
171
|
+
name,
|
|
172
|
+
organization: '',
|
|
173
|
+
team: '',
|
|
174
|
+
project: name,
|
|
175
|
+
platform: products.includes('next') || products.includes('vinext') ? 'javascript-nextjs' : 'node',
|
|
176
|
+
},
|
|
177
|
+
posthog: { name, organizationId: '', region: '' },
|
|
178
|
+
openai: { name, serviceAccountName: 'anhedral-runtime' },
|
|
179
|
+
};
|
|
180
|
+
return {
|
|
181
|
+
schemaVersion: 1,
|
|
182
|
+
providers: Object.fromEntries(INTEGRATION_PROVIDERS.filter((id) => products.includes(id) || (id === 'openai' && products.includes('ai-sdk'))).map((id) => [id, templates[id]])),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import type { StandardProduct } from '../stack-registry.js';
|
|
2
|
+
/** Public provider configuration describes resources, never credentials. Empty strings are
|
|
3
|
+
* permitted for scaffolding; provisioning separately checks the required choices. */
|
|
4
|
+
export interface NeonSettings {
|
|
5
|
+
projectId?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
organizationId?: string;
|
|
8
|
+
region?: string;
|
|
9
|
+
database?: string;
|
|
10
|
+
role?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ClerkSettings {
|
|
13
|
+
workspaceId?: string;
|
|
14
|
+
applicationId?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
environment?: '' | 'development' | 'production';
|
|
17
|
+
}
|
|
18
|
+
export interface ResendSettings {
|
|
19
|
+
domainId?: string;
|
|
20
|
+
domain?: string;
|
|
21
|
+
region?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface StripeSettings {
|
|
24
|
+
accountId?: string;
|
|
25
|
+
mode?: '' | 'test' | 'live';
|
|
26
|
+
webhookUrl?: string;
|
|
27
|
+
webhookEvents?: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface RevenueCatSettings {
|
|
30
|
+
projectId?: string;
|
|
31
|
+
name?: string;
|
|
32
|
+
iosAppId?: string;
|
|
33
|
+
androidAppId?: string;
|
|
34
|
+
webAppId?: string;
|
|
35
|
+
stripeAccountId?: string;
|
|
36
|
+
bundleId?: string;
|
|
37
|
+
packageName?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface SentrySettings {
|
|
40
|
+
organization?: string;
|
|
41
|
+
team?: string;
|
|
42
|
+
project?: string;
|
|
43
|
+
name?: string;
|
|
44
|
+
platform?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface PostHogSettings {
|
|
47
|
+
organizationId?: string;
|
|
48
|
+
projectId?: string;
|
|
49
|
+
name?: string;
|
|
50
|
+
region?: '' | 'us' | 'eu';
|
|
51
|
+
}
|
|
52
|
+
export interface OpenAISettings {
|
|
53
|
+
projectId?: string;
|
|
54
|
+
name?: string;
|
|
55
|
+
serviceAccountName?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface ProviderSettingsById {
|
|
58
|
+
neon: NeonSettings;
|
|
59
|
+
clerk: ClerkSettings;
|
|
60
|
+
resend: ResendSettings;
|
|
61
|
+
stripe: StripeSettings;
|
|
62
|
+
revenuecat: RevenueCatSettings;
|
|
63
|
+
sentry: SentrySettings;
|
|
64
|
+
posthog: PostHogSettings;
|
|
65
|
+
openai: OpenAISettings;
|
|
66
|
+
}
|
|
67
|
+
export type IntegrationProvider = keyof ProviderSettingsById;
|
|
68
|
+
/** Shared adapter access remains bounded to known fields; provider configuration itself
|
|
69
|
+
* uses ProviderSettingsById so fields cannot leak between providers. */
|
|
70
|
+
export type ProviderSettings = Omit<NeonSettings & ClerkSettings & ResendSettings & StripeSettings & RevenueCatSettings & SentrySettings & PostHogSettings & OpenAISettings, 'region'> & {
|
|
71
|
+
region?: string;
|
|
72
|
+
};
|
|
73
|
+
export type ProviderField = keyof ProviderSettings;
|
|
74
|
+
export type FieldKind = 'text' | 'identity' | 'https-url' | 'events';
|
|
75
|
+
export interface ProviderFieldDefinition {
|
|
76
|
+
kind: FieldKind;
|
|
77
|
+
choices?: readonly string[];
|
|
78
|
+
}
|
|
79
|
+
export interface ProviderDefinition {
|
|
80
|
+
credential: string;
|
|
81
|
+
auth: {
|
|
82
|
+
path: string;
|
|
83
|
+
products?: readonly StandardProduct[];
|
|
84
|
+
runtime?: {
|
|
85
|
+
credential: string;
|
|
86
|
+
path: string;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
origin: string;
|
|
90
|
+
regionalOrigins?: Readonly<Record<string, string>>;
|
|
91
|
+
fields: Partial<Record<ProviderField, ProviderFieldDefinition>>;
|
|
92
|
+
/** Describes adapter scope, not account permissions or production readiness. */
|
|
93
|
+
resources: readonly string[];
|
|
94
|
+
ownerSteps: readonly string[];
|
|
95
|
+
}
|
|
96
|
+
export declare const PROVIDER_DEFINITIONS: {
|
|
97
|
+
readonly neon: {
|
|
98
|
+
readonly credential: "NEON_API_KEY";
|
|
99
|
+
readonly auth: {
|
|
100
|
+
readonly path: "/api/v2/projects";
|
|
101
|
+
};
|
|
102
|
+
readonly origin: "https://console.neon.tech";
|
|
103
|
+
readonly fields: {
|
|
104
|
+
readonly projectId: {
|
|
105
|
+
readonly kind: "identity";
|
|
106
|
+
};
|
|
107
|
+
readonly name: {
|
|
108
|
+
readonly kind: "text";
|
|
109
|
+
};
|
|
110
|
+
readonly organizationId: {
|
|
111
|
+
readonly kind: "identity";
|
|
112
|
+
};
|
|
113
|
+
readonly region: {
|
|
114
|
+
readonly kind: "text";
|
|
115
|
+
};
|
|
116
|
+
readonly database: {
|
|
117
|
+
readonly kind: "text";
|
|
118
|
+
};
|
|
119
|
+
readonly role: {
|
|
120
|
+
readonly kind: "text";
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
readonly resources: readonly ["project", "database connection"];
|
|
124
|
+
readonly ownerSteps: readonly ["Choose an organization and region; approve any provider billing requirements."];
|
|
125
|
+
};
|
|
126
|
+
readonly clerk: {
|
|
127
|
+
readonly credential: "CLERK_PLATFORM_API_KEY";
|
|
128
|
+
readonly auth: {
|
|
129
|
+
readonly path: "/v1/platform/me";
|
|
130
|
+
readonly runtime: {
|
|
131
|
+
readonly credential: "CLERK_SECRET_KEY";
|
|
132
|
+
readonly path: "/v1/users/count";
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
readonly origin: "https://api.clerk.com";
|
|
136
|
+
readonly fields: {
|
|
137
|
+
readonly workspaceId: {
|
|
138
|
+
readonly kind: "identity";
|
|
139
|
+
};
|
|
140
|
+
readonly applicationId: {
|
|
141
|
+
readonly kind: "identity";
|
|
142
|
+
};
|
|
143
|
+
readonly name: {
|
|
144
|
+
readonly kind: "text";
|
|
145
|
+
};
|
|
146
|
+
readonly environment: {
|
|
147
|
+
readonly kind: "text";
|
|
148
|
+
readonly choices: readonly ["", "development", "production"];
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
readonly resources: readonly ["application", "environment keys"];
|
|
152
|
+
readonly ownerSteps: readonly ["Obtain Platform API access; configure production domains, DNS, and OAuth credentials."];
|
|
153
|
+
};
|
|
154
|
+
readonly resend: {
|
|
155
|
+
readonly credential: "RESEND_API_KEY";
|
|
156
|
+
readonly auth: {
|
|
157
|
+
readonly path: "/domains";
|
|
158
|
+
};
|
|
159
|
+
readonly origin: "https://api.resend.com";
|
|
160
|
+
readonly fields: {
|
|
161
|
+
readonly domainId: {
|
|
162
|
+
readonly kind: "identity";
|
|
163
|
+
};
|
|
164
|
+
readonly domain: {
|
|
165
|
+
readonly kind: "text";
|
|
166
|
+
};
|
|
167
|
+
readonly region: {
|
|
168
|
+
readonly kind: "text";
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
readonly resources: readonly ["domain", "sending key"];
|
|
172
|
+
readonly ownerSteps: readonly ["Publish domain verification DNS records and wait for verification."];
|
|
173
|
+
};
|
|
174
|
+
readonly stripe: {
|
|
175
|
+
readonly credential: "STRIPE_SECRET_KEY";
|
|
176
|
+
readonly auth: {
|
|
177
|
+
readonly path: "/v1/account";
|
|
178
|
+
};
|
|
179
|
+
readonly origin: "https://api.stripe.com";
|
|
180
|
+
readonly fields: {
|
|
181
|
+
readonly accountId: {
|
|
182
|
+
readonly kind: "identity";
|
|
183
|
+
};
|
|
184
|
+
readonly mode: {
|
|
185
|
+
readonly kind: "text";
|
|
186
|
+
readonly choices: readonly ["", "test", "live"];
|
|
187
|
+
};
|
|
188
|
+
readonly webhookUrl: {
|
|
189
|
+
readonly kind: "https-url";
|
|
190
|
+
};
|
|
191
|
+
readonly webhookEvents: {
|
|
192
|
+
readonly kind: "events";
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
readonly resources: readonly ["account verification", "webhook"];
|
|
196
|
+
readonly ownerSteps: readonly ["Complete account activation; define products, prices, and purchase behavior."];
|
|
197
|
+
};
|
|
198
|
+
readonly revenuecat: {
|
|
199
|
+
readonly credential: "RC_API_KEY";
|
|
200
|
+
readonly auth: {
|
|
201
|
+
readonly path: "/v2/projects";
|
|
202
|
+
};
|
|
203
|
+
readonly origin: "https://api.revenuecat.com";
|
|
204
|
+
readonly fields: {
|
|
205
|
+
readonly projectId: {
|
|
206
|
+
readonly kind: "identity";
|
|
207
|
+
};
|
|
208
|
+
readonly name: {
|
|
209
|
+
readonly kind: "text";
|
|
210
|
+
};
|
|
211
|
+
readonly iosAppId: {
|
|
212
|
+
readonly kind: "identity";
|
|
213
|
+
};
|
|
214
|
+
readonly androidAppId: {
|
|
215
|
+
readonly kind: "identity";
|
|
216
|
+
};
|
|
217
|
+
readonly webAppId: {
|
|
218
|
+
readonly kind: "identity";
|
|
219
|
+
};
|
|
220
|
+
readonly stripeAccountId: {
|
|
221
|
+
readonly kind: "identity";
|
|
222
|
+
};
|
|
223
|
+
readonly bundleId: {
|
|
224
|
+
readonly kind: "text";
|
|
225
|
+
};
|
|
226
|
+
readonly packageName: {
|
|
227
|
+
readonly kind: "text";
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
readonly resources: readonly ["project", "application", "SDK keys"];
|
|
231
|
+
readonly ownerSteps: readonly ["Configure store credentials or Stripe, products, offerings, and entitlements; verify purchases."];
|
|
232
|
+
};
|
|
233
|
+
readonly sentry: {
|
|
234
|
+
readonly credential: "SENTRY_AUTH_TOKEN";
|
|
235
|
+
readonly auth: {
|
|
236
|
+
readonly path: "/api/0/organizations/";
|
|
237
|
+
};
|
|
238
|
+
readonly origin: "https://sentry.io";
|
|
239
|
+
readonly fields: {
|
|
240
|
+
readonly organization: {
|
|
241
|
+
readonly kind: "identity";
|
|
242
|
+
};
|
|
243
|
+
readonly team: {
|
|
244
|
+
readonly kind: "identity";
|
|
245
|
+
};
|
|
246
|
+
readonly project: {
|
|
247
|
+
readonly kind: "identity";
|
|
248
|
+
};
|
|
249
|
+
readonly name: {
|
|
250
|
+
readonly kind: "text";
|
|
251
|
+
};
|
|
252
|
+
readonly platform: {
|
|
253
|
+
readonly kind: "text";
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
readonly resources: readonly ["project", "DSN"];
|
|
257
|
+
readonly ownerSteps: readonly ["Choose organization and team access; configure source maps and application instrumentation."];
|
|
258
|
+
};
|
|
259
|
+
readonly posthog: {
|
|
260
|
+
readonly credential: "POSTHOG_PERSONAL_API_KEY";
|
|
261
|
+
readonly auth: {
|
|
262
|
+
readonly path: "/api/organizations/";
|
|
263
|
+
};
|
|
264
|
+
readonly origin: "https://us.posthog.com";
|
|
265
|
+
readonly regionalOrigins: {
|
|
266
|
+
readonly us: "https://us.posthog.com";
|
|
267
|
+
readonly eu: "https://eu.posthog.com";
|
|
268
|
+
};
|
|
269
|
+
readonly fields: {
|
|
270
|
+
readonly organizationId: {
|
|
271
|
+
readonly kind: "identity";
|
|
272
|
+
};
|
|
273
|
+
readonly projectId: {
|
|
274
|
+
readonly kind: "identity";
|
|
275
|
+
};
|
|
276
|
+
readonly name: {
|
|
277
|
+
readonly kind: "text";
|
|
278
|
+
};
|
|
279
|
+
readonly region: {
|
|
280
|
+
readonly kind: "text";
|
|
281
|
+
readonly choices: readonly ["", "us", "eu"];
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
readonly resources: readonly ["project", "ingestion configuration"];
|
|
285
|
+
readonly ownerSteps: readonly ["Choose organization and region; define analytics events and consent behavior."];
|
|
286
|
+
};
|
|
287
|
+
readonly openai: {
|
|
288
|
+
readonly credential: "OPENAI_ADMIN_KEY";
|
|
289
|
+
readonly auth: {
|
|
290
|
+
readonly path: "/v1/organization/projects";
|
|
291
|
+
readonly products: readonly ["openai", "ai-sdk"];
|
|
292
|
+
readonly runtime: {
|
|
293
|
+
readonly credential: "OPENAI_API_KEY";
|
|
294
|
+
readonly path: "/v1/models";
|
|
295
|
+
};
|
|
296
|
+
};
|
|
297
|
+
readonly origin: "https://api.openai.com";
|
|
298
|
+
readonly fields: {
|
|
299
|
+
readonly projectId: {
|
|
300
|
+
readonly kind: "identity";
|
|
301
|
+
};
|
|
302
|
+
readonly name: {
|
|
303
|
+
readonly kind: "text";
|
|
304
|
+
};
|
|
305
|
+
readonly serviceAccountName: {
|
|
306
|
+
readonly kind: "text";
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
readonly resources: readonly ["project", "service account", "runtime key"];
|
|
310
|
+
readonly ownerSteps: readonly ["Obtain organization administration access; configure billing and application usage limits."];
|
|
311
|
+
};
|
|
312
|
+
};
|
|
313
|
+
export declare const INTEGRATION_PROVIDERS: IntegrationProvider[];
|
|
314
|
+
export declare const PROVIDER_TEXT_PATTERN = "^(?:[a-zA-Z0-9][a-zA-Z0-9_. -]*)?$";
|
|
315
|
+
export declare const PROVIDER_IDENTITY_PATTERN = "^(?:[a-zA-Z0-9][a-zA-Z0-9_-]{0,254})?$";
|
|
316
|
+
export declare const PROVIDER_EVENT_PATTERN = "^[a-z][a-z0-9_.]{0,100}$";
|
|
317
|
+
/** Schema and runtime use the same pattern; URL parsing also verifies normalization. */
|
|
318
|
+
export declare const PROVIDER_WEBHOOK_PATTERN = "^https://[^/?#@\\\\\\s]+(?:/[^?#\\\\\\s]*)?$";
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const text = { kind: 'text' };
|
|
2
|
+
const identity = { kind: 'identity' };
|
|
3
|
+
export const PROVIDER_DEFINITIONS = {
|
|
4
|
+
neon: {
|
|
5
|
+
credential: 'NEON_API_KEY',
|
|
6
|
+
auth: { path: '/api/v2/projects' },
|
|
7
|
+
origin: 'https://console.neon.tech',
|
|
8
|
+
fields: {
|
|
9
|
+
projectId: identity,
|
|
10
|
+
name: text,
|
|
11
|
+
organizationId: identity,
|
|
12
|
+
region: text,
|
|
13
|
+
database: text,
|
|
14
|
+
role: text,
|
|
15
|
+
},
|
|
16
|
+
resources: ['project', 'database connection'],
|
|
17
|
+
ownerSteps: ['Choose an organization and region; approve any provider billing requirements.'],
|
|
18
|
+
},
|
|
19
|
+
clerk: {
|
|
20
|
+
credential: 'CLERK_PLATFORM_API_KEY',
|
|
21
|
+
auth: {
|
|
22
|
+
path: '/v1/platform/me',
|
|
23
|
+
runtime: { credential: 'CLERK_SECRET_KEY', path: '/v1/users/count' },
|
|
24
|
+
},
|
|
25
|
+
origin: 'https://api.clerk.com',
|
|
26
|
+
fields: {
|
|
27
|
+
workspaceId: identity,
|
|
28
|
+
applicationId: identity,
|
|
29
|
+
name: text,
|
|
30
|
+
environment: { kind: 'text', choices: ['', 'development', 'production'] },
|
|
31
|
+
},
|
|
32
|
+
resources: ['application', 'environment keys'],
|
|
33
|
+
ownerSteps: [
|
|
34
|
+
'Obtain Platform API access; configure production domains, DNS, and OAuth credentials.',
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
resend: {
|
|
38
|
+
credential: 'RESEND_API_KEY',
|
|
39
|
+
auth: { path: '/domains' },
|
|
40
|
+
origin: 'https://api.resend.com',
|
|
41
|
+
fields: { domainId: identity, domain: text, region: text },
|
|
42
|
+
resources: ['domain', 'sending key'],
|
|
43
|
+
ownerSteps: ['Publish domain verification DNS records and wait for verification.'],
|
|
44
|
+
},
|
|
45
|
+
stripe: {
|
|
46
|
+
credential: 'STRIPE_SECRET_KEY',
|
|
47
|
+
auth: { path: '/v1/account' },
|
|
48
|
+
origin: 'https://api.stripe.com',
|
|
49
|
+
fields: {
|
|
50
|
+
accountId: identity,
|
|
51
|
+
mode: { kind: 'text', choices: ['', 'test', 'live'] },
|
|
52
|
+
webhookUrl: { kind: 'https-url' },
|
|
53
|
+
webhookEvents: { kind: 'events' },
|
|
54
|
+
},
|
|
55
|
+
resources: ['account verification', 'webhook'],
|
|
56
|
+
ownerSteps: ['Complete account activation; define products, prices, and purchase behavior.'],
|
|
57
|
+
},
|
|
58
|
+
revenuecat: {
|
|
59
|
+
credential: 'RC_API_KEY',
|
|
60
|
+
auth: { path: '/v2/projects' },
|
|
61
|
+
origin: 'https://api.revenuecat.com',
|
|
62
|
+
fields: {
|
|
63
|
+
projectId: identity,
|
|
64
|
+
name: text,
|
|
65
|
+
iosAppId: identity,
|
|
66
|
+
androidAppId: identity,
|
|
67
|
+
webAppId: identity,
|
|
68
|
+
stripeAccountId: identity,
|
|
69
|
+
bundleId: text,
|
|
70
|
+
packageName: text,
|
|
71
|
+
},
|
|
72
|
+
resources: ['project', 'application', 'SDK keys'],
|
|
73
|
+
ownerSteps: [
|
|
74
|
+
'Configure store credentials or Stripe, products, offerings, and entitlements; verify purchases.',
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
sentry: {
|
|
78
|
+
credential: 'SENTRY_AUTH_TOKEN',
|
|
79
|
+
auth: { path: '/api/0/organizations/' },
|
|
80
|
+
origin: 'https://sentry.io',
|
|
81
|
+
fields: {
|
|
82
|
+
organization: identity,
|
|
83
|
+
team: identity,
|
|
84
|
+
project: identity,
|
|
85
|
+
name: text,
|
|
86
|
+
platform: text,
|
|
87
|
+
},
|
|
88
|
+
resources: ['project', 'DSN'],
|
|
89
|
+
ownerSteps: [
|
|
90
|
+
'Choose organization and team access; configure source maps and application instrumentation.',
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
posthog: {
|
|
94
|
+
credential: 'POSTHOG_PERSONAL_API_KEY',
|
|
95
|
+
auth: { path: '/api/organizations/' },
|
|
96
|
+
origin: 'https://us.posthog.com',
|
|
97
|
+
regionalOrigins: { us: 'https://us.posthog.com', eu: 'https://eu.posthog.com' },
|
|
98
|
+
fields: {
|
|
99
|
+
organizationId: identity,
|
|
100
|
+
projectId: identity,
|
|
101
|
+
name: text,
|
|
102
|
+
region: { kind: 'text', choices: ['', 'us', 'eu'] },
|
|
103
|
+
},
|
|
104
|
+
resources: ['project', 'ingestion configuration'],
|
|
105
|
+
ownerSteps: ['Choose organization and region; define analytics events and consent behavior.'],
|
|
106
|
+
},
|
|
107
|
+
openai: {
|
|
108
|
+
credential: 'OPENAI_ADMIN_KEY',
|
|
109
|
+
auth: {
|
|
110
|
+
path: '/v1/organization/projects',
|
|
111
|
+
products: ['openai', 'ai-sdk'],
|
|
112
|
+
runtime: { credential: 'OPENAI_API_KEY', path: '/v1/models' },
|
|
113
|
+
},
|
|
114
|
+
origin: 'https://api.openai.com',
|
|
115
|
+
fields: { projectId: identity, name: text, serviceAccountName: text },
|
|
116
|
+
resources: ['project', 'service account', 'runtime key'],
|
|
117
|
+
ownerSteps: [
|
|
118
|
+
'Obtain organization administration access; configure billing and application usage limits.',
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
export const INTEGRATION_PROVIDERS = Object.keys(PROVIDER_DEFINITIONS);
|
|
123
|
+
export const PROVIDER_TEXT_PATTERN = '^(?:[a-zA-Z0-9][a-zA-Z0-9_. -]*)?$';
|
|
124
|
+
export const PROVIDER_IDENTITY_PATTERN = '^(?:[a-zA-Z0-9][a-zA-Z0-9_-]{0,254})?$';
|
|
125
|
+
export const PROVIDER_EVENT_PATTERN = '^[a-z][a-z0-9_.]{0,100}$';
|
|
126
|
+
/** Schema and runtime use the same pattern; URL parsing also verifies normalization. */
|
|
127
|
+
export const PROVIDER_WEBHOOK_PATTERN = '^https://[^/?#@\\\\\\s]+(?:/[^?#\\\\\\s]*)?$';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type DeliveryRunner } from '../project-state.js';
|
|
2
|
+
export declare function planProviderProvisioning(root: string): {
|
|
3
|
+
schemaVersion: number;
|
|
4
|
+
providers: {
|
|
5
|
+
provider: keyof import("./definitions.js").ProviderSettingsById;
|
|
6
|
+
settings: import("./definitions.js").NeonSettings | import("./definitions.js").ClerkSettings | import("./definitions.js").ResendSettings | import("./definitions.js").StripeSettings | import("./definitions.js").RevenueCatSettings | import("./definitions.js").SentrySettings | import("./definitions.js").PostHogSettings | import("./definitions.js").OpenAISettings;
|
|
7
|
+
credential: string;
|
|
8
|
+
missing: string[];
|
|
9
|
+
}[];
|
|
10
|
+
configured: boolean;
|
|
11
|
+
mutation: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function provisionSelectedProviders(root: string, dryRun?: boolean, run?: DeliveryRunner, send?: typeof fetch, environment?: NodeJS.ProcessEnv): Promise<{
|
|
14
|
+
schemaVersion: number;
|
|
15
|
+
results: {
|
|
16
|
+
provider: string;
|
|
17
|
+
resources: Record<string, string>;
|
|
18
|
+
ready: boolean;
|
|
19
|
+
nextActions: string[];
|
|
20
|
+
}[];
|
|
21
|
+
ready: boolean;
|
|
22
|
+
configuredFiles: string[];
|
|
23
|
+
mutation: boolean;
|
|
24
|
+
}>;
|