@mondaydotcomorg/z2h-cli 0.30.0 → 0.32.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/handlers.d.ts +3 -0
- package/dist/backend/handlers.d.ts.map +1 -1
- package/dist/backend/handlers.js +7 -4
- package/dist/backend/parser/__tests__/ast.test.d.ts.map +1 -0
- package/dist/backend/parser/__tests__/parse.test.d.ts.map +1 -0
- package/dist/backend/parser/api-signatures.d.ts +4 -0
- package/dist/backend/parser/api-signatures.d.ts.map +1 -0
- package/dist/backend/parser/api-signatures.js +31 -0
- package/dist/backend/parser/api-usage.d.ts +5 -0
- package/dist/backend/parser/api-usage.d.ts.map +1 -0
- package/dist/backend/parser/api-usage.js +61 -0
- package/dist/backend/parser/bindings.d.ts +5 -0
- package/dist/backend/parser/bindings.d.ts.map +1 -0
- package/dist/backend/parser/bindings.js +84 -0
- package/dist/backend/parser/handler-declaration.d.ts.map +1 -0
- package/dist/backend/parser/member-path.d.ts +3 -0
- package/dist/backend/parser/member-path.d.ts.map +1 -0
- package/dist/backend/parser/member-path.js +31 -0
- package/dist/backend/parser/messages.d.ts.map +1 -0
- package/dist/backend/parser/parse.d.ts.map +1 -0
- package/dist/backend/{parse.js → parser/parse.js} +9 -9
- package/dist/backend/parser/static-value.d.ts +6 -0
- package/dist/backend/parser/static-value.d.ts.map +1 -0
- package/dist/backend/parser/static-value.js +117 -0
- package/dist/backend/parser/types.d.ts +53 -0
- package/dist/backend/parser/types.d.ts.map +1 -0
- package/dist/backend/parser/types.js +1 -0
- package/dist/backend/parser/utils.d.ts +19 -0
- package/dist/backend/parser/utils.d.ts.map +1 -0
- package/dist/backend/parser/utils.js +97 -0
- package/dist/backend/validate.d.ts +3 -3
- package/dist/backend/validate.d.ts.map +1 -1
- package/dist/backend/validate.js +18 -18
- package/dist/commands/__tests__/llm-migration.test.d.ts +2 -0
- package/dist/commands/__tests__/llm-migration.test.d.ts.map +1 -0
- package/dist/commands/backend.d.ts +2 -13
- package/dist/commands/backend.d.ts.map +1 -1
- package/dist/commands/backend.js +30 -21
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +1 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +2 -8
- package/dist/commands/llm-migration.d.ts +6 -0
- package/dist/commands/llm-migration.d.ts.map +1 -0
- package/dist/commands/llm-migration.js +33 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +6 -13
- package/dist/esm/backend/handlers.d.ts +3 -0
- package/dist/esm/backend/handlers.d.ts.map +1 -1
- package/dist/esm/backend/handlers.mjs +7 -4
- package/dist/esm/backend/parser/__tests__/ast.test.d.ts.map +1 -0
- package/dist/esm/backend/parser/__tests__/parse.test.d.ts.map +1 -0
- package/dist/esm/backend/parser/api-signatures.d.ts +4 -0
- package/dist/esm/backend/parser/api-signatures.d.ts.map +1 -0
- package/dist/esm/backend/parser/api-signatures.mjs +24 -0
- package/dist/esm/backend/parser/api-usage.d.ts +5 -0
- package/dist/esm/backend/parser/api-usage.d.ts.map +1 -0
- package/dist/esm/backend/parser/api-usage.mjs +58 -0
- package/dist/esm/backend/parser/bindings.d.ts +5 -0
- package/dist/esm/backend/parser/bindings.d.ts.map +1 -0
- package/dist/esm/backend/parser/bindings.mjs +81 -0
- package/dist/esm/backend/parser/handler-declaration.d.ts.map +1 -0
- package/dist/esm/backend/parser/member-path.d.ts +3 -0
- package/dist/esm/backend/parser/member-path.d.ts.map +1 -0
- package/dist/esm/backend/parser/member-path.mjs +29 -0
- package/dist/esm/backend/parser/messages.d.ts.map +1 -0
- package/dist/esm/backend/parser/parse.d.ts.map +1 -0
- package/dist/esm/backend/{parse.mjs → parser/parse.mjs} +1 -1
- package/dist/esm/backend/parser/static-value.d.ts +6 -0
- package/dist/esm/backend/parser/static-value.d.ts.map +1 -0
- package/dist/esm/backend/parser/static-value.mjs +114 -0
- package/dist/esm/backend/parser/types.d.ts +53 -0
- package/dist/esm/backend/parser/types.d.ts.map +1 -0
- package/dist/esm/backend/parser/types.mjs +1 -0
- package/dist/esm/backend/parser/utils.d.ts +19 -0
- package/dist/esm/backend/parser/utils.d.ts.map +1 -0
- package/dist/esm/backend/parser/utils.mjs +88 -0
- package/dist/esm/backend/validate.d.ts +3 -3
- package/dist/esm/backend/validate.d.ts.map +1 -1
- package/dist/esm/backend/validate.mjs +8 -8
- package/dist/esm/commands/__tests__/llm-migration.test.d.ts +2 -0
- package/dist/esm/commands/__tests__/llm-migration.test.d.ts.map +1 -0
- package/dist/esm/commands/backend.d.ts +2 -13
- package/dist/esm/commands/backend.d.ts.map +1 -1
- package/dist/esm/commands/backend.mjs +31 -22
- package/dist/esm/commands/create.d.ts.map +1 -1
- package/dist/esm/commands/create.mjs +1 -0
- package/dist/esm/commands/deploy.d.ts.map +1 -1
- package/dist/esm/commands/deploy.mjs +3 -8
- package/dist/esm/commands/llm-migration.d.ts +6 -0
- package/dist/esm/commands/llm-migration.d.ts.map +1 -0
- package/dist/esm/commands/llm-migration.mjs +27 -0
- package/dist/esm/commands/migrate.d.ts.map +1 -1
- package/dist/esm/commands/migrate.mjs +6 -12
- package/dist/esm/index.mjs +6 -0
- package/dist/esm/templates/app/usage.mjs +5 -0
- package/dist/esm/util/machine-identity.d.ts +2 -3
- package/dist/esm/util/machine-identity.d.ts.map +1 -1
- package/dist/esm/util/machine-identity.mjs +7 -4
- package/dist/esm/util/migrations-json.d.ts +28 -0
- package/dist/esm/util/migrations-json.d.ts.map +1 -0
- package/dist/esm/util/migrations-json.mjs +17 -0
- package/dist/index.js +6 -0
- package/dist/templates/app/usage.js +7 -0
- package/dist/util/machine-identity.d.ts +2 -3
- package/dist/util/machine-identity.d.ts.map +1 -1
- package/dist/util/machine-identity.js +11 -3
- package/dist/util/migrations-json.d.ts +28 -0
- package/dist/util/migrations-json.d.ts.map +1 -0
- package/dist/util/migrations-json.js +25 -0
- package/handler-api/signatures.json +26 -0
- package/migrations.json +6 -0
- package/package.json +1 -1
- package/src/backend/__tests__/handlers.test.ts +13 -2
- package/src/backend/__tests__/validate.test.ts +54 -0
- package/src/backend/handlers.ts +10 -5
- package/src/backend/parser/__tests__/ast.test.ts +252 -0
- package/src/backend/{__tests__ → parser/__tests__}/parse.test.ts +2 -2
- package/src/backend/parser/api-signatures.ts +23 -0
- package/src/backend/parser/api-usage.ts +73 -0
- package/src/backend/parser/bindings.ts +87 -0
- package/src/backend/parser/member-path.ts +31 -0
- package/src/backend/{parse.ts → parser/parse.ts} +1 -1
- package/src/backend/parser/static-value.ts +145 -0
- package/src/backend/parser/types.ts +66 -0
- package/src/backend/parser/utils.ts +106 -0
- package/src/backend/validate.ts +12 -8
- package/src/commands/__tests__/backend-deploy.test.ts +70 -5
- package/src/commands/__tests__/backend-validate.test.ts +11 -1
- package/src/commands/__tests__/create.test.ts +1 -0
- package/src/commands/__tests__/llm-migration.test.ts +98 -0
- package/src/commands/backend.ts +31 -20
- package/src/commands/create.ts +1 -0
- package/src/commands/deploy.ts +3 -9
- package/src/commands/llm-migration.ts +33 -0
- package/src/commands/migrate.ts +7 -25
- package/src/index.ts +7 -0
- package/src/migrations-llm/snowflake-service-to-backend-runner.md +168 -0
- package/src/templates/app/usage.json +1 -0
- package/src/util/machine-identity.ts +7 -3
- package/src/util/migrations-json.ts +45 -0
- package/dist/backend/__tests__/ast.test.d.ts.map +0 -1
- package/dist/backend/__tests__/parse.test.d.ts.map +0 -1
- package/dist/backend/ast.d.ts +0 -39
- package/dist/backend/ast.d.ts.map +0 -1
- package/dist/backend/ast.js +0 -149
- package/dist/backend/handler-declaration.d.ts.map +0 -1
- package/dist/backend/messages.d.ts.map +0 -1
- package/dist/backend/parse.d.ts.map +0 -1
- package/dist/esm/backend/__tests__/ast.test.d.ts.map +0 -1
- package/dist/esm/backend/__tests__/parse.test.d.ts.map +0 -1
- package/dist/esm/backend/ast.d.ts +0 -39
- package/dist/esm/backend/ast.d.ts.map +0 -1
- package/dist/esm/backend/ast.mjs +0 -144
- package/dist/esm/backend/handler-declaration.d.ts.map +0 -1
- package/dist/esm/backend/messages.d.ts.map +0 -1
- package/dist/esm/backend/parse.d.ts.map +0 -1
- package/src/backend/__tests__/ast.test.ts +0 -53
- package/src/backend/ast.ts +0 -201
- /package/dist/backend/{__tests__ → parser/__tests__}/ast.test.d.ts +0 -0
- /package/dist/backend/{__tests__ → parser/__tests__}/parse.test.d.ts +0 -0
- /package/dist/backend/{handler-declaration.d.ts → parser/handler-declaration.d.ts} +0 -0
- /package/dist/backend/{handler-declaration.js → parser/handler-declaration.js} +0 -0
- /package/dist/backend/{messages.d.ts → parser/messages.d.ts} +0 -0
- /package/dist/backend/{messages.js → parser/messages.js} +0 -0
- /package/dist/backend/{parse.d.ts → parser/parse.d.ts} +0 -0
- /package/dist/esm/backend/{__tests__ → parser/__tests__}/ast.test.d.ts +0 -0
- /package/dist/esm/backend/{__tests__ → parser/__tests__}/parse.test.d.ts +0 -0
- /package/dist/esm/backend/{handler-declaration.d.ts → parser/handler-declaration.d.ts} +0 -0
- /package/dist/esm/backend/{handler-declaration.mjs → parser/handler-declaration.mjs} +0 -0
- /package/dist/esm/backend/{messages.d.ts → parser/messages.d.ts} +0 -0
- /package/dist/esm/backend/{messages.mjs → parser/messages.mjs} +0 -0
- /package/dist/esm/backend/{parse.d.ts → parser/parse.d.ts} +0 -0
- /package/src/backend/{handler-declaration.ts → parser/handler-declaration.ts} +0 -0
- /package/src/backend/{messages.ts → parser/messages.ts} +0 -0
package/src/commands/backend.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
|
+
import { readJson, writeJson } from 'fs-extra';
|
|
3
4
|
import { PutObjectCommand } from '@aws-sdk/client-s3';
|
|
4
5
|
import { info, output } from '../util/logger';
|
|
5
6
|
import { mfAppName, getCwdAppName } from '../util/app-name';
|
|
7
|
+
import { whoAmI } from '../util/machine-identity';
|
|
6
8
|
import { readHandlers } from '../backend/handlers';
|
|
7
9
|
import { detectIntegrations } from '../backend/detect-integrations';
|
|
8
10
|
import { getS3Client } from '../util/s3/client';
|
|
@@ -18,24 +20,36 @@ function currentAppName(): string {
|
|
|
18
20
|
return mfAppName(raw);
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
/**
|
|
22
|
-
* True if the app ships an app-level backend — a `backend/handlers/` directory.
|
|
23
|
-
* Callers gate on this so the deploy path is explicit about whether a backend is in
|
|
24
|
-
* play.
|
|
25
|
-
*/
|
|
26
23
|
export function hasHandlers(appDir: string): boolean {
|
|
27
24
|
return existsSync(path.join(appDir, 'backend', 'handlers'));
|
|
28
25
|
}
|
|
29
26
|
|
|
27
|
+
/** Other features may add top-level fields, so writes below merge into this copy instead of replacing it. */
|
|
28
|
+
async function readExistingUsage(appDir: string): Promise<Record<string, unknown>> {
|
|
29
|
+
try {
|
|
30
|
+
return (await readJson(path.join(appDir, 'usage.json'))) as Record<string, unknown>;
|
|
31
|
+
} catch (err) {
|
|
32
|
+
if ((err as { code?: string }).code === 'ENOENT') {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Keeps the app's git-tracked `usage.json` current with every handler parse (validate or deploy). */
|
|
40
|
+
async function writeUsageJson(appDir: string, patch: Record<string, unknown>): Promise<void> {
|
|
41
|
+
const existing = await readExistingUsage(appDir);
|
|
42
|
+
await writeJson(path.join(appDir, 'usage.json'), { ...existing, ...patch }, { spaces: 2 });
|
|
43
|
+
}
|
|
44
|
+
|
|
30
45
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* (or it is empty of sources) so callers can always pass the result to
|
|
34
|
-
* `registerOrUpdateApp` without a separate gate.
|
|
46
|
+
* Returns `[]` when there is no `backend/handlers/` dir (or it is empty of sources)
|
|
47
|
+
* so callers can always pass the result to `registerOrUpdateApp` without a separate gate.
|
|
35
48
|
*/
|
|
36
49
|
async function readDetectAndUpload(
|
|
37
50
|
appDir: string,
|
|
38
51
|
prefix: string,
|
|
52
|
+
appName: string,
|
|
39
53
|
version: number,
|
|
40
54
|
deployedBy: string,
|
|
41
55
|
logLabel: string
|
|
@@ -44,11 +58,12 @@ async function readDetectAndUpload(
|
|
|
44
58
|
return [];
|
|
45
59
|
}
|
|
46
60
|
info(`reading backend handlers ${logLabel}`);
|
|
47
|
-
const { handlers, apiDomains } = await readHandlers(appDir);
|
|
61
|
+
const { handlers, apiDomains, apiUsages } = await readHandlers(appDir);
|
|
48
62
|
const integrations = detectIntegrations(apiDomains);
|
|
49
63
|
if (Object.keys(handlers).length === 0) {
|
|
50
64
|
return integrations;
|
|
51
65
|
}
|
|
66
|
+
await writeUsageJson(appDir, { appName, deployedBy, apiUsages });
|
|
52
67
|
const s3 = getS3Client();
|
|
53
68
|
const meta = JSON.stringify({ version, deployedBy, handlers: Object.keys(handlers) });
|
|
54
69
|
const put = (key: string, body: string, contentType: string) =>
|
|
@@ -65,11 +80,6 @@ async function readDetectAndUpload(
|
|
|
65
80
|
* from their sources. Each `.js` handler is uploaded as its own flat file — no
|
|
66
81
|
* compilation, no bundling, no npm deps.
|
|
67
82
|
*
|
|
68
|
-
* Live and preview differ only in where the handlers land, so that is the only
|
|
69
|
-
* thing `isPreview` switches:
|
|
70
|
-
* - live: `<appName>/backend-<version>` — the app's immutable per-version slot.
|
|
71
|
-
* - preview: `<mfKeyPrefix without /MF>/MS` — beside the preview's `/MF` frontend.
|
|
72
|
-
*
|
|
73
83
|
* Integration sync note: the returned set is re-derived from the CURRENT backend
|
|
74
84
|
* sources on every deploy, and the platform set is reconciled to exactly match
|
|
75
85
|
* it (there is no stored declaration to fall back on). A deploy therefore always
|
|
@@ -88,25 +98,26 @@ export async function deployBackend(opts: {
|
|
|
88
98
|
const prefix = isPreview ? `${mfKeyPrefix.replace(/\/MF$/, '')}/MS` : `${appName}/backend-${version}`;
|
|
89
99
|
const logLabel = isPreview ? 'for preview' : `for "${appName}"`;
|
|
90
100
|
try {
|
|
91
|
-
return await readDetectAndUpload(appDir, prefix, version, deployedBy, logLabel);
|
|
101
|
+
return await readDetectAndUpload(appDir, prefix, appName, version, deployedBy, logLabel);
|
|
92
102
|
} catch (err) {
|
|
93
103
|
throw tagDeployError('deploy_backend_error', err);
|
|
94
104
|
}
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* uploading anything. Throws on the first invalid handler.
|
|
108
|
+
* Runs the same checks deploy does against the working tree, refreshing the
|
|
109
|
+
* local `usage.json` — no upload to S3. Throws on the first invalid handler.
|
|
101
110
|
*/
|
|
102
111
|
export async function backendValidateCommand(appDir: string = process.cwd()): Promise<void> {
|
|
103
112
|
if (!hasHandlers(appDir)) {
|
|
104
113
|
output('[z2h-cli] no backend/handlers/ directory — nothing to validate', { handlers: [], integrations: [] });
|
|
105
114
|
return;
|
|
106
115
|
}
|
|
107
|
-
const { handlers, apiDomains } = await readHandlers(appDir);
|
|
116
|
+
const { handlers, apiDomains, apiUsages } = await readHandlers(appDir);
|
|
108
117
|
const names = Object.keys(handlers).sort();
|
|
109
118
|
const integrations = detectIntegrations(apiDomains);
|
|
119
|
+
const { name } = (await readJson(path.join(appDir, 'package.json'))) as { name?: string };
|
|
120
|
+
await writeUsageJson(appDir, { appName: name ? mfAppName(name) : undefined, deployedBy: whoAmI(), apiUsages });
|
|
110
121
|
const list = (xs: string[]) => (xs.length ? xs.join(', ') : 'none');
|
|
111
122
|
output(`[z2h-cli] ${names.length} handler(s) valid: ${list(names)}\n[z2h-cli] integrations: ${list(integrations)}`, {
|
|
112
123
|
handlers: names,
|
package/src/commands/create.ts
CHANGED
|
@@ -108,6 +108,7 @@ export async function createCommand(appName: string, opts: CreateOptions = {}):
|
|
|
108
108
|
await copy(path.join(appTemplatesDir, 'gitignore'), path.join(appDir, '.gitignore'));
|
|
109
109
|
await copy(path.join(appTemplatesDir, 'claude-settings.json'), path.join(appDir, '.claude', 'settings.json'));
|
|
110
110
|
await copy(path.join(appTemplatesDir, 'CLAUDE.md'), path.join(appDir, 'CLAUDE.md'));
|
|
111
|
+
await copy(path.join(appTemplatesDir, 'usage.json'), path.join(appDir, 'usage.json'));
|
|
111
112
|
|
|
112
113
|
// html-embed: also copy the source HTML.
|
|
113
114
|
if (template === 'html-embed' && opts.htmlFile) {
|
package/src/commands/deploy.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
1
|
import path from 'node:path';
|
|
3
2
|
import { copy, ensureDir, readJson } from 'fs-extra';
|
|
4
3
|
import { info, warn, output, getOutputMode } from '../util/logger';
|
|
@@ -19,7 +18,7 @@ import { getRemoteUrl, guardCleanTree, pullAndResolve, pushToRemote, initRepo }
|
|
|
19
18
|
import { resolveGitEnv } from '../util/git/git-env';
|
|
20
19
|
import { type RemoteType, validateRemoteOptions, resolveRemoteUrl } from '../util/git/remote-options';
|
|
21
20
|
import { setupS3Client } from '../util/s3/client';
|
|
22
|
-
import {
|
|
21
|
+
import { whoAmI } from '../util/machine-identity';
|
|
23
22
|
import { fetchApp, registerOrUpdateApp } from '../util/broker/app';
|
|
24
23
|
import { openInBrowser } from '../util/open-browser';
|
|
25
24
|
import { resolvePortAndMetadata } from '../shadow/prepare';
|
|
@@ -30,11 +29,6 @@ import { promptForTags } from './tag';
|
|
|
30
29
|
import type { ConsumerPaths, DeployFailureReason } from '../types';
|
|
31
30
|
import type { ManifestEntry, AssetManifestFile } from '../util/manifest';
|
|
32
31
|
|
|
33
|
-
/** Who is publishing this version — the machine identity, else the OS user. */
|
|
34
|
-
function whoDeployed(): string {
|
|
35
|
-
return getMachineAppName() ?? os.userInfo().username;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
32
|
export interface DeployOptions {
|
|
39
33
|
dryRun?: boolean;
|
|
40
34
|
remote?: RemoteType;
|
|
@@ -106,7 +100,7 @@ async function buildAndStamp(opts: {
|
|
|
106
100
|
} else {
|
|
107
101
|
({ assetManifest } = await buildCommand({ publicUrl: opts.publicUrl }));
|
|
108
102
|
}
|
|
109
|
-
const deployedBy =
|
|
103
|
+
const deployedBy = whoAmI();
|
|
110
104
|
|
|
111
105
|
return {
|
|
112
106
|
...assetManifest,
|
|
@@ -148,7 +142,7 @@ async function uploadAndRegister(opts: {
|
|
|
148
142
|
appName,
|
|
149
143
|
mfKeyPrefix: keyPrefix,
|
|
150
144
|
version: nextVersion,
|
|
151
|
-
deployedBy:
|
|
145
|
+
deployedBy: whoAmI(),
|
|
152
146
|
isPreview,
|
|
153
147
|
});
|
|
154
148
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { readFile } from 'fs-extra';
|
|
3
|
+
import { output } from '../util/logger';
|
|
4
|
+
import { resolveMigrationsJson, LLM_MIGRATION_TYPE } from '../util/migrations-json';
|
|
5
|
+
|
|
6
|
+
export interface LlmMigrationOptions {
|
|
7
|
+
/** Omit to list every LLM migration instead of printing one guide. */
|
|
8
|
+
migration?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function llmMigrationCommand(opts: LlmMigrationOptions): Promise<void> {
|
|
12
|
+
const resolved = await resolveMigrationsJson();
|
|
13
|
+
if (!resolved) {
|
|
14
|
+
throw new Error('migrations.json not found');
|
|
15
|
+
}
|
|
16
|
+
const { migrationsJson, migrationsDir } = resolved;
|
|
17
|
+
|
|
18
|
+
if (!opts.migration) {
|
|
19
|
+
const migrations = Object.entries(migrationsJson.migrations)
|
|
20
|
+
.filter(([, entry]) => entry.type === LLM_MIGRATION_TYPE)
|
|
21
|
+
.map(([name, entry]) => ({ name, description: entry.description }));
|
|
22
|
+
output(migrations.map(m => `${m.name}\n ${m.description}`).join('\n'), { ok: true, migrations });
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const entry = migrationsJson.migrations[opts.migration];
|
|
27
|
+
if (entry?.type !== LLM_MIGRATION_TYPE) {
|
|
28
|
+
throw new Error(`no LLM migration named "${opts.migration}"`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const guide = await readFile(path.resolve(migrationsDir, entry.guide), 'utf8');
|
|
32
|
+
output(guide, { ok: true, description: entry.description, guide });
|
|
33
|
+
}
|
package/src/commands/migrate.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
3
|
import { pathExists, readJson, writeJson } from 'fs-extra';
|
|
5
4
|
import semver from 'semver';
|
|
6
5
|
import { errorLog, info, warn } from '../util/logger';
|
|
7
6
|
import { getWorkspaceHome, isZ2hWorkspace, ensureWorkspaceNx, runYarnInstall } from '../util/workspace';
|
|
7
|
+
import { resolveMigrationsJson, LLM_MIGRATION_TYPE } from '../util/migrations-json';
|
|
8
|
+
import type { DeterministicMigrationEntry } from '../util/migrations-json';
|
|
8
9
|
|
|
9
10
|
const STATE_FILE = '.z2h-migrations-state.json';
|
|
10
11
|
|
|
@@ -12,18 +13,6 @@ interface MigrationState {
|
|
|
12
13
|
lastMigratedVersion: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
interface MigrationEntry {
|
|
16
|
-
version: string;
|
|
17
|
-
description: string;
|
|
18
|
-
migrate: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface MigrationsJson {
|
|
22
|
-
migrations: Record<string, MigrationEntry>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// ─── Helpers ───
|
|
26
|
-
|
|
27
16
|
// A prerelease sorts BELOW its own release under plain semver ordering, so
|
|
28
17
|
// comparing raw beta versions here would skip migrations the beta introduced.
|
|
29
18
|
export function baseVersion(version: string): string {
|
|
@@ -75,22 +64,15 @@ export async function migrateCommand(cliVersion: string, opts: MigrateOptions =
|
|
|
75
64
|
|
|
76
65
|
info(`migrating workspace from v${lastVersion} → v${targetVersion}...`);
|
|
77
66
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const migrationsJsonPath = path.resolve(thisDir, '..', '..', 'migrations.json');
|
|
81
|
-
|
|
82
|
-
if (!(await pathExists(migrationsJsonPath))) {
|
|
67
|
+
const resolved = await resolveMigrationsJson();
|
|
68
|
+
if (!resolved) {
|
|
83
69
|
warn('migrations.json not found — skipping migrations');
|
|
84
70
|
return;
|
|
85
71
|
}
|
|
72
|
+
const { migrationsJson, migrationsDir } = resolved;
|
|
86
73
|
|
|
87
|
-
const migrationsJson = (await readJson(migrationsJsonPath)) as MigrationsJson;
|
|
88
|
-
|
|
89
|
-
// Filter to pending migrations (version > lastVersion AND <= targetVersion).
|
|
90
|
-
// CONVENTION: a migration's `version` field must equal the CLI release it
|
|
91
|
-
// ships with (not the feature branch version). This ensures the `gt` check
|
|
92
|
-
// always catches a migration on the exact upgrade that introduces it.
|
|
93
74
|
const pending = Object.entries(migrationsJson.migrations)
|
|
75
|
+
.filter((pair): pair is [string, DeterministicMigrationEntry] => pair[1].type !== LLM_MIGRATION_TYPE)
|
|
94
76
|
.filter(([, entry]) => semver.gt(entry.version, lastVersion) && semver.lte(entry.version, targetVersion))
|
|
95
77
|
.sort(([, a], [, b]) => semver.compare(a.version, b.version));
|
|
96
78
|
|
|
@@ -128,7 +110,7 @@ export async function migrateCommand(cliVersion: string, opts: MigrateOptions =
|
|
|
128
110
|
for (const [name, entry] of pending) {
|
|
129
111
|
const tree = new FsTree(workspaceHome, false);
|
|
130
112
|
try {
|
|
131
|
-
const factoryPath = path.resolve(
|
|
113
|
+
const factoryPath = path.resolve(migrationsDir, entry.migrate);
|
|
132
114
|
const mod = require(factoryPath);
|
|
133
115
|
const fn = mod.default || mod;
|
|
134
116
|
fn(tree);
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { generateCommand, GenerateOptions } from './commands/generate';
|
|
|
15
15
|
import { templatesCommand } from './commands/templates';
|
|
16
16
|
import { generateZ2HTokenCommand } from './commands/generate-z2h-token';
|
|
17
17
|
import { migrateCommand } from './commands/migrate';
|
|
18
|
+
import { llmMigrationCommand } from './commands/llm-migration';
|
|
18
19
|
import { tagCommand } from './commands/tag';
|
|
19
20
|
import { editCommand } from './commands/edit';
|
|
20
21
|
import { backendInvokeCommand, backendValidateCommand } from './commands/backend';
|
|
@@ -297,6 +298,12 @@ async function main(): Promise<void> {
|
|
|
297
298
|
.option('--dry-run', 'show pending migrations without applying any changes')
|
|
298
299
|
.action(runCommand({}, (opts: { dryRun?: boolean }) => migrateCommand(version, opts)));
|
|
299
300
|
|
|
301
|
+
program
|
|
302
|
+
.command('llm-migration')
|
|
303
|
+
.description('List agent-guided (LLM) migrations, or print one migration guide with --migration')
|
|
304
|
+
.option('--migration <name>', 'name of the LLM migration to print; omit to list all')
|
|
305
|
+
.action(runCommand({}, (opts: { migration?: string }) => llmMigrationCommand(opts)));
|
|
306
|
+
|
|
300
307
|
program
|
|
301
308
|
.command('doctor')
|
|
302
309
|
.description('Check that all Z2H tools, auth, workspace, and plugins are correctly set up')
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Migration: snowflake-service-to-backend-runner
|
|
2
|
+
|
|
3
|
+
You are migrating one Z2H consumer app off a deprecated data-fetching pattern. Detection is
|
|
4
|
+
your job too: find the old pattern first (see Detect), then rewrite, verify, and check for edit
|
|
5
|
+
collisions before reporting done (see Verify).
|
|
6
|
+
|
|
7
|
+
## Detect (do this first)
|
|
8
|
+
|
|
9
|
+
Grep the app's `src/` for the endpoint path:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cd "$APP_DIR"
|
|
13
|
+
grep -rn --include='*.ts' --include='*.tsx' --include='*.js' --include='*.jsx' --include='*.html' \
|
|
14
|
+
'/snowflake/query' src/
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- **No matches** → the app does not use the deprecated pattern (or is already migrated). Do
|
|
18
|
+
nothing and report **not applicable**.
|
|
19
|
+
- **Matches** → the app is in scope. The base URL is built one of three ways; grep for whichever
|
|
20
|
+
the matched files use so you find every file involved (helper and fetch often live apart):
|
|
21
|
+
- `getBigBrainAPI(` — React apps, via `bigbrainBaseUrl` (often wrapped in a `getBaseUrl()`)
|
|
22
|
+
- `window.location.origin` — html-embed apps (`getBigBrainAPI` is unavailable in iframes)
|
|
23
|
+
- a hardcoded `https://…/snowflake/query` URL
|
|
24
|
+
|
|
25
|
+
## Read first: `z2h:backend-development`
|
|
26
|
+
|
|
27
|
+
The target pattern is an app backend handler, and the authoritative guide for writing one is the
|
|
28
|
+
`z2h:backend-development` skill. Read it before touching any file — invoke the skill if you have
|
|
29
|
+
the Skill tool, otherwise read `z2h/skills/backend-development/SKILL.md` from the Z2H plugin
|
|
30
|
+
checkout. This document only tells you *what* to move; that skill tells you *how* handlers must
|
|
31
|
+
be written (file layout, the `ctx` surface, hard rules, how the frontend calls them, error
|
|
32
|
+
handling). Where the two disagree, the skill wins.
|
|
33
|
+
|
|
34
|
+
Two sections there matter most for this migration:
|
|
35
|
+
|
|
36
|
+
- **Hard rules for handler code** — one file per operation, a plain global `handler(ctx)` with no
|
|
37
|
+
`export`/`module.exports`, no imports of any kind, JSON in / JSON out.
|
|
38
|
+
- **The `ctx` API** — the exact `ctx.api.v1.snowflake.*` signature lives in
|
|
39
|
+
`packages/z2h-cli/handler-api/backend-runner/channels/snowflake-channel/snowflake.channel.ts`.
|
|
40
|
+
Read it rather than guessing the method name or return shape.
|
|
41
|
+
|
|
42
|
+
## Idempotency (required)
|
|
43
|
+
|
|
44
|
+
This migration must be safe to run on an app in any state — untouched, partially migrated by an
|
|
45
|
+
earlier interrupted run, or already fully migrated. Concretely:
|
|
46
|
+
|
|
47
|
+
- Work from what's in the code now, not from assumptions about the "before" shape. If a handler
|
|
48
|
+
for a query already exists under `backend/handlers/`, reuse it instead of creating a duplicate.
|
|
49
|
+
If some call sites already go through `callBackend`, leave them alone.
|
|
50
|
+
- Never leave a half-state behind. Either every step below completes or you report failed — don't
|
|
51
|
+
report done with the old `runQuery` still in place "for later".
|
|
52
|
+
- Rewrite every call site the Detect step found, not just the first.
|
|
53
|
+
- Done means: **the Detect grep returns nothing.** Re-run it before reporting done.
|
|
54
|
+
|
|
55
|
+
## Old pattern (remove)
|
|
56
|
+
|
|
57
|
+
The app fetches Snowflake data directly from the frontend via the generic `bigbrain-zth` endpoint:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// src/queries.ts (or similar) — DEPRECATED
|
|
61
|
+
import { getBigBrainAPI } from '@mondaydotcomorg/trident-runtime';
|
|
62
|
+
|
|
63
|
+
export async function runQuery(sql: string) {
|
|
64
|
+
const baseUrl = getBigBrainAPI()?.contextService?.bigbrainBaseUrl;
|
|
65
|
+
const res = await fetch(`${baseUrl}/snowflake/query`, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
body: JSON.stringify({ sql }),
|
|
68
|
+
});
|
|
69
|
+
return res.json();
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This pattern is banned for new code (see `z2h:feature-development`'s data-fetching hierarchy) and
|
|
74
|
+
is not available at all inside html-embed iframes.
|
|
75
|
+
|
|
76
|
+
## New pattern (target)
|
|
77
|
+
|
|
78
|
+
SQL moves server-side into an app backend handler, called from the frontend through the Z2H SDK.
|
|
79
|
+
|
|
80
|
+
1. **Create a handler** at `backend/handlers/<name>.js`, following the handler rules in
|
|
81
|
+
`z2h:backend-development`. Move the SQL that was in `queries.ts` into it, and query through
|
|
82
|
+
`ctx.api.v1.snowflake`:
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
// backend/handlers/<name>.js — plain global `handler`, no export, no imports
|
|
86
|
+
async function handler(ctx) {
|
|
87
|
+
const rows = await ctx.api.v1.snowflake.query('SELECT ...', undefined, { identifier: '<name>' });
|
|
88
|
+
return rows;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Pick `<name>` from what the query returns (e.g. `get_encounter`, `list_boards`) — not `runQuery`
|
|
93
|
+
or anything generic; one handler per distinct query/shape, matching existing app conventions.
|
|
94
|
+
The filename (without `.js`) is exactly what the frontend passes to `callBackend`. No scaffold
|
|
95
|
+
command exists — if the app has no `backend/` folder yet, just create `backend/handlers/`.
|
|
96
|
+
|
|
97
|
+
Always pass `identifier` — a short kebab-case string (`[a-z0-9-_]`, max 64 chars), not a
|
|
98
|
+
number; the handler name is a good default. `z2h:backend-development`'s hard rules require it
|
|
99
|
+
on every `ctx.api.v1.snowflake.query` call — it's optional in the API signature, but skipping
|
|
100
|
+
it leaves the query identified by a hash of its SQL, so every later edit to the query makes it
|
|
101
|
+
a new query in logs and metrics. A stable identifier keeps its history across edits.
|
|
102
|
+
|
|
103
|
+
2. **Replace every call site.** Wherever the frontend called `runQuery(sql)` (or equivalent),
|
|
104
|
+
call the handler through the SDK instead (see *Calling the backend from the frontend* in
|
|
105
|
+
`z2h:backend-development` for error handling with `isBackendError`):
|
|
106
|
+
|
|
107
|
+
**React apps:**
|
|
108
|
+
```typescript
|
|
109
|
+
import { createZ2HSDK } from '@mondaydotcomorg/z2h-runtime-sdk';
|
|
110
|
+
|
|
111
|
+
const rows = await createZ2HSDK().callBackend('<name>', { /* params, not raw SQL */ });
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**html-embed apps (`page.html`):**
|
|
115
|
+
```javascript
|
|
116
|
+
const rows = await window.__z2hSdk__.callBackend('<name>', { /* params */ });
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
3. **Delete the old file** (`src/queries.ts` or equivalent) once nothing imports it, and remove
|
|
120
|
+
the now-unused `getBigBrainAPI` import from anywhere else in `src/`.
|
|
121
|
+
|
|
122
|
+
4. **Parameterize, don't template.** If the old `runQuery` took a raw SQL string built from
|
|
123
|
+
variables (string interpolation), do NOT ship the handler taking raw SQL from the frontend —
|
|
124
|
+
that's the same trust boundary the old pattern already had, just moved. Have the frontend pass
|
|
125
|
+
structured params (ids, filters) and build/parameterize the SQL inside the handler.
|
|
126
|
+
|
|
127
|
+
## Verify
|
|
128
|
+
|
|
129
|
+
Two checks, in order. Fix any failure before moving on.
|
|
130
|
+
|
|
131
|
+
1. **Handler validation** — validates every handler under `backend/handlers/` (syntax, isolate
|
|
132
|
+
rules, known integrations, static-SQL check) without deploying:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
cd "$APP_DIR"
|
|
136
|
+
z2h-cli backend validate
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Fix any error it reports (minimal fix, re-run).
|
|
140
|
+
|
|
141
|
+
Also confirm every `ctx.api.v1.snowflake.query` call you wrote passes `options.identifier` —
|
|
142
|
+
`z2h-cli backend validate` does not check for this, so grep for it yourself:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
grep -n 'snowflake.query' backend/handlers/*.js
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
For each match, check the call includes an `identifier` in its `options` argument. Add one if
|
|
149
|
+
missing before moving on.
|
|
150
|
+
|
|
151
|
+
2. **Build** — confirms the frontend typechecks and bundles after the rewrite:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
z2h-cli deploy --dry-run
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Fix any typecheck/lint failure (minimal fix, re-run).
|
|
158
|
+
|
|
159
|
+
3. **Collision check (required, run last)** — for every file you edited during this migration,
|
|
160
|
+
re-read its current contents and compare against what you last wrote there. If anything
|
|
161
|
+
differs from what you expect — the app owner edited that file while you were working —
|
|
162
|
+
discard your changes for this run and report "failed" with the reason: "The app was edited
|
|
163
|
+
while the update was in progress, so it wasn't applied — run this update again once things
|
|
164
|
+
have settled." Do not attempt to reconcile or partially apply; the idempotency contract above
|
|
165
|
+
means this migration is always safe to retry cleanly from scratch on the next touch.
|
|
166
|
+
|
|
167
|
+
Do not deploy or commit as part of this migration — that's the normal feature-development flow's
|
|
168
|
+
job, once the person driving this session is ready to ship.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Returns the machine app name when running as a service (e.g. agent-runner).
|
|
3
5
|
* Available when the process is a monday.com microservice with `APP_NAME` set
|
|
4
|
-
* by the platform runtime.
|
|
5
|
-
*
|
|
6
|
-
* Returns `undefined` for interactive human users (fallback to os.userInfo).
|
|
6
|
+
* by the platform runtime. Returns `undefined` for interactive human users.
|
|
7
7
|
*/
|
|
8
8
|
export function getMachineAppName(): string | undefined {
|
|
9
9
|
return process.env.APP_NAME || undefined;
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
export function whoAmI(): string {
|
|
13
|
+
return getMachineAppName() ?? os.userInfo().username;
|
|
14
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathExists, readJson } from 'fs-extra';
|
|
4
|
+
|
|
5
|
+
export interface DeterministicMigrationEntry {
|
|
6
|
+
type?: never;
|
|
7
|
+
/** Must equal the CLI release it ships with (not the feature branch version),
|
|
8
|
+
* so the `gt` check in migrate.ts always catches it on the exact upgrade that introduces it. */
|
|
9
|
+
version: string;
|
|
10
|
+
description: string;
|
|
11
|
+
migrate: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const LLM_MIGRATION_TYPE = 'llm' as const;
|
|
15
|
+
|
|
16
|
+
/** Applied by handing `guide`'s contents to the calling agent. The guide owns detection too:
|
|
17
|
+
* it tells the agent how to find the old pattern and to report "not relevant" if absent. */
|
|
18
|
+
export interface LlmMigrationEntry {
|
|
19
|
+
type: typeof LLM_MIGRATION_TYPE;
|
|
20
|
+
/** Version of the CLI this migration was added at. */
|
|
21
|
+
version: string;
|
|
22
|
+
description: string;
|
|
23
|
+
/** Path (relative to migrations.json) to the markdown guideline for the agent applying this migration. */
|
|
24
|
+
guide: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type MigrationEntry = DeterministicMigrationEntry | LlmMigrationEntry;
|
|
28
|
+
|
|
29
|
+
export interface MigrationsJson {
|
|
30
|
+
migrations: Record<string, MigrationEntry>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function resolveMigrationsJson(): Promise<{
|
|
34
|
+
migrationsJson: MigrationsJson;
|
|
35
|
+
migrationsDir: string;
|
|
36
|
+
} | null> {
|
|
37
|
+
const pkgJsonPath = createRequire(import.meta.url).resolve('@mondaydotcomorg/z2h-cli/package.json');
|
|
38
|
+
const migrationsDir = path.dirname(pkgJsonPath);
|
|
39
|
+
const migrationsJsonPath = path.join(migrationsDir, 'migrations.json');
|
|
40
|
+
if (!(await pathExists(migrationsJsonPath))) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const migrationsJson = (await readJson(migrationsJsonPath)) as MigrationsJson;
|
|
44
|
+
return { migrationsJson, migrationsDir };
|
|
45
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ast.test.d.ts","sourceRoot":"","sources":["../../../src/backend/__tests__/ast.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parse.test.d.ts","sourceRoot":"","sources":["../../../src/backend/__tests__/parse.test.ts"],"names":[],"mappings":""}
|
package/dist/backend/ast.d.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { AnyNode, Expression, Pattern, Program, VariableDeclaration } from 'acorn';
|
|
2
|
-
export interface Declaration {
|
|
3
|
-
kind: VariableDeclaration['kind'];
|
|
4
|
-
init: Expression | null | undefined;
|
|
5
|
-
/**
|
|
6
|
-
* Property path from `init` to this binding: `[]` for `const x = init`,
|
|
7
|
-
* `['a', 'b']` for `const { a: { b: x } } = init`, `undefined` when the value
|
|
8
|
-
* is not a plain property read (array/rest/default patterns).
|
|
9
|
-
*/
|
|
10
|
-
path: string[] | undefined;
|
|
11
|
-
}
|
|
12
|
-
export interface Bindings {
|
|
13
|
-
/** Every `var|let|const <name> = …` in the file, by name. */
|
|
14
|
-
declarations: Map<string, Declaration[]>;
|
|
15
|
-
/** Names whose value cannot be pinned: reassigned, updated, or bound as parameters. */
|
|
16
|
-
unpinnable: Set<string>;
|
|
17
|
-
}
|
|
18
|
-
/** Every identifier a binding pattern introduces (`{ a, b: [c] } = …` → a, c). */
|
|
19
|
-
export declare const patternNames: (pattern: Pattern) => string[];
|
|
20
|
-
export declare function collectBindings(program: Program): Bindings;
|
|
21
|
-
export interface PathResolver {
|
|
22
|
-
/**
|
|
23
|
-
* Dotted path of a member chain, following `const` aliases back to their
|
|
24
|
-
* source: with `const sf = ctx.api.v1.snowflake`, `sf.query` resolves to
|
|
25
|
-
* `ctx.api.v1.snowflake.query`. Destructuring counts too
|
|
26
|
-
* (`const { snowflake: sf } = ctx.api.v1`). `undefined` if any link is dynamic.
|
|
27
|
-
*
|
|
28
|
-
* Only single, never-reassigned, never-shadowed `const` bindings are followed —
|
|
29
|
-
* anything else is left as written, so a `let` alias or a parameter of the same
|
|
30
|
-
* name is never mistaken for the channel. The handler's first parameter is
|
|
31
|
-
* treated as `ctx` even when renamed (`handler(context)` → `context.api.v1.db`
|
|
32
|
-
* resolves as `ctx.api.v1.db`).
|
|
33
|
-
*/
|
|
34
|
-
pathOf(node: AnyNode): string | undefined;
|
|
35
|
-
}
|
|
36
|
-
export declare function createPathResolver(bindings: Bindings, contextParam?: string): PathResolver;
|
|
37
|
-
/** Every `ctx.api.v1.<domain>` the program touches (known or not), through `paths`. */
|
|
38
|
-
export declare function collectApiDomains(program: Program, paths: PathResolver): Set<string>;
|
|
39
|
-
//# sourceMappingURL=ast.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/backend/ast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAKxF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC;;;;OAIG;IACH,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACvB,6DAA6D;IAC7D,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IACzC,uFAAuF;IACvF,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACzB;AA8CD,kFAAkF;AAClF,eAAO,MAAM,YAAY,GAAI,SAAS,OAAO,KAAG,MAAM,EAAmD,CAAC;AAE1G,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,CA4C1D;AA2BD,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,YAAY,CA6B1F;AAED,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,CAWpF"}
|