@mondaydotcomorg/z2h-cli 0.34.0 → 0.34.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/CHANGELOG.md +2969 -0
  2. package/dist/backend/__tests__/ast.test.d.ts +2 -0
  3. package/dist/backend/__tests__/ast.test.d.ts.map +1 -0
  4. package/dist/backend/__tests__/parse.test.d.ts +2 -0
  5. package/dist/backend/__tests__/parse.test.d.ts.map +1 -0
  6. package/dist/backend/ast.d.ts +41 -0
  7. package/dist/backend/ast.d.ts.map +1 -0
  8. package/dist/backend/ast.js +152 -0
  9. package/dist/backend/handler-declaration.d.ts +17 -0
  10. package/dist/backend/handler-declaration.d.ts.map +1 -0
  11. package/dist/backend/handler-declaration.js +83 -0
  12. package/dist/backend/messages.d.ts +10 -0
  13. package/dist/backend/messages.d.ts.map +1 -0
  14. package/dist/backend/messages.js +14 -0
  15. package/dist/backend/parse.d.ts +11 -0
  16. package/dist/backend/parse.d.ts.map +1 -0
  17. package/dist/backend/parse.js +66 -0
  18. package/dist/backend/rules/__tests__/snowflake-static-sql.test.d.ts +2 -0
  19. package/dist/backend/rules/__tests__/snowflake-static-sql.test.d.ts.map +1 -0
  20. package/dist/backend/rules/index.d.ts +5 -0
  21. package/dist/backend/rules/index.d.ts.map +1 -0
  22. package/dist/backend/rules/index.js +8 -0
  23. package/dist/backend/rules/snowflake-static-sql.d.ts +15 -0
  24. package/dist/backend/rules/snowflake-static-sql.d.ts.map +1 -0
  25. package/dist/backend/rules/snowflake-static-sql.js +75 -0
  26. package/dist/backend/rules/types.d.ts +29 -0
  27. package/dist/backend/rules/types.d.ts.map +1 -0
  28. package/dist/backend/rules/types.js +1 -0
  29. package/dist/commands/__tests__/delete.test.d.ts +2 -0
  30. package/dist/commands/__tests__/delete.test.d.ts.map +1 -0
  31. package/dist/commands/backend.d.ts +0 -1
  32. package/dist/commands/backend.d.ts.map +1 -1
  33. package/dist/commands/backend.js +6 -6
  34. package/dist/commands/deploy.d.ts.map +1 -1
  35. package/dist/commands/deploy.js +16 -13
  36. package/dist/constants.d.ts +2 -13
  37. package/dist/constants.d.ts.map +1 -1
  38. package/dist/constants.js +60 -45
  39. package/dist/esm/backend/__tests__/ast.test.d.ts +2 -0
  40. package/dist/esm/backend/__tests__/ast.test.d.ts.map +1 -0
  41. package/dist/esm/backend/__tests__/parse.test.d.ts +2 -0
  42. package/dist/esm/backend/__tests__/parse.test.d.ts.map +1 -0
  43. package/dist/esm/backend/ast.d.ts +41 -0
  44. package/dist/esm/backend/ast.d.ts.map +1 -0
  45. package/dist/esm/backend/ast.mjs +146 -0
  46. package/dist/esm/backend/handler-declaration.d.ts +17 -0
  47. package/dist/esm/backend/handler-declaration.d.ts.map +1 -0
  48. package/dist/esm/backend/handler-declaration.mjs +78 -0
  49. package/dist/esm/backend/messages.d.ts +10 -0
  50. package/dist/esm/backend/messages.d.ts.map +1 -0
  51. package/dist/esm/backend/messages.mjs +12 -0
  52. package/dist/esm/backend/parse.d.ts +11 -0
  53. package/dist/esm/backend/parse.d.ts.map +1 -0
  54. package/dist/esm/backend/parse.mjs +64 -0
  55. package/dist/esm/backend/rules/__tests__/snowflake-static-sql.test.d.ts +2 -0
  56. package/dist/esm/backend/rules/__tests__/snowflake-static-sql.test.d.ts.map +1 -0
  57. package/dist/esm/backend/rules/index.d.ts +5 -0
  58. package/dist/esm/backend/rules/index.d.ts.map +1 -0
  59. package/dist/esm/backend/rules/index.mjs +6 -0
  60. package/dist/esm/backend/rules/snowflake-static-sql.d.ts +15 -0
  61. package/dist/esm/backend/rules/snowflake-static-sql.d.ts.map +1 -0
  62. package/dist/esm/backend/rules/snowflake-static-sql.mjs +73 -0
  63. package/dist/esm/backend/rules/types.d.ts +29 -0
  64. package/dist/esm/backend/rules/types.d.ts.map +1 -0
  65. package/dist/esm/backend/rules/types.mjs +1 -0
  66. package/dist/esm/commands/__tests__/delete.test.d.ts +2 -0
  67. package/dist/esm/commands/__tests__/delete.test.d.ts.map +1 -0
  68. package/dist/esm/commands/backend.d.ts +0 -1
  69. package/dist/esm/commands/backend.d.ts.map +1 -1
  70. package/dist/esm/commands/backend.mjs +5 -5
  71. package/dist/esm/commands/deploy.d.ts.map +1 -1
  72. package/dist/esm/commands/deploy.mjs +9 -6
  73. package/dist/esm/constants.d.ts +2 -13
  74. package/dist/esm/constants.d.ts.map +1 -1
  75. package/dist/esm/constants.mjs +5 -35
  76. package/dist/esm/util/__tests__/usage-upload.test.d.ts +2 -0
  77. package/dist/esm/util/__tests__/usage-upload.test.d.ts.map +1 -0
  78. package/dist/esm/util/app-name.mjs +1 -1
  79. package/dist/esm/util/auth/__tests__/index.test.d.ts +2 -0
  80. package/dist/esm/util/auth/__tests__/index.test.d.ts.map +1 -0
  81. package/dist/esm/util/auth/broker-credential-provider.d.ts +6 -1
  82. package/dist/esm/util/auth/broker-credential-provider.d.ts.map +1 -1
  83. package/dist/esm/util/auth/broker-credential-provider.mjs +2 -2
  84. package/dist/esm/util/auth/index.d.ts +3 -0
  85. package/dist/esm/util/auth/index.d.ts.map +1 -0
  86. package/dist/esm/util/auth/index.mjs +2 -0
  87. package/dist/esm/util/broker/app.d.ts +0 -1
  88. package/dist/esm/util/broker/app.d.ts.map +1 -1
  89. package/dist/esm/util/broker/app.mjs +3 -4
  90. package/dist/esm/util/git/repo.mjs +1 -1
  91. package/dist/esm/util/s3/client.mjs +1 -1
  92. package/dist/esm/util/s3/upload.mjs +1 -1
  93. package/dist/esm/util/usage-json.d.ts +2 -2
  94. package/dist/esm/util/usage-json.d.ts.map +1 -1
  95. package/dist/esm/util/usage-json.mjs +5 -2
  96. package/dist/esm/util/usage-upload.d.ts +3 -0
  97. package/dist/esm/util/usage-upload.d.ts.map +1 -0
  98. package/dist/esm/util/usage-upload.mjs +29 -0
  99. package/dist/util/__tests__/usage-upload.test.d.ts +2 -0
  100. package/dist/util/__tests__/usage-upload.test.d.ts.map +1 -0
  101. package/dist/util/app-name.js +6 -6
  102. package/dist/util/auth/__tests__/index.test.d.ts +2 -0
  103. package/dist/util/auth/__tests__/index.test.d.ts.map +1 -0
  104. package/dist/util/auth/broker-credential-provider.d.ts +6 -1
  105. package/dist/util/auth/broker-credential-provider.d.ts.map +1 -1
  106. package/dist/util/auth/broker-credential-provider.js +2 -2
  107. package/dist/util/auth/index.d.ts +3 -0
  108. package/dist/util/auth/index.d.ts.map +1 -0
  109. package/dist/util/auth/index.js +11 -0
  110. package/dist/util/broker/app.d.ts +0 -1
  111. package/dist/util/broker/app.d.ts.map +1 -1
  112. package/dist/util/broker/app.js +3 -4
  113. package/dist/util/git/repo.js +2 -2
  114. package/dist/util/s3/client.js +2 -2
  115. package/dist/util/s3/upload.js +2 -2
  116. package/dist/util/usage-json.d.ts +2 -2
  117. package/dist/util/usage-json.d.ts.map +1 -1
  118. package/dist/util/usage-json.js +5 -2
  119. package/dist/util/usage-upload.d.ts +3 -0
  120. package/dist/util/usage-upload.d.ts.map +1 -0
  121. package/dist/util/usage-upload.js +31 -0
  122. package/package.json +3 -3
  123. package/src/commands/__tests__/backend-deploy.test.ts +2 -4
  124. package/src/commands/__tests__/deploy.test.ts +43 -11
  125. package/src/commands/backend.ts +4 -5
  126. package/src/commands/deploy.ts +9 -5
  127. package/src/constants.ts +25 -47
  128. package/src/util/__tests__/usage-upload.test.ts +58 -0
  129. package/src/util/auth/__tests__/broker-credential-provider.test.ts +4 -4
  130. package/src/util/auth/broker-credential-provider.ts +4 -4
  131. package/src/util/broker/app.ts +2 -15
  132. package/src/util/usage-json.ts +11 -4
  133. package/src/util/usage-upload.ts +38 -0
@@ -10,7 +10,7 @@ const util_machineIdentity = require('../util/machine-identity.js');
10
10
  const backend_handlers = require('../backend/handlers.js');
11
11
  const backend_detectIntegrations = require('../backend/detect-integrations.js');
12
12
  const util_s3_client = require('../util/s3/client.js');
13
- const constants = require('../constants.js');
13
+ const z2hSharedUtils = require('@mondaydotcomorg/z2h-shared-utils');
14
14
  const backend_client = require('../backend/client.js');
15
15
  const backend_formatSandboxRun = require('../backend/format-sandbox-run.js');
16
16
  const backend_validate = require('../backend/validate.js');
@@ -30,23 +30,23 @@ function currentAppName() {
30
30
  }
31
31
  async function readDetectAndUpload(appDir, prefix, appName, version, deployedBy, logLabel) {
32
32
  if (!util_usageJson.hasHandlers(appDir)) {
33
- return { integrations: [], handlerNames: [], apiUsages: {} };
33
+ return { integrations: [], handlerNames: [] };
34
34
  }
35
35
  util_logger.info(`reading backend handlers ${logLabel}`);
36
- const { handlers, apiDomains, apiUsages } = await backend_handlers.readHandlers(appDir);
36
+ const { handlers, apiDomains } = await backend_handlers.readHandlers(appDir);
37
37
  const integrations = backend_detectIntegrations.detectIntegrations(apiDomains);
38
38
  const handlerNames = Object.keys(handlers);
39
39
  if (Object.keys(handlers).length === 0) {
40
- return { integrations, handlerNames, apiUsages };
40
+ return { integrations, handlerNames };
41
41
  }
42
42
  const s3 = util_s3_client.getS3Client();
43
43
  const meta = JSON.stringify({ version, deployedBy, handlers: Object.keys(handlers) });
44
- const put = (key, body, contentType) => s3.send(new clientS3.PutObjectCommand({ Bucket: constants.BUCKET_WRITE_NAME, Key: key, Body: body, ContentType: contentType }));
44
+ const put = (key, body, contentType) => s3.send(new clientS3.PutObjectCommand({ Bucket: z2hSharedUtils.BUCKET_WRITE_NAME, Key: key, Body: body, ContentType: contentType }));
45
45
  await Promise.all([
46
46
  ...Object.entries(handlers).map(([name, code]) => put(`${prefix}/${name}.js`, code, 'application/javascript')),
47
47
  put(`${prefix}/meta.json`, meta, 'application/json'),
48
48
  ]);
49
- return { integrations, handlerNames, apiUsages };
49
+ return { integrations, handlerNames };
50
50
  }
51
51
  /**
52
52
  * Integration sync note: the returned set is re-derived from the CURRENT backend
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,UAAU,EAA2C,MAAM,4BAA4B,CAAC;AAatG,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAWD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3E"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,KAAK,UAAU,EAA2C,MAAM,4BAA4B,CAAC;AAatG,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAWD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3E"}
@@ -12,6 +12,7 @@ const util_auth_authStore = require('../util/auth/auth-store.js');
12
12
  const util_appName = require('../util/app-name.js');
13
13
  const util_git_repo = require('../util/git/repo.js');
14
14
  const util_usageJson = require('../util/usage-json.js');
15
+ const util_usageUpload = require('../util/usage-upload.js');
15
16
  const util_git_gitEnv = require('../util/git/git-env.js');
16
17
  const util_git_remoteOptions = require('../util/git/remote-options.js');
17
18
  const util_s3_client = require('../util/s3/client.js');
@@ -23,6 +24,7 @@ const util_errors = require('../util/errors.js');
23
24
  const commands_backend = require('./backend.js');
24
25
  const commands_build = require('./build.js');
25
26
  const commands_tag = require('./tag.js');
27
+ const z2hSharedUtils = require('@mondaydotcomorg/z2h-shared-utils');
26
28
 
27
29
  const _interopDefault = e => e && e.__esModule ? e : { default: e };
28
30
 
@@ -88,7 +90,7 @@ async function uploadAndRegister(opts) {
88
90
  const { paths, keyPrefix, appName, token, nextVersion, entry, gitRemote, isPreview, description, masterAppName } = opts;
89
91
  // Runs before the frontend upload so a `deployBackend` failure can't leave a
90
92
  // preview's unversioned `MF` dir overwritten with no rollback.
91
- const { integrations, handlerNames, apiUsages } = await commands_backend.deployBackend({
93
+ const { integrations, handlerNames } = await commands_backend.deployBackend({
92
94
  appDir: process.cwd(),
93
95
  appName,
94
96
  mfKeyPrefix: keyPrefix,
@@ -97,7 +99,7 @@ async function uploadAndRegister(opts) {
97
99
  isPreview,
98
100
  });
99
101
  try {
100
- util_logger.info(`uploading ${isPreview ? 'preview ' : ''}build → s3://${constants.BUCKET_WRITE_NAME}/${keyPrefix}/`);
102
+ util_logger.info(`uploading ${isPreview ? 'preview ' : ''}build → s3://${z2hSharedUtils.BUCKET_WRITE_NAME}/${keyPrefix}/`);
101
103
  await util_s3_upload.uploadDir(paths.shadowBuild, keyPrefix);
102
104
  await util_broker_app.registerOrUpdateApp({
103
105
  appName,
@@ -107,7 +109,6 @@ async function uploadAndRegister(opts) {
107
109
  gitRemote,
108
110
  integrations,
109
111
  handlerNames,
110
- apiUsages,
111
112
  description,
112
113
  masterAppName,
113
114
  });
@@ -135,13 +136,13 @@ async function deployPreview({ paths, consumerName, appName, token, opts }) {
135
136
  const { previewId } = await shadow_prepare.resolvePortAndMetadata(paths, util_appName.cleanAppName(consumerName));
136
137
  const previewSlug = util_appName.buildPreviewSlug(consumerName, previewId);
137
138
  const previewAppName = util_appName.mfAppName(previewSlug);
138
- const keyPrefix = `${constants.S3_PREVIEWS_FOLDER}/${previewAppName}/MF`;
139
+ const keyPrefix = `${z2hSharedUtils.S3_PREVIEWS_FOLDER}/${previewAppName}/MF`;
139
140
  // Full CDN URL, not a relative path — trident-skeleton uses a manifest
140
141
  // entry's `url` verbatim as the asset base, never joined with its own mfUrl.
141
- const url = `${constants.BUCKET_READ_DNS_URL}/${keyPrefix}`;
142
+ const url = `${z2hSharedUtils.BUCKET_READ_DNS_URL}/${keyPrefix}`;
142
143
  // No catch-all: every stage below tags its own DeployError reason at the throw site,
143
144
  // and anything unclassified reports as `unknown` via reasonOf() in runCommand.
144
- const nextVersion = (await util_auth_brokerCredentialProvider.fetchDeployVersion(previewAppName)) + 1;
145
+ const nextVersion = (await util_auth_brokerCredentialProvider.fetchDeployVersion(previewAppName)).version + 1;
145
146
  util_logger.info('building');
146
147
  const entry = await buildAndStamp({
147
148
  paths,
@@ -173,7 +174,7 @@ async function deployPreview({ paths, consumerName, appName, token, opts }) {
173
174
  appName,
174
175
  previewId,
175
176
  previewAppName,
176
- bucket: constants.BUCKET_WRITE_NAME,
177
+ bucket: z2hSharedUtils.BUCKET_WRITE_NAME,
177
178
  keyPrefix,
178
179
  url,
179
180
  previewUrl,
@@ -206,9 +207,10 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
206
207
  await util_git_repo.initRepo(process.cwd(), remote);
207
208
  gitRemote = remote;
208
209
  }
209
- const version = await util_auth_brokerCredentialProvider.fetchDeployVersion(appName);
210
+ const { version, id: appId } = await util_auth_brokerCredentialProvider.fetchDeployVersion(appName);
210
211
  const nextVersion = version + 1;
211
212
  const gitEnv = await util_git_gitEnv.resolveGitEnv(appName, gitRemote);
213
+ let usage = {};
212
214
  // Sync git first so a divergence (GitConflictError) stops us.
213
215
  // The skill commits the working tree (with an LLM-authored message) before
214
216
  // invoking deploy, so the tree should be clean here.
@@ -217,7 +219,7 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
217
219
  await util_git_repo.guardCleanTree(process.cwd());
218
220
  util_logger.info('syncing with git remote');
219
221
  await util_git_repo.pullAndResolve(process.cwd(), gitEnv);
220
- await util_usageJson.updateOrCreateUsageJson(process.cwd(), appName, util_machineIdentity.whoAmI());
222
+ usage = await util_usageJson.updateOrCreateUsageJson(process.cwd(), appName, util_machineIdentity.whoAmI());
221
223
  }
222
224
  catch (err) {
223
225
  throw util_errors.tagDeployError('git_error', err);
@@ -225,16 +227,16 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
225
227
  }
226
228
  util_logger.info('reading current Z2H manifest');
227
229
  const keyPrefix = `${appName}/${nextVersion}`;
228
- const url = `${constants.BUCKET_READ_DNS_URL}/${keyPrefix}`;
230
+ const url = `${z2hSharedUtils.BUCKET_READ_DNS_URL}/${keyPrefix}`;
229
231
  util_logger.info('building');
230
232
  const entry = await buildAndStamp({ paths, publicUrl: url, nextVersion, gitRemote, prebuiltDir: opts.prebuiltDir });
231
233
  if (opts.dryRun) {
232
- util_logger.output(`[z2h-cli] dry-run: would upload to s3://${constants.BUCKET_WRITE_NAME}/${keyPrefix}/ as v${version}`, {
234
+ util_logger.output(`[z2h-cli] dry-run: would upload to s3://${z2hSharedUtils.BUCKET_WRITE_NAME}/${keyPrefix}/ as v${version}`, {
233
235
  ok: true,
234
236
  dryRun: true,
235
237
  appName,
236
238
  version,
237
- bucket: constants.BUCKET_WRITE_NAME,
239
+ bucket: z2hSharedUtils.BUCKET_WRITE_NAME,
238
240
  keyPrefix,
239
241
  url,
240
242
  entry,
@@ -274,6 +276,7 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
274
276
  util_logger.info('releasing deploy lock');
275
277
  await releaseLockBestEffort(appName, token);
276
278
  }
279
+ await util_usageUpload.putAppUsage(appName, appId, usage, nextVersion);
277
280
  // Tags are a post-deploy concern: the S3 upload has already succeeded, so a
278
281
  // tag failure never fails the deploy (handled inside).
279
282
  await handleDeployTags(appName);
@@ -285,7 +288,7 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
285
288
  ok: true,
286
289
  appName,
287
290
  version: nextVersion,
288
- bucket: constants.BUCKET_WRITE_NAME,
291
+ bucket: z2hSharedUtils.BUCKET_WRITE_NAME,
289
292
  keyPrefix,
290
293
  url,
291
294
  liveUrl,
@@ -8,19 +8,7 @@ export declare const SHADOW_DEV_DEPS: Record<string, string>;
8
8
  export declare const MIN_PORT = 13000;
9
9
  export declare const MAX_PORT = 19999;
10
10
  export declare const GITIGNORE_ENTRIES: string[];
11
- export declare const BUCKET_WRITE_NAME = "prod-use1-bigbrain-zth-mf-assets";
12
- export declare const REGION = "us-east-1";
13
- export declare const BUCKET_WRITE_URL = "https://prod-use1-bigbrain-zth-mf-assets.s3.us-east-1.amazonaws.com";
14
- export declare const BUCKET_READ_DNS_NAME = "bigbrain-zth-mf-assets";
15
- export declare const BUCKET_READ_DNS_URL = "https://bigbrain-zth-mf-assets.bigbrain.me";
16
- export declare const S3_PREVIEWS_FOLDER = "previews";
17
- export declare const MANIFEST_KEY = "mf-assets";
18
- export declare const MANIFEST_READ_URL = "https://bigbrain-zth-mf-assets.bigbrain.me/mf-assets";
19
- export declare const S3_SKILLS_BUCKET_NAME = "prod-use1-bigbrain-zth-skills";
20
- export declare const DATAVIZ_SKILLS_PREFIX = "dataviz/";
21
- export declare const APP_NAME_PATTERN: RegExp;
22
- export declare const PREVIEW_APP_NAME_PREFIX = "zth-preview-";
23
- export { ALWAYS_ON_CHANNELS, DECLARED_INTEGRATIONS, type DeclaredIntegration } from '@mondaydotcomorg/z2h-shared-utils';
11
+ export { ALWAYS_ON_CHANNELS, APP_USAGE_BUCKET_NAME, appUsageFolder, APP_NAME_PATTERN, BUCKET_READ_DNS_NAME, BUCKET_READ_DNS_URL, BUCKET_WRITE_NAME, BUCKET_WRITE_URL, DATAVIZ_SKILLS_PREFIX, DECLARED_INTEGRATIONS, MANIFEST_KEY, MANIFEST_READ_URL, PREVIEW_APP_NAME_PREFIX, REGION, S3_PREVIEWS_FOLDER, S3_SKILLS_BUCKET_NAME, type DeclaredIntegration, } from '@mondaydotcomorg/z2h-shared-utils';
24
12
  export declare const DEFAULT_BROKER_BASE_URL = "https://bigbrain-zth.bigbrain.me";
25
13
  /**
26
14
  * Resolves the broker origin (no trailing slash). Honors
@@ -28,6 +16,7 @@ export declare const DEFAULT_BROKER_BASE_URL = "https://bigbrain-zth.bigbrain.me
28
16
  * STS credential provider and the npm-broker registry URL.
29
17
  */
30
18
  export declare function brokerBaseUrl(): string;
19
+ export declare function isProdBroker(): boolean;
31
20
  export declare function routingHeaders(): Record<string, string>;
32
21
  export declare const NPM_BROKER_PATH = "/z2h-cli/npm";
33
22
  export declare const BROKER_NPM_SCOPE = "@mondaydotcomorg";
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,eAAO,MAAM,eAAe,SAAS,CAAC;AAGtC,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAEjD,CAAC;AAKF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAElD,CAAC;AAMF,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAG9B,eAAO,MAAM,iBAAiB,UAAY,CAAC;AAQ3C,eAAO,MAAM,iBAAiB,qCAAqC,CAAC;AACpE,eAAO,MAAM,MAAM,cAAc,CAAC;AAClC,eAAO,MAAM,gBAAgB,wEAA4D,CAAC;AAE1F,eAAO,MAAM,oBAAoB,2BAA2B,CAAC;AAC7D,eAAO,MAAM,mBAAmB,+CAAgD,CAAC;AAKjF,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAK7C,eAAO,MAAM,YAAY,cAAc,CAAC;AACxC,eAAO,MAAM,iBAAiB,yDAA2C,CAAC;AAM1E,eAAO,MAAM,qBAAqB,kCAAkC,CAAC;AAIrE,eAAO,MAAM,qBAAqB,aAAa,CAAC;AAMhD,eAAO,MAAM,gBAAgB,QAA2B,CAAC;AAEzD,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAMtD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,KAAK,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAKxH,eAAO,MAAM,uBAAuB,qCAAqC,CAAC;AAE1E;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAID,wBAAgB,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGvD;AAKD,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAG9C,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAGnD,eAAO,MAAM,uBAAuB,gCAAgC,CAAC;AAMrE,eAAO,MAAM,iBAAiB,EAAG,eAAwB,CAAC;AAE1D;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,eAAO,MAAM,eAAe,SAAS,CAAC;AAGtC,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAEjD,CAAC;AAKF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAElD,CAAC;AAMF,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAG9B,eAAO,MAAM,iBAAiB,UAAY,CAAC;AAI3C,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,uBAAuB,EACvB,MAAM,EACN,kBAAkB,EAClB,qBAAqB,EACrB,KAAK,mBAAmB,GACzB,MAAM,mCAAmC,CAAC;AAK3C,eAAO,MAAM,uBAAuB,qCAAqC,CAAC;AAE1E;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAID,wBAAgB,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGvD;AAKD,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAG9C,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAGnD,eAAO,MAAM,uBAAuB,gCAAgC,CAAC;AAMrE,eAAO,MAAM,iBAAiB,EAAG,eAAwB,CAAC;AAE1D;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C"}
package/dist/constants.js CHANGED
@@ -26,39 +26,6 @@ const MIN_PORT = 13000;
26
26
  const MAX_PORT = 19999;
27
27
  // ─────────── Gitignore entries auto-appended to the consumer's file ────
28
28
  const GITIGNORE_ENTRIES = ['.zth/'];
29
- // ─────────── Z2H deploy bucket / contract ─────────
30
- // Source of truth for the bucket layout. The CLI uses these when configuring
31
- // the S3 client + uploading; bigbrain-zth's STS broker imports them via
32
- // `@mondaydotcomorg/z2h-cli/constants` to scope the per-deploy session
33
- // policy to the same bucket + manifest key.
34
- const BUCKET_WRITE_NAME = 'prod-use1-bigbrain-zth-mf-assets';
35
- const REGION = 'us-east-1';
36
- const BUCKET_WRITE_URL = `https://${BUCKET_WRITE_NAME}.s3.${REGION}.amazonaws.com`;
37
- const BUCKET_READ_DNS_NAME = 'bigbrain-zth-mf-assets';
38
- const BUCKET_READ_DNS_URL = `https://${BUCKET_READ_DNS_NAME}.bigbrain.me`;
39
- // Root prefix for ephemeral preview uploads: `previews/<previewAppName>/MF/...`,
40
- // where previewAppName is the preview's own `mf-zth-preview-<id>-<app>` name.
41
- // Shared by the CLI (upload path) and bigbrain-zth (STS policy + manifest overlay).
42
- const S3_PREVIEWS_FOLDER = 'previews';
43
- // No `.json` extension — TridentSkeleton's `mfUrl` API appends `/mf-assets`
44
- // to the bucket base when fetching, so the manifest must live at this exact
45
- // key. See microfrontends/mf-bigbrain-zth/.../ZeroToHeroHost.tsx.
46
- const MANIFEST_KEY = 'mf-assets';
47
- const MANIFEST_READ_URL = `${BUCKET_READ_DNS_URL}/${MANIFEST_KEY}`;
48
- // ─────────── Skills bucket (read-only) ─────────
49
- // bigbrain-zth mints read-only STS creds scoped to this bucket (mintDatavizSkillsCredentials);
50
- // bigbrain-zth-agent syncs from it into its vendor/ at pod startup (syncDatavizVendor).
51
- // Both import from here so the two sides can't drift.
52
- const S3_SKILLS_BUCKET_NAME = 'prod-use1-bigbrain-zth-skills';
53
- // Prefix the dataviz design-system skills are published under
54
- // (by data-cookbook's own CI on merge to master).
55
- const DATAVIZ_SKILLS_PREFIX = 'dataviz/';
56
- // ─────────── App-name slug — backend & CLI both honor this ─────
57
- // Lowercase kebab-case, must start with a letter, 2-50 chars total. The
58
- // backend STS broker rejects anything else with 400; the CLI can validate
59
- // upfront for friendlier errors.
60
- const APP_NAME_PATTERN = /^[a-z][a-z0-9-]{1,49}$/;
61
- const PREVIEW_APP_NAME_PREFIX = 'zth-preview-';
62
29
  // ─────────── Broker origin + npm registry (shared §3 contract) ─────
63
30
  // The bigbrain-zth broker fronts both the STS credential route and the npm
64
31
  // proxy. `Z2H_BROKER_BASE_URL` overrides the origin for tests/staging.
@@ -71,6 +38,9 @@ const DEFAULT_BROKER_BASE_URL = 'https://bigbrain-zth.bigbrain.me';
71
38
  function brokerBaseUrl() {
72
39
  return process.env.Z2H_BROKER_BASE_URL || DEFAULT_BROKER_BASE_URL;
73
40
  }
41
+ function isProdBroker() {
42
+ return brokerBaseUrl() === DEFAULT_BROKER_BASE_URL;
43
+ }
74
44
  // Returns Baggage header for monday-mirror routing when Z2H_ROUTING_KEY is set.
75
45
  // No-op in production (returns {}) — only activates when the env var is set.
76
46
  function routingHeaders() {
@@ -104,33 +74,78 @@ Object.defineProperty(exports, 'ALWAYS_ON_CHANNELS', {
104
74
  enumerable: true,
105
75
  get: () => z2hSharedUtils.ALWAYS_ON_CHANNELS
106
76
  });
77
+ Object.defineProperty(exports, 'APP_NAME_PATTERN', {
78
+ enumerable: true,
79
+ get: () => z2hSharedUtils.APP_NAME_PATTERN
80
+ });
81
+ Object.defineProperty(exports, 'APP_USAGE_BUCKET_NAME', {
82
+ enumerable: true,
83
+ get: () => z2hSharedUtils.APP_USAGE_BUCKET_NAME
84
+ });
85
+ Object.defineProperty(exports, 'BUCKET_READ_DNS_NAME', {
86
+ enumerable: true,
87
+ get: () => z2hSharedUtils.BUCKET_READ_DNS_NAME
88
+ });
89
+ Object.defineProperty(exports, 'BUCKET_READ_DNS_URL', {
90
+ enumerable: true,
91
+ get: () => z2hSharedUtils.BUCKET_READ_DNS_URL
92
+ });
93
+ Object.defineProperty(exports, 'BUCKET_WRITE_NAME', {
94
+ enumerable: true,
95
+ get: () => z2hSharedUtils.BUCKET_WRITE_NAME
96
+ });
97
+ Object.defineProperty(exports, 'BUCKET_WRITE_URL', {
98
+ enumerable: true,
99
+ get: () => z2hSharedUtils.BUCKET_WRITE_URL
100
+ });
101
+ Object.defineProperty(exports, 'DATAVIZ_SKILLS_PREFIX', {
102
+ enumerable: true,
103
+ get: () => z2hSharedUtils.DATAVIZ_SKILLS_PREFIX
104
+ });
107
105
  Object.defineProperty(exports, 'DECLARED_INTEGRATIONS', {
108
106
  enumerable: true,
109
107
  get: () => z2hSharedUtils.DECLARED_INTEGRATIONS
110
108
  });
111
- exports.APP_NAME_PATTERN = APP_NAME_PATTERN;
109
+ Object.defineProperty(exports, 'MANIFEST_KEY', {
110
+ enumerable: true,
111
+ get: () => z2hSharedUtils.MANIFEST_KEY
112
+ });
113
+ Object.defineProperty(exports, 'MANIFEST_READ_URL', {
114
+ enumerable: true,
115
+ get: () => z2hSharedUtils.MANIFEST_READ_URL
116
+ });
117
+ Object.defineProperty(exports, 'PREVIEW_APP_NAME_PREFIX', {
118
+ enumerable: true,
119
+ get: () => z2hSharedUtils.PREVIEW_APP_NAME_PREFIX
120
+ });
121
+ Object.defineProperty(exports, 'REGION', {
122
+ enumerable: true,
123
+ get: () => z2hSharedUtils.REGION
124
+ });
125
+ Object.defineProperty(exports, 'S3_PREVIEWS_FOLDER', {
126
+ enumerable: true,
127
+ get: () => z2hSharedUtils.S3_PREVIEWS_FOLDER
128
+ });
129
+ Object.defineProperty(exports, 'S3_SKILLS_BUCKET_NAME', {
130
+ enumerable: true,
131
+ get: () => z2hSharedUtils.S3_SKILLS_BUCKET_NAME
132
+ });
133
+ Object.defineProperty(exports, 'appUsageFolder', {
134
+ enumerable: true,
135
+ get: () => z2hSharedUtils.appUsageFolder
136
+ });
112
137
  exports.BROKER_NPM_SCOPE = BROKER_NPM_SCOPE;
113
- exports.BUCKET_READ_DNS_NAME = BUCKET_READ_DNS_NAME;
114
- exports.BUCKET_READ_DNS_URL = BUCKET_READ_DNS_URL;
115
- exports.BUCKET_WRITE_NAME = BUCKET_WRITE_NAME;
116
- exports.BUCKET_WRITE_URL = BUCKET_WRITE_URL;
117
- exports.DATAVIZ_SKILLS_PREFIX = DATAVIZ_SKILLS_PREFIX;
118
138
  exports.DEFAULT_BROKER_BASE_URL = DEFAULT_BROKER_BASE_URL;
119
139
  exports.GITIGNORE_ENTRIES = GITIGNORE_ENTRIES;
120
- exports.MANIFEST_KEY = MANIFEST_KEY;
121
- exports.MANIFEST_READ_URL = MANIFEST_READ_URL;
122
140
  exports.MAX_PORT = MAX_PORT;
123
141
  exports.MIN_PORT = MIN_PORT;
124
142
  exports.NPM_BROKER_PATH = NPM_BROKER_PATH;
125
- exports.PREVIEW_APP_NAME_PREFIX = PREVIEW_APP_NAME_PREFIX;
126
143
  exports.PUBLIC_NPM_REGISTRY_URL = PUBLIC_NPM_REGISTRY_URL;
127
- exports.REGION = REGION;
128
- exports.S3_PREVIEWS_FOLDER = S3_PREVIEWS_FOLDER;
129
- exports.S3_SKILLS_BUCKET_NAME = S3_SKILLS_BUCKET_NAME;
130
144
  exports.SHADOW_DEV_DEPS = SHADOW_DEV_DEPS;
131
145
  exports.SHADOW_DIR_NAME = SHADOW_DIR_NAME;
132
146
  exports.SHADOW_SCRIPTS = SHADOW_SCRIPTS;
133
147
  exports.Z2H_NPM_TOKEN_ENV = Z2H_NPM_TOKEN_ENV;
134
148
  exports.brokerBaseUrl = brokerBaseUrl;
135
149
  exports.brokerNpmRegistryUrl = brokerNpmRegistryUrl;
150
+ exports.isProdBroker = isProdBroker;
136
151
  exports.routingHeaders = routingHeaders;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ast.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.test.d.ts","sourceRoot":"","sources":["../../../../src/backend/__tests__/ast.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parse.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.test.d.ts","sourceRoot":"","sources":["../../../../src/backend/__tests__/parse.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,41 @@
1
+ import type { AnyNode, Expression, Node, Pattern, Program, VariableDeclaration } from 'acorn';
2
+ /** 1-based line a node starts on; requires the program to be parsed with `locations: true`. */
3
+ export declare const lineOf: (node: Node) => number | undefined;
4
+ export interface Declaration {
5
+ kind: VariableDeclaration['kind'];
6
+ init: Expression | null | undefined;
7
+ /**
8
+ * Property path from `init` to this binding: `[]` for `const x = init`,
9
+ * `['a', 'b']` for `const { a: { b: x } } = init`, `undefined` when the value
10
+ * is not a plain property read (array/rest/default patterns).
11
+ */
12
+ path: string[] | undefined;
13
+ }
14
+ export interface Bindings {
15
+ /** Every `var|let|const <name> = …` in the file, by name. */
16
+ declarations: Map<string, Declaration[]>;
17
+ /** Names whose value cannot be pinned: reassigned, updated, or bound as parameters. */
18
+ unpinnable: Set<string>;
19
+ }
20
+ /** Every identifier a binding pattern introduces (`{ a, b: [c] } = …` → a, c). */
21
+ export declare const patternNames: (pattern: Pattern) => string[];
22
+ export declare function collectBindings(program: Program): Bindings;
23
+ export interface PathResolver {
24
+ /**
25
+ * Dotted path of a member chain, following `const` aliases back to their
26
+ * source: with `const sf = ctx.api.v1.snowflake`, `sf.query` resolves to
27
+ * `ctx.api.v1.snowflake.query`. Destructuring counts too
28
+ * (`const { snowflake: sf } = ctx.api.v1`). `undefined` if any link is dynamic.
29
+ *
30
+ * Only single, never-reassigned, never-shadowed `const` bindings are followed —
31
+ * anything else is left as written, so a `let` alias or a parameter of the same
32
+ * name is never mistaken for the channel. The handler's first parameter is
33
+ * treated as `ctx` even when renamed (`handler(context)` → `context.api.v1.db`
34
+ * resolves as `ctx.api.v1.db`).
35
+ */
36
+ pathOf(node: AnyNode): string | undefined;
37
+ }
38
+ export declare function createPathResolver(bindings: Bindings, contextParam?: string): PathResolver;
39
+ /** Every `ctx.api.v1.<domain>` the program touches (known or not), through `paths`. */
40
+ export declare function collectApiDomains(program: Program, paths: PathResolver): Set<string>;
41
+ //# sourceMappingURL=ast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../../src/backend/ast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAG9F,+FAA+F;AAC/F,eAAO,MAAM,MAAM,GAAI,MAAM,IAAI,KAAG,MAAM,GAAG,SAAiC,CAAC;AAI/E,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"}
@@ -0,0 +1,146 @@
1
+ import { full, simple } from 'acorn-walk';
2
+
3
+ /** 1-based line a node starts on; requires the program to be parsed with `locations: true`. */
4
+ const lineOf = (node) => node.loc?.start.line;
5
+ /**
6
+ * Every identifier a binding pattern introduces, with its property path from the
7
+ * pattern's value. `path` is deliberately not defaulted: callers pass `[]` for
8
+ * the root and `undefined` once the pattern stops being a plain property read.
9
+ */
10
+ function patternBindings(pattern, path) {
11
+ switch (pattern.type) {
12
+ case 'Identifier':
13
+ return [{ name: pattern.name, path }];
14
+ case 'ObjectPattern':
15
+ return pattern.properties.flatMap(p => {
16
+ if (p.type === 'RestElement') {
17
+ return patternBindings(p.argument, undefined);
18
+ }
19
+ const key = propertyKey(p.key, p.computed);
20
+ return patternBindings(p.value, path && key !== undefined ? [...path, key] : undefined);
21
+ });
22
+ case 'ArrayPattern':
23
+ return pattern.elements.flatMap(e => (e ? patternBindings(e, undefined) : []));
24
+ case 'AssignmentPattern':
25
+ return patternBindings(pattern.left, undefined);
26
+ case 'RestElement':
27
+ return patternBindings(pattern.argument, undefined);
28
+ default:
29
+ // MemberExpression targets (`obj.x = …`) bind no names.
30
+ return [];
31
+ }
32
+ }
33
+ function propertyKey(key, computed) {
34
+ if (key.type === 'Literal' && typeof key.value === 'string') {
35
+ return key.value;
36
+ }
37
+ if (!computed && key.type === 'Identifier') {
38
+ return key.name;
39
+ }
40
+ return undefined;
41
+ }
42
+ /** Every identifier a binding pattern introduces (`{ a, b: [c] } = …` → a, c). */
43
+ const patternNames = (pattern) => patternBindings(pattern, []).map(b => b.name);
44
+ function collectBindings(program) {
45
+ const declarations = new Map();
46
+ const unpinnable = new Set();
47
+ const markUnpinnable = (pattern) => patternNames(pattern).forEach(n => unpinnable.add(n));
48
+ full(program, node => {
49
+ switch (node.type) {
50
+ case 'VariableDeclaration':
51
+ for (const declarator of node.declarations) {
52
+ for (const { name, path } of patternBindings(declarator.id, [])) {
53
+ const list = declarations.get(name) ?? [];
54
+ list.push({ kind: node.kind, init: declarator.init, path });
55
+ declarations.set(name, list);
56
+ }
57
+ }
58
+ break;
59
+ case 'AssignmentExpression':
60
+ markUnpinnable(node.left);
61
+ break;
62
+ case 'UpdateExpression':
63
+ if (node.argument.type === 'Identifier') {
64
+ unpinnable.add(node.argument.name);
65
+ }
66
+ break;
67
+ case 'ForInStatement':
68
+ case 'ForOfStatement':
69
+ if (node.left.type !== 'VariableDeclaration') {
70
+ markUnpinnable(node.left);
71
+ }
72
+ break;
73
+ case 'FunctionDeclaration':
74
+ case 'FunctionExpression':
75
+ case 'ArrowFunctionExpression':
76
+ node.params.forEach(markUnpinnable);
77
+ break;
78
+ case 'CatchClause':
79
+ if (node.param) {
80
+ markUnpinnable(node.param);
81
+ }
82
+ break;
83
+ }
84
+ });
85
+ return { declarations, unpinnable };
86
+ }
87
+ /** `a.b.c` / `a['b'].c` → root `a`, parts `[b, c]`; `undefined` if any link is dynamic or the root is not an identifier. */
88
+ function memberChain(node) {
89
+ if (node.type === 'ChainExpression') {
90
+ return memberChain(node.expression);
91
+ }
92
+ const parts = [];
93
+ let cur = node;
94
+ while (cur.type === 'MemberExpression') {
95
+ const key = propertyKey(cur.property, cur.computed);
96
+ if (key === undefined) {
97
+ return undefined;
98
+ }
99
+ parts.unshift(key);
100
+ cur = cur.object;
101
+ }
102
+ return cur.type === 'Identifier' ? { root: cur.name, parts } : undefined;
103
+ }
104
+ function createPathResolver(bindings, contextParam) {
105
+ function resolveChain(node, resolving) {
106
+ const chain = memberChain(node);
107
+ if (!chain) {
108
+ return undefined;
109
+ }
110
+ return [resolveRoot(chain.root, resolving), ...chain.parts].join('.');
111
+ }
112
+ function resolveRoot(name, resolving) {
113
+ if (contextParam !== undefined && name === contextParam) {
114
+ return 'ctx';
115
+ }
116
+ if (bindings.unpinnable.has(name) || resolving.has(name)) {
117
+ return name;
118
+ }
119
+ const decls = bindings.declarations.get(name);
120
+ if (!decls || decls.length !== 1) {
121
+ return name;
122
+ }
123
+ const [{ kind, init, path }] = decls;
124
+ if (kind !== 'const' || !init || !path) {
125
+ return name;
126
+ }
127
+ const base = resolveChain(init, new Set(resolving).add(name));
128
+ return base ? [base, ...path].join('.') : name;
129
+ }
130
+ return { pathOf: node => resolveChain(node, new Set()) };
131
+ }
132
+ /** Every `ctx.api.v1.<domain>` the program touches (known or not), through `paths`. */
133
+ function collectApiDomains(program, paths) {
134
+ const apiDomains = new Set();
135
+ simple(program, {
136
+ MemberExpression(node) {
137
+ const [obj, api, version, domain] = paths.pathOf(node)?.split('.') ?? [];
138
+ if (obj === 'ctx' && api === 'api' && version === 'v1' && domain) {
139
+ apiDomains.add(domain);
140
+ }
141
+ },
142
+ });
143
+ return apiDomains;
144
+ }
145
+
146
+ export { collectApiDomains, collectBindings, createPathResolver, lineOf, patternNames };
@@ -0,0 +1,17 @@
1
+ import type { Expression, FunctionDeclaration, ModuleDeclaration, Statement } from 'acorn';
2
+ export type HandlerDeclaration = {
3
+ kind: 'function';
4
+ params: FunctionDeclaration['params'];
5
+ } | {
6
+ kind: 'variable';
7
+ init: Expression | null | undefined;
8
+ };
9
+ /** The top-level declaration that binds `handler`, if any. */
10
+ export declare function findHandlerDeclaration(body: readonly (Statement | ModuleDeclaration)[]): HandlerDeclaration | undefined;
11
+ /** `export function handler`, `export const handler`, `export { handler }`, `export default handler`. */
12
+ export declare function exportsHandler(node: Statement | ModuleDeclaration): boolean;
13
+ /** First positional parameter of `handler`, when it's a plain identifier (`ctx`, `context`, …). */
14
+ export declare function handlerContextParamName(declaration: HandlerDeclaration): string | undefined;
15
+ /** Values that can never be a function, so `const handler = <this>` is caught at build time. */
16
+ export declare const NEVER_A_FUNCTION: ReadonlySet<string>;
17
+ //# sourceMappingURL=handler-declaration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-declaration.d.ts","sourceRoot":"","sources":["../../../src/backend/handler-declaration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,UAAU,EACV,mBAAmB,EAEnB,iBAAiB,EAEjB,SAAS,EACV,MAAM,OAAO,CAAC;AAMf,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,CAAC;AAE9D,8DAA8D;AAC9D,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,SAAS,CAAC,SAAS,GAAG,iBAAiB,CAAC,EAAE,GAC/C,kBAAkB,GAAG,SAAS,CAahC;AAED,yGAAyG;AACzG,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,iBAAiB,GAAG,OAAO,CAe3E;AAsCD,mGAAmG;AACnG,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,kBAAkB,GAAG,MAAM,GAAG,SAAS,CAG3F;AAED,gGAAgG;AAChG,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAK/C,CAAC"}
@@ -0,0 +1,78 @@
1
+ /** True for the `handler` identifier specifically — used everywhere a node might name the binding. */
2
+ const isHandlerIdentifier = (candidate) => candidate?.type === 'Identifier' && candidate.name === 'handler';
3
+ /** The top-level declaration that binds `handler`, if any. */
4
+ function findHandlerDeclaration(body) {
5
+ for (const node of body) {
6
+ if (node.type === 'FunctionDeclaration' && isHandlerIdentifier(node.id)) {
7
+ return { kind: 'function', params: node.params };
8
+ }
9
+ if (node.type === 'VariableDeclaration') {
10
+ const declarator = node.declarations.find(d => isHandlerIdentifier(d.id));
11
+ if (declarator) {
12
+ return { kind: 'variable', init: declarator.init };
13
+ }
14
+ }
15
+ }
16
+ return undefined;
17
+ }
18
+ /** `export function handler`, `export const handler`, `export { handler }`, `export default handler`. */
19
+ function exportsHandler(node) {
20
+ if (node.type === 'ExportNamedDeclaration') {
21
+ return ((node.declaration != null && findHandlerDeclaration([node.declaration]) !== undefined) ||
22
+ node.specifiers.some(s => isHandlerIdentifier(s.local) || isHandlerIdentifier(s.exported)));
23
+ }
24
+ if (node.type === 'ExportDefaultDeclaration') {
25
+ const { declaration } = node;
26
+ return ((declaration.type === 'FunctionDeclaration' && isHandlerIdentifier(declaration.id)) ||
27
+ isHandlerIdentifier(declaration));
28
+ }
29
+ return false;
30
+ }
31
+ /** Peel `wrap(fn)`, `(0, fn)`, and the like until a function expression remains. */
32
+ function asFunction(expr) {
33
+ if (!expr) {
34
+ return undefined;
35
+ }
36
+ switch (expr.type) {
37
+ case 'FunctionExpression':
38
+ case 'ArrowFunctionExpression':
39
+ return expr;
40
+ case 'SequenceExpression':
41
+ return asFunction(expr.expressions.at(-1));
42
+ case 'CallExpression': {
43
+ for (const arg of expr.arguments) {
44
+ if (arg.type === 'SpreadElement') {
45
+ continue;
46
+ }
47
+ const fn = asFunction(arg);
48
+ if (fn) {
49
+ return fn;
50
+ }
51
+ }
52
+ return undefined;
53
+ }
54
+ default:
55
+ return undefined;
56
+ }
57
+ }
58
+ function firstParamName(params) {
59
+ let first = params[0];
60
+ if (first?.type === 'AssignmentPattern') {
61
+ first = first.left;
62
+ }
63
+ return first?.type === 'Identifier' ? first.name : undefined;
64
+ }
65
+ /** First positional parameter of `handler`, when it's a plain identifier (`ctx`, `context`, …). */
66
+ function handlerContextParamName(declaration) {
67
+ const params = declaration.kind === 'function' ? declaration.params : asFunction(declaration.init)?.params;
68
+ return params ? firstParamName(params) : undefined;
69
+ }
70
+ /** Values that can never be a function, so `const handler = <this>` is caught at build time. */
71
+ const NEVER_A_FUNCTION = new Set([
72
+ 'Literal',
73
+ 'ObjectExpression',
74
+ 'ArrayExpression',
75
+ 'TemplateLiteral',
76
+ ]);
77
+
78
+ export { NEVER_A_FUNCTION, exportsHandler, findHandlerDeclaration, handlerContextParamName };