@mondaydotcomorg/z2h-cli 0.27.1 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/__tests__/ast.test.d.ts +2 -0
- package/dist/backend/__tests__/ast.test.d.ts.map +1 -0
- package/dist/backend/__tests__/parse.test.d.ts +2 -0
- package/dist/backend/__tests__/parse.test.d.ts.map +1 -0
- package/dist/backend/__tests__/validate.test.d.ts +2 -0
- package/dist/backend/__tests__/validate.test.d.ts.map +1 -0
- package/dist/backend/ast.d.ts +41 -0
- package/dist/backend/ast.d.ts.map +1 -0
- package/dist/backend/ast.js +152 -0
- package/dist/backend/detect-integrations.d.ts +8 -4
- package/dist/backend/detect-integrations.d.ts.map +1 -1
- package/dist/backend/detect-integrations.js +12 -15
- package/dist/backend/handler-declaration.d.ts +17 -0
- package/dist/backend/handler-declaration.d.ts.map +1 -0
- package/dist/backend/handler-declaration.js +83 -0
- package/dist/backend/handlers.d.ts +2 -0
- package/dist/backend/handlers.d.ts.map +1 -1
- package/dist/backend/handlers.js +7 -36
- package/dist/backend/messages.d.ts +10 -0
- package/dist/backend/messages.d.ts.map +1 -0
- package/dist/backend/messages.js +14 -0
- package/dist/backend/parse.d.ts +11 -0
- package/dist/backend/parse.d.ts.map +1 -0
- package/dist/backend/parse.js +66 -0
- package/dist/backend/rules/__tests__/snowflake-static-sql.test.d.ts +2 -0
- package/dist/backend/rules/__tests__/snowflake-static-sql.test.d.ts.map +1 -0
- package/dist/backend/rules/index.d.ts +5 -0
- package/dist/backend/rules/index.d.ts.map +1 -0
- package/dist/backend/rules/index.js +8 -0
- package/dist/backend/rules/snowflake-static-sql.d.ts +15 -0
- package/dist/backend/rules/snowflake-static-sql.d.ts.map +1 -0
- package/dist/backend/rules/snowflake-static-sql.js +75 -0
- package/dist/backend/rules/types.d.ts +29 -0
- package/dist/backend/rules/types.d.ts.map +1 -0
- package/dist/backend/rules/types.js +1 -0
- package/dist/backend/validate.d.ts +23 -0
- package/dist/backend/validate.d.ts.map +1 -0
- package/dist/backend/validate.js +64 -0
- package/dist/commands/__tests__/backend-validate.test.d.ts +2 -0
- package/dist/commands/__tests__/backend-validate.test.d.ts.map +1 -0
- package/dist/commands/backend.d.ts +6 -0
- package/dist/commands/backend.d.ts.map +1 -1
- package/dist/commands/backend.js +22 -2
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +10 -3
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +2 -1
- package/dist/constants.d.ts +1 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -6
- package/dist/esm/backend/__tests__/ast.test.d.ts +2 -0
- package/dist/esm/backend/__tests__/ast.test.d.ts.map +1 -0
- package/dist/esm/backend/__tests__/parse.test.d.ts +2 -0
- package/dist/esm/backend/__tests__/parse.test.d.ts.map +1 -0
- package/dist/esm/backend/__tests__/validate.test.d.ts +2 -0
- package/dist/esm/backend/__tests__/validate.test.d.ts.map +1 -0
- package/dist/esm/backend/ast.d.ts +41 -0
- package/dist/esm/backend/ast.d.ts.map +1 -0
- package/dist/esm/backend/ast.mjs +146 -0
- package/dist/esm/backend/detect-integrations.d.ts +8 -4
- package/dist/esm/backend/detect-integrations.d.ts.map +1 -1
- package/dist/esm/backend/detect-integrations.mjs +12 -15
- package/dist/esm/backend/handler-declaration.d.ts +17 -0
- package/dist/esm/backend/handler-declaration.d.ts.map +1 -0
- package/dist/esm/backend/handler-declaration.mjs +78 -0
- package/dist/esm/backend/handlers.d.ts +2 -0
- package/dist/esm/backend/handlers.d.ts.map +1 -1
- package/dist/esm/backend/handlers.mjs +7 -36
- package/dist/esm/backend/messages.d.ts +10 -0
- package/dist/esm/backend/messages.d.ts.map +1 -0
- package/dist/esm/backend/messages.mjs +12 -0
- package/dist/esm/backend/parse.d.ts +11 -0
- package/dist/esm/backend/parse.d.ts.map +1 -0
- package/dist/esm/backend/parse.mjs +64 -0
- package/dist/esm/backend/rules/__tests__/snowflake-static-sql.test.d.ts +2 -0
- package/dist/esm/backend/rules/__tests__/snowflake-static-sql.test.d.ts.map +1 -0
- package/dist/esm/backend/rules/index.d.ts +5 -0
- package/dist/esm/backend/rules/index.d.ts.map +1 -0
- package/dist/esm/backend/rules/index.mjs +6 -0
- package/dist/esm/backend/rules/snowflake-static-sql.d.ts +15 -0
- package/dist/esm/backend/rules/snowflake-static-sql.d.ts.map +1 -0
- package/dist/esm/backend/rules/snowflake-static-sql.mjs +73 -0
- package/dist/esm/backend/rules/types.d.ts +29 -0
- package/dist/esm/backend/rules/types.d.ts.map +1 -0
- package/dist/esm/backend/rules/types.mjs +1 -0
- package/dist/esm/backend/validate.d.ts +23 -0
- package/dist/esm/backend/validate.d.ts.map +1 -0
- package/dist/esm/backend/validate.mjs +62 -0
- package/dist/esm/commands/__tests__/backend-validate.test.d.ts +2 -0
- package/dist/esm/commands/__tests__/backend-validate.test.d.ts.map +1 -0
- package/dist/esm/commands/backend.d.ts +6 -0
- package/dist/esm/commands/backend.d.ts.map +1 -1
- package/dist/esm/commands/backend.mjs +23 -4
- package/dist/esm/commands/create.d.ts.map +1 -1
- package/dist/esm/commands/create.mjs +11 -4
- package/dist/esm/commands/generate.d.ts.map +1 -1
- package/dist/esm/commands/generate.mjs +2 -1
- package/dist/esm/constants.d.ts +1 -2
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.mjs +3 -6
- package/dist/esm/index.mjs +6 -2
- package/dist/esm/util/broker/app.d.ts +12 -2
- package/dist/esm/util/broker/app.d.ts.map +1 -1
- package/dist/esm/util/broker/app.mjs +20 -2
- package/dist/index.js +5 -1
- package/dist/util/broker/app.d.ts +12 -2
- package/dist/util/broker/app.d.ts.map +1 -1
- package/dist/util/broker/app.js +20 -1
- package/package.json +4 -2
- package/src/backend/__tests__/ast.test.ts +53 -0
- package/src/backend/__tests__/detect-integrations.test.ts +12 -30
- package/src/backend/__tests__/handlers.test.ts +21 -34
- package/src/backend/__tests__/parse.test.ts +25 -0
- package/src/backend/__tests__/validate.test.ts +246 -0
- package/src/backend/ast.ts +204 -0
- package/src/backend/detect-integrations.ts +12 -14
- package/src/backend/handler-declaration.ts +104 -0
- package/src/backend/handlers.ts +9 -44
- package/src/backend/messages.ts +16 -0
- package/src/backend/parse.ts +82 -0
- package/src/backend/rules/__tests__/snowflake-static-sql.test.ts +175 -0
- package/src/backend/rules/index.ts +7 -0
- package/src/backend/rules/snowflake-static-sql.ts +78 -0
- package/src/backend/rules/types.ts +30 -0
- package/src/backend/validate.ts +73 -0
- package/src/commands/__tests__/backend-deploy.test.ts +5 -3
- package/src/commands/__tests__/backend-validate.test.ts +71 -0
- package/src/commands/__tests__/create.test.ts +46 -0
- package/src/commands/__tests__/edit.test.ts +6 -0
- package/src/commands/backend.ts +23 -3
- package/src/commands/create.ts +13 -4
- package/src/commands/generate.ts +2 -1
- package/src/constants.ts +5 -6
- package/src/index.ts +9 -2
- package/src/util/broker/__tests__/app.test.ts +29 -1
- package/src/util/broker/app.ts +24 -2
|
@@ -78,6 +78,7 @@ describe('edit', () => {
|
|
|
78
78
|
version: 3,
|
|
79
79
|
gitRemote: S3_REMOTE,
|
|
80
80
|
entry: {},
|
|
81
|
+
deployed: true,
|
|
81
82
|
deployedBy: 'u@x.com',
|
|
82
83
|
createdBy: 'u@x.com',
|
|
83
84
|
});
|
|
@@ -100,6 +101,7 @@ describe('edit', () => {
|
|
|
100
101
|
version: 3,
|
|
101
102
|
gitRemote: S3_REMOTE,
|
|
102
103
|
entry: {},
|
|
104
|
+
deployed: true,
|
|
103
105
|
deployedBy: 'u@x.com',
|
|
104
106
|
createdBy: 'u@x.com',
|
|
105
107
|
});
|
|
@@ -117,6 +119,7 @@ describe('edit', () => {
|
|
|
117
119
|
version: 3,
|
|
118
120
|
gitRemote: S3_REMOTE,
|
|
119
121
|
entry: {},
|
|
122
|
+
deployed: true,
|
|
120
123
|
deployedBy: 'u@x.com',
|
|
121
124
|
createdBy: 'u@x.com',
|
|
122
125
|
});
|
|
@@ -134,6 +137,7 @@ describe('edit', () => {
|
|
|
134
137
|
version: 3,
|
|
135
138
|
gitRemote: GITHUB_REMOTE,
|
|
136
139
|
entry: {},
|
|
140
|
+
deployed: true,
|
|
137
141
|
deployedBy: 'u@x.com',
|
|
138
142
|
createdBy: 'u@x.com',
|
|
139
143
|
});
|
|
@@ -151,6 +155,7 @@ describe('edit', () => {
|
|
|
151
155
|
version: 3,
|
|
152
156
|
gitRemote: S3_REMOTE,
|
|
153
157
|
entry: {},
|
|
158
|
+
deployed: true,
|
|
154
159
|
deployedBy: 'u@x.com',
|
|
155
160
|
createdBy: 'u@x.com',
|
|
156
161
|
});
|
|
@@ -172,6 +177,7 @@ describe('edit', () => {
|
|
|
172
177
|
version: 3,
|
|
173
178
|
gitRemote: S3_REMOTE,
|
|
174
179
|
entry: {},
|
|
180
|
+
deployed: true,
|
|
175
181
|
deployedBy: 'u@x.com',
|
|
176
182
|
createdBy: 'u@x.com',
|
|
177
183
|
});
|
package/src/commands/backend.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { PutObjectCommand } from '@aws-sdk/client-s3';
|
|
|
4
4
|
import { info, output } from '../util/logger';
|
|
5
5
|
import { mfAppName, getCwdAppName } from '../util/app-name';
|
|
6
6
|
import { readHandlers } from '../backend/handlers';
|
|
7
|
-
import {
|
|
7
|
+
import { detectIntegrations } from '../backend/detect-integrations';
|
|
8
8
|
import { getS3Client } from '../util/s3/client';
|
|
9
9
|
import { BUCKET_WRITE_NAME } from '../constants';
|
|
10
10
|
import { invokeBackend } from '../backend/client';
|
|
@@ -44,8 +44,8 @@ async function readDetectAndUpload(
|
|
|
44
44
|
return [];
|
|
45
45
|
}
|
|
46
46
|
info(`reading backend handlers ${logLabel}`);
|
|
47
|
-
const { handlers } = await readHandlers(appDir);
|
|
48
|
-
const integrations =
|
|
47
|
+
const { handlers, apiDomains } = await readHandlers(appDir);
|
|
48
|
+
const integrations = detectIntegrations(apiDomains);
|
|
49
49
|
if (Object.keys(handlers).length === 0) {
|
|
50
50
|
return integrations;
|
|
51
51
|
}
|
|
@@ -94,6 +94,26 @@ export async function deployBackend(opts: {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Run the same handler validation deploy runs (syntax, isolate constraints,
|
|
99
|
+
* handler rules, integration detection) against the working tree — without
|
|
100
|
+
* uploading anything. Throws on the first invalid handler.
|
|
101
|
+
*/
|
|
102
|
+
export async function backendValidateCommand(appDir: string = process.cwd()): Promise<void> {
|
|
103
|
+
if (!hasHandlers(appDir)) {
|
|
104
|
+
output('[z2h-cli] no backend/handlers/ directory — nothing to validate', { handlers: [], integrations: [] });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const { handlers, apiDomains } = await readHandlers(appDir);
|
|
108
|
+
const names = Object.keys(handlers).sort();
|
|
109
|
+
const integrations = detectIntegrations(apiDomains);
|
|
110
|
+
const list = (xs: string[]) => (xs.length ? xs.join(', ') : 'none');
|
|
111
|
+
output(`[z2h-cli] ${names.length} handler(s) valid: ${list(names)}\n[z2h-cli] integrations: ${list(integrations)}`, {
|
|
112
|
+
handlers: names,
|
|
113
|
+
integrations,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
97
117
|
export async function backendInvokeCommand(handlerName: string, opts: { input?: string } = {}): Promise<void> {
|
|
98
118
|
const appName = currentAppName();
|
|
99
119
|
let input: unknown = null;
|
package/src/commands/create.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { getTemplatesDir, renderTemplate, APP_TEMPLATES, type AppTemplate } from
|
|
|
7
7
|
import { runYarnInstall } from '../util/npm/install';
|
|
8
8
|
import { initRepo } from '../util/git/repo';
|
|
9
9
|
import { REMOTE_TYPES, type RemoteType, validateRemoteOptions, resolveRemoteUrl } from '../util/git/remote-options';
|
|
10
|
-
import { fetchApp } from '../util/broker/app';
|
|
10
|
+
import { fetchApp, claimApp } from '../util/broker/app';
|
|
11
11
|
import { mfAppName, validateAppName } from '../util/app-name';
|
|
12
12
|
import { prepareShadow } from '../shadow/prepare';
|
|
13
13
|
|
|
@@ -69,12 +69,22 @@ export async function createCommand(appName: string, opts: CreateOptions = {}):
|
|
|
69
69
|
throw new Error(`${appDir} is non-empty. Pick a different name or wipe it first.`);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
// Check the broker — fail fast if the app
|
|
72
|
+
// Check the broker — fail fast if the app already has a real release.
|
|
73
|
+
// A claimed-but-unpublished stub (`entry: null`) is fine to proceed past:
|
|
74
|
+
// either it's this same name being re-created after a local wipe (we'd only
|
|
75
|
+
// see it at all if we still have access to it), or it genuinely doesn't
|
|
76
|
+
// exist yet and we claim it below.
|
|
73
77
|
const existing = await fetchApp(mfAppName(appName));
|
|
74
|
-
if (existing) {
|
|
78
|
+
if (existing?.deployed) {
|
|
75
79
|
throw new Error(`An app named "${appName}" already exists in the Z2H registry. Pick a different name.`);
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
const gitRemote = resolveRemoteUrl(opts.remote as RemoteType, appName, opts.repoUrl);
|
|
83
|
+
|
|
84
|
+
if (!existing) {
|
|
85
|
+
await claimApp(mfAppName(appName), gitRemote);
|
|
86
|
+
}
|
|
87
|
+
|
|
78
88
|
// Compute substitutions.
|
|
79
89
|
const vars = {
|
|
80
90
|
__APP_NAME__: appName,
|
|
@@ -105,7 +115,6 @@ export async function createCommand(appName: string, opts: CreateOptions = {}):
|
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
info(`initializing git repo for ${appName}...`);
|
|
108
|
-
const gitRemote = resolveRemoteUrl(opts.remote as RemoteType, appName, opts.repoUrl);
|
|
109
118
|
await initRepo(appDir, gitRemote);
|
|
110
119
|
|
|
111
120
|
info(`installing dependencies for ${appName}...`);
|
package/src/commands/generate.ts
CHANGED
|
@@ -157,8 +157,9 @@ export async function generateCommand(appName: string, opts: GenerateOptions): P
|
|
|
157
157
|
throw new Error(`${appDir} is non-empty. Pick a different name or wipe it first.`);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
// A claimed-but-unpublished stub (`entry: null`) is fine to proceed past — only a real release blocks generate.
|
|
160
161
|
const existing = await fetchApp(mfAppName(appName));
|
|
161
|
-
if (existing) {
|
|
162
|
+
if (existing?.deployed) {
|
|
162
163
|
throw new Error(`An app named "${appName}" already exists in the Z2H registry. Pick a different name.`);
|
|
163
164
|
}
|
|
164
165
|
|
package/src/constants.ts
CHANGED
|
@@ -70,12 +70,11 @@ export const APP_NAME_PATTERN = /^[a-z][a-z0-9-]{1,49}$/;
|
|
|
70
70
|
|
|
71
71
|
export const PREVIEW_APP_NAME_PREFIX = 'zth-preview-';
|
|
72
72
|
|
|
73
|
-
// ───────────
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
export
|
|
78
|
-
export type DeclaredIntegration = (typeof DECLARED_INTEGRATIONS)[number];
|
|
73
|
+
// ─────────── Backend channels — re-exported from shared-utils ─────
|
|
74
|
+
// Single source of truth lives in `@mondaydotcomorg/z2h-shared-utils` so the
|
|
75
|
+
// CLI detector and broker stay in lockstep. Kept here for existing
|
|
76
|
+
// `@mondaydotcomorg/z2h-cli/constants` consumers.
|
|
77
|
+
export { ALWAYS_ON_CHANNELS, DECLARED_INTEGRATIONS, type DeclaredIntegration } from '@mondaydotcomorg/z2h-shared-utils';
|
|
79
78
|
|
|
80
79
|
// ─────────── Broker origin + npm registry (shared §3 contract) ─────
|
|
81
80
|
// The bigbrain-zth broker fronts both the STS credential route and the npm
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { generateZ2HTokenCommand } from './commands/generate-z2h-token';
|
|
|
17
17
|
import { migrateCommand } from './commands/migrate';
|
|
18
18
|
import { tagCommand } from './commands/tag';
|
|
19
19
|
import { editCommand } from './commands/edit';
|
|
20
|
-
import { backendInvokeCommand } from './commands/backend';
|
|
20
|
+
import { backendInvokeCommand, backendValidateCommand } from './commands/backend';
|
|
21
21
|
import { doctorCommand } from './commands/doctor';
|
|
22
22
|
import { errorLog, fail, getOutputMode, setOutputMode } from './util/logger';
|
|
23
23
|
import { runCommand } from './util/run-command';
|
|
@@ -267,7 +267,14 @@ async function main(): Promise<void> {
|
|
|
267
267
|
|
|
268
268
|
const backend = program
|
|
269
269
|
.command('backend')
|
|
270
|
-
.description("Inspect the app's backend: invoke a deployed handler by name");
|
|
270
|
+
.description("Inspect the app's backend: validate handler sources or invoke a deployed handler by name");
|
|
271
|
+
|
|
272
|
+
backend
|
|
273
|
+
.command('validate')
|
|
274
|
+
.description(
|
|
275
|
+
'Validate backend/handlers/*.js exactly as deploy would (syntax, isolate rules, integrations) without uploading'
|
|
276
|
+
)
|
|
277
|
+
.action(runCommand({ data: cwdApp }, () => backendValidateCommand()));
|
|
271
278
|
|
|
272
279
|
backend
|
|
273
280
|
.command('invoke <handlerName>')
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { registerOrUpdateApp } from '../app';
|
|
2
|
+
import { registerOrUpdateApp, claimApp } from '../app';
|
|
3
3
|
import * as promptVisibility from '../../prompt-visibility';
|
|
4
4
|
import { DeployError } from '../../errors';
|
|
5
|
+
import * as authStore from '../../auth/auth-store';
|
|
5
6
|
import type { ManifestEntry } from '../../manifest';
|
|
6
7
|
|
|
8
|
+
vi.spyOn(authStore, 'readZthAuth').mockResolvedValue({ token: 'jwt-stub' });
|
|
9
|
+
|
|
7
10
|
vi.mock('../../prompt-visibility', () => ({
|
|
8
11
|
resolveAppVisibility: vi.fn(),
|
|
9
12
|
}));
|
|
@@ -105,3 +108,28 @@ describe('registerOrUpdateApp', () => {
|
|
|
105
108
|
expect((err as DeployError).reason).toBe('register_error');
|
|
106
109
|
});
|
|
107
110
|
});
|
|
111
|
+
|
|
112
|
+
describe('claimApp', () => {
|
|
113
|
+
afterEach(() => {
|
|
114
|
+
vi.unstubAllGlobals();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('posts the app name and gitRemote to the claim endpoint', async () => {
|
|
118
|
+
const fetchMock = mockFetchOnce(204);
|
|
119
|
+
|
|
120
|
+
await claimApp('mf-my-app', 's3://bucket/mf-my-app/git');
|
|
121
|
+
|
|
122
|
+
const [calledUrl, init] = fetchMock.mock.calls[0] as [string, RequestInit];
|
|
123
|
+
expect(calledUrl).toContain('/z2h-cli/apps/claim');
|
|
124
|
+
expect(JSON.parse(init.body as string)).toEqual({
|
|
125
|
+
appName: 'mf-my-app',
|
|
126
|
+
gitRemote: 's3://bucket/mf-my-app/git',
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('propagates a claim failure', async () => {
|
|
131
|
+
mockFetchOnce(409);
|
|
132
|
+
|
|
133
|
+
await expect(claimApp('mf-my-app', 's3://bucket/mf-my-app/git')).rejects.toThrow(/Claiming app name failed/);
|
|
134
|
+
});
|
|
135
|
+
});
|
package/src/util/broker/app.ts
CHANGED
|
@@ -8,7 +8,10 @@ import type { ManifestEntry } from '../manifest';
|
|
|
8
8
|
export interface AppInfo {
|
|
9
9
|
appName: string;
|
|
10
10
|
version: number;
|
|
11
|
-
|
|
11
|
+
/** Null for a claimed-but-unpublished app — reserved via `create`, never deployed yet. */
|
|
12
|
+
entry: Record<string, unknown> | null;
|
|
13
|
+
/** True once a real release has landed (`entry` is set) — false for a claimed-but-unpublished stub. */
|
|
14
|
+
deployed: boolean;
|
|
12
15
|
deployedBy: string;
|
|
13
16
|
gitRemote: string;
|
|
14
17
|
createdBy: string;
|
|
@@ -16,7 +19,7 @@ export interface AppInfo {
|
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Fetches a single app's current state from the broker.
|
|
19
|
-
* Returns null if the app has never been
|
|
22
|
+
* Returns null if the app has never even been claimed (404).
|
|
20
23
|
*/
|
|
21
24
|
export async function fetchApp(appName: string): Promise<AppInfo | null> {
|
|
22
25
|
const { token } = await readZthAuth();
|
|
@@ -42,6 +45,25 @@ export async function fetchApp(appName: string): Promise<AppInfo | null> {
|
|
|
42
45
|
return res.json() as Promise<AppInfo>;
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Reserves an app name (+ owner grant) before any deploy — called once from
|
|
50
|
+
* `create`. Idempotent from the caller's perspective: `create` only calls this
|
|
51
|
+
* when {@link fetchApp} found nothing at all, so a 409 here means someone else
|
|
52
|
+
* claimed the exact same name in the gap between the two calls.
|
|
53
|
+
*/
|
|
54
|
+
export async function claimApp(appName: string, gitRemote: string): Promise<void> {
|
|
55
|
+
const { token } = await readZthAuth();
|
|
56
|
+
const res = await fetch(`${brokerBaseUrl()}/z2h-cli/apps/claim`, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...routingHeaders() },
|
|
59
|
+
body: JSON.stringify({ appName, gitRemote }),
|
|
60
|
+
});
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
const text = await res.text();
|
|
63
|
+
throw new Error(`Claiming app name failed (${res.status}): ${text.slice(0, 1024)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
45
67
|
async function registerApp(
|
|
46
68
|
appName: string,
|
|
47
69
|
token: string,
|