@lark-base-open/base-database-cli 1.3.9 → 1.4.3
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/CHANGELOG.md +75 -0
- package/README.md +119 -15
- package/THIRD_PARTY_LICENSES.json +1662 -0
- package/config-schema.json +64 -3
- package/data/template-hmr-scripts.json +9 -0
- package/data/template-index-html/17e698eef687ece8f1628e3e2c4801da5e7233faa87076b3424acde8df57b825.html +24 -0
- package/dist/agent-tools-guide.d.ts +29 -0
- package/dist/agent-tools-guide.d.ts.map +1 -0
- package/dist/agent-tools-guide.js +169 -0
- package/dist/agent-tools-guide.js.map +1 -0
- package/dist/agent-tools-skill.d.ts +16 -0
- package/dist/agent-tools-skill.d.ts.map +1 -0
- package/dist/agent-tools-skill.js +183 -0
- package/dist/agent-tools-skill.js.map +1 -0
- package/dist/agent-tools.d.ts +20 -0
- package/dist/agent-tools.d.ts.map +1 -0
- package/dist/agent-tools.js +137 -0
- package/dist/agent-tools.js.map +1 -0
- package/dist/ai-functions.d.ts +10 -0
- package/dist/ai-functions.d.ts.map +1 -1
- package/dist/ai-functions.js +264 -212
- package/dist/ai-functions.js.map +1 -1
- package/dist/app-builder/core.d.mts +37 -0
- package/dist/app-builder/core.d.mts.map +1 -0
- package/dist/app-builder/core.mjs +673 -0
- package/dist/app-builder/core.mjs.map +1 -0
- package/dist/app-builder/package.json +1 -0
- package/dist/app-builder/schema.d.ts +4 -0
- package/dist/app-builder/schema.d.ts.map +1 -0
- package/dist/app-builder/schema.js +72 -0
- package/dist/app-builder/schema.js.map +1 -0
- package/dist/archive.d.ts +22 -2
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +311 -74
- package/dist/archive.js.map +1 -1
- package/dist/auth.d.ts +2 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +13 -8
- package/dist/auth.js.map +1 -1
- package/dist/build-server.d.ts +25 -0
- package/dist/build-server.d.ts.map +1 -0
- package/dist/build-server.js +32 -0
- package/dist/build-server.js.map +1 -0
- package/dist/business-update-notice.d.ts +37 -0
- package/dist/business-update-notice.d.ts.map +1 -0
- package/dist/business-update-notice.js +24 -0
- package/dist/business-update-notice.js.map +1 -0
- package/dist/check.d.ts +11 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +85 -15
- package/dist/check.js.map +1 -1
- package/dist/cloud-build-diagnostic.d.ts +9 -0
- package/dist/cloud-build-diagnostic.d.ts.map +1 -0
- package/dist/cloud-build-diagnostic.js +65 -0
- package/dist/cloud-build-diagnostic.js.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +41 -11
- package/dist/config.js.map +1 -1
- package/dist/contract-extractor.d.ts +21 -0
- package/dist/contract-extractor.d.ts.map +1 -0
- package/dist/contract-extractor.js +260 -0
- package/dist/contract-extractor.js.map +1 -0
- package/dist/contract-worker.d.ts +12 -0
- package/dist/contract-worker.d.ts.map +1 -0
- package/dist/contract-worker.js +90 -0
- package/dist/contract-worker.js.map +1 -0
- package/dist/dependency-policy.d.ts.map +1 -1
- package/dist/dependency-policy.js +6 -1
- package/dist/dependency-policy.js.map +1 -1
- package/dist/deploy.d.ts +20 -1
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +181 -52
- package/dist/deploy.js.map +1 -1
- package/dist/dev-bootstrap.d.ts +5 -0
- package/dist/dev-bootstrap.d.ts.map +1 -0
- package/dist/dev-bootstrap.js +185 -0
- package/dist/dev-bootstrap.js.map +1 -0
- package/dist/dev-events.d.ts +24 -0
- package/dist/dev-events.d.ts.map +1 -0
- package/dist/dev-events.js +67 -0
- package/dist/dev-events.js.map +1 -0
- package/dist/dev-pairing.d.ts +4 -0
- package/dist/dev-pairing.d.ts.map +1 -0
- package/dist/dev-pairing.js +54 -0
- package/dist/dev-pairing.js.map +1 -0
- package/dist/dev-url.d.ts +4 -0
- package/dist/dev-url.d.ts.map +1 -0
- package/dist/dev-url.js +29 -0
- package/dist/dev-url.js.map +1 -0
- package/dist/dev-vite.d.ts +9 -0
- package/dist/dev-vite.d.ts.map +1 -0
- package/dist/dev-vite.js +237 -0
- package/dist/dev-vite.js.map +1 -0
- package/dist/dev-watch.d.ts +33 -0
- package/dist/dev-watch.d.ts.map +1 -0
- package/dist/dev-watch.js +246 -0
- package/dist/dev-watch.js.map +1 -0
- package/dist/dev.d.ts +24 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.js +244 -0
- package/dist/dev.js.map +1 -0
- package/dist/development-pairing.generated.d.ts +12 -0
- package/dist/development-pairing.generated.d.ts.map +1 -0
- package/dist/development-pairing.generated.js +13 -0
- package/dist/development-pairing.generated.js.map +1 -0
- package/dist/diagnostic-platforms.d.ts +3 -0
- package/dist/diagnostic-platforms.d.ts.map +1 -0
- package/dist/diagnostic-platforms.js +3 -0
- package/dist/diagnostic-platforms.js.map +1 -0
- package/dist/diagnostic-schema.d.ts +151 -0
- package/dist/diagnostic-schema.d.ts.map +1 -0
- package/dist/diagnostic-schema.js +129 -0
- package/dist/diagnostic-schema.js.map +1 -0
- package/dist/diagnostics.d.ts +78 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +333 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/distribution-commands.d.ts.map +1 -1
- package/dist/distribution-commands.js +6 -2
- package/dist/distribution-commands.js.map +1 -1
- package/dist/distribution-npm.d.ts +2 -2
- package/dist/distribution-npm.d.ts.map +1 -1
- package/dist/distribution-npm.js +14 -1
- package/dist/distribution-npm.js.map +1 -1
- package/dist/environment-variables.d.ts.map +1 -1
- package/dist/environment-variables.js +2 -1
- package/dist/environment-variables.js.map +1 -1
- package/dist/function-contracts.d.ts +40 -0
- package/dist/function-contracts.d.ts.map +1 -0
- package/dist/function-contracts.js +357 -0
- package/dist/function-contracts.js.map +1 -0
- package/dist/function-description.d.ts +11 -4
- package/dist/function-description.d.ts.map +1 -1
- package/dist/function-description.js +22 -12
- package/dist/function-description.js.map +1 -1
- package/dist/function-input-types.d.ts +6 -4
- package/dist/function-input-types.d.ts.map +1 -1
- package/dist/function-input-types.js +97 -135
- package/dist/function-input-types.js.map +1 -1
- package/dist/function-params.d.ts +53 -0
- package/dist/function-params.d.ts.map +1 -0
- package/dist/function-params.js +184 -0
- package/dist/function-params.js.map +1 -0
- package/dist/function-schema.d.ts +14 -0
- package/dist/function-schema.d.ts.map +1 -1
- package/dist/function-schema.js +99 -9
- package/dist/function-schema.js.map +1 -1
- package/dist/init-journal.d.ts +1 -0
- package/dist/init-journal.d.ts.map +1 -1
- package/dist/init-journal.js +21 -7
- package/dist/init-journal.js.map +1 -1
- package/dist/init.d.ts +1 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +55 -5
- package/dist/init.js.map +1 -1
- package/dist/lark-cli-error.d.ts +13 -2
- package/dist/lark-cli-error.d.ts.map +1 -1
- package/dist/lark-cli-error.js +54 -13
- package/dist/lark-cli-error.js.map +1 -1
- package/dist/lark-cli-passthrough.d.ts +9 -0
- package/dist/lark-cli-passthrough.d.ts.map +1 -0
- package/dist/lark-cli-passthrough.js +101 -0
- package/dist/lark-cli-passthrough.js.map +1 -0
- package/dist/lark-cli.d.ts +5 -2
- package/dist/lark-cli.d.ts.map +1 -1
- package/dist/lark-cli.js +96 -29
- package/dist/lark-cli.js.map +1 -1
- package/dist/legacy-function-input-renderer.d.ts +6 -0
- package/dist/legacy-function-input-renderer.d.ts.map +1 -0
- package/dist/legacy-function-input-renderer.js +175 -0
- package/dist/legacy-function-input-renderer.js.map +1 -0
- package/dist/link-guard-check.d.ts +12 -0
- package/dist/link-guard-check.d.ts.map +1 -0
- package/dist/link-guard-check.js +138 -0
- package/dist/link-guard-check.js.map +1 -0
- package/dist/managed-agent-docs.d.ts +14 -0
- package/dist/managed-agent-docs.d.ts.map +1 -0
- package/dist/managed-agent-docs.js +172 -0
- package/dist/managed-agent-docs.js.map +1 -0
- package/dist/mutation-schema.d.ts +6 -0
- package/dist/mutation-schema.d.ts.map +1 -1
- package/dist/mutation-schema.js +6 -0
- package/dist/mutation-schema.js.map +1 -1
- package/dist/mutation.d.ts +4 -1
- package/dist/mutation.d.ts.map +1 -1
- package/dist/mutation.js +17 -2
- package/dist/mutation.js.map +1 -1
- package/dist/npm.d.ts +1 -0
- package/dist/npm.d.ts.map +1 -1
- package/dist/npm.js +28 -0
- package/dist/npm.js.map +1 -1
- package/dist/preview-receipt.d.ts +0 -2
- package/dist/preview-receipt.d.ts.map +1 -1
- package/dist/preview-receipt.js +0 -28
- package/dist/preview-receipt.js.map +1 -1
- package/dist/process.d.ts +1 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +25 -0
- package/dist/process.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +239 -26
- package/dist/program.js.map +1 -1
- package/dist/project-build.d.ts +2 -1
- package/dist/project-build.d.ts.map +1 -1
- package/dist/project-build.js +53 -16
- package/dist/project-build.js.map +1 -1
- package/dist/project-capabilities.d.ts +8 -0
- package/dist/project-capabilities.d.ts.map +1 -0
- package/dist/project-capabilities.js +47 -0
- package/dist/project-capabilities.js.map +1 -0
- package/dist/project-context.d.ts +66 -0
- package/dist/project-context.d.ts.map +1 -1
- package/dist/project-context.js +68 -6
- package/dist/project-context.js.map +1 -1
- package/dist/project-layout.d.ts +11 -0
- package/dist/project-layout.d.ts.map +1 -1
- package/dist/project-layout.js +63 -1
- package/dist/project-layout.js.map +1 -1
- package/dist/public-exports.d.ts +5 -0
- package/dist/public-exports.d.ts.map +1 -0
- package/dist/public-exports.js +76 -0
- package/dist/public-exports.js.map +1 -0
- package/dist/public-sql-error.d.ts +3 -1
- package/dist/public-sql-error.d.ts.map +1 -1
- package/dist/public-sql-error.js +5 -2
- package/dist/public-sql-error.js.map +1 -1
- package/dist/pull.d.ts +1 -0
- package/dist/pull.d.ts.map +1 -1
- package/dist/pull.js +103 -20
- package/dist/pull.js.map +1 -1
- package/dist/sdk-codegen.d.ts +16 -0
- package/dist/sdk-codegen.d.ts.map +1 -0
- package/dist/sdk-codegen.js +181 -0
- package/dist/sdk-codegen.js.map +1 -0
- package/dist/sdk-install.d.ts +4 -0
- package/dist/sdk-install.d.ts.map +1 -0
- package/dist/sdk-install.js +33 -0
- package/dist/sdk-install.js.map +1 -0
- package/dist/skill-frontmatter.d.ts +5 -0
- package/dist/skill-frontmatter.d.ts.map +1 -0
- package/dist/skill-frontmatter.js +28 -0
- package/dist/skill-frontmatter.js.map +1 -0
- package/dist/skill.d.ts +2 -1
- package/dist/skill.d.ts.map +1 -1
- package/dist/skill.js +18 -12
- package/dist/skill.js.map +1 -1
- package/dist/source-format.d.ts +10 -0
- package/dist/source-format.d.ts.map +1 -0
- package/dist/source-format.js +49 -0
- package/dist/source-format.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/unified-functions.d.ts +22 -0
- package/dist/unified-functions.d.ts.map +1 -0
- package/dist/unified-functions.js +114 -0
- package/dist/unified-functions.js.map +1 -0
- package/dist/updates.d.ts +13 -1
- package/dist/updates.d.ts.map +1 -1
- package/dist/updates.js +65 -29
- package/dist/updates.js.map +1 -1
- package/dist/workspace-fingerprint.d.ts.map +1 -1
- package/dist/workspace-fingerprint.js +5 -0
- package/dist/workspace-fingerprint.js.map +1 -1
- package/package.json +17 -5
- package/skills/lark-base-database/SKILL.md +21 -55
- package/skills/lark-base-database/references/agent-tool-design.md +181 -0
- package/skills/lark-base-database/references/agent-tools.md +101 -0
- package/skills/lark-base-database/references/ai-function.md +80 -186
- package/skills/lark-base-database/references/database-function-limitations.md +12 -4
- package/skills/lark-base-database/references/database-function.md +49 -21
- package/skills/lark-base-database/references/development.md +81 -109
- package/skills/lark-base-database/references/environment-variables.md +2 -2
- package/skills/lark-base-database/references/faas.md +43 -16
- package/skills/lark-base-database/references/file-transfer.md +10 -33
- package/skills/lark-base-database/references/frontend-design-guide.md +1 -1
- package/skills/lark-base-database/references/legacy-ai-function.md +120 -0
- package/skills/lark-base-database/references/legacy-development.md +156 -0
- package/skills/lark-base-database/references/legacy-file-transfer.md +68 -0
- package/skills/lark-base-database/references/mutations.md +38 -29
- package/skills/lark-base-database/references/notifications.md +1 -1
- package/skills/lark-base-database/references/permission-design.md +11 -5
- package/skills/lark-base-database/references/release.md +22 -7
- package/skills/lark-base-database/references/verification.md +55 -16
- package/skills/lark-base-database/references/workspace-migration.md +86 -0
- package/skills/lark-base-database/references/workspace.md +55 -44
- package/templates/database-starter/README.md +2 -0
- package/templates/database-starter/database-functions/README.md +2 -2
- package/templates/database-starter/mutations/README.md +2 -2
- package/templates/database-starter/page/README.md +5 -4
- package/templates/database-starter/page/src/examples/ai-function/TextStreamOperation.tsx +6 -6
- package/templates/database-starter/page/src/lib/attachment-image.tsx +19 -5
- package/templates/database-starter/page/src/lib/database-runtime-attachment.test.ts +124 -5
- package/templates/database-starter/page/src/lib/database-runtime.test.ts +2 -2
- package/templates/database-starter/page/src/lib/database-runtime.ts +77 -13
- package/templates/database-starter/server-functions/scripts/run-tsc.mjs +3 -2
- package/templates/database-starter-v2/README.md +25 -0
- package/templates/database-starter-v2/base-app.config.ts +10 -0
- package/templates/database-starter-v2/database-functions/README.md +1 -0
- package/templates/database-starter-v2/gitignore +6 -0
- package/templates/database-starter-v2/mutations/README.md +1 -0
- package/templates/database-starter-v2/package-lock.json +2239 -0
- package/templates/database-starter-v2/package.json +18 -0
- package/templates/database-starter-v2/page/index.html +2 -0
- package/templates/database-starter-v2/page/package.json +25 -0
- package/templates/database-starter-v2/page/src/App.tsx +12 -0
- package/templates/database-starter-v2/page/src/generated/README.md +1 -0
- package/templates/database-starter-v2/page/src/index.css +1 -0
- package/templates/database-starter-v2/page/src/lib/date-time.ts +61 -0
- package/templates/database-starter-v2/page/src/main.tsx +6 -0
- package/templates/database-starter-v2/page/tsconfig.json +19 -0
- package/templates/database-starter-v2/page/vite.config.ts +3 -0
- package/templates/database-starter-v2/prettierignore +3 -0
- package/templates/database-starter-v2/server/functions/README.md +1 -0
- package/templates/database-starter-v2/server/functions/hello/contract.ts +5 -0
- package/templates/database-starter-v2/server/functions/hello/index.ts +7 -0
- package/templates/database-starter-v2/server/functions/hello/meta.json +1 -0
- package/templates/database-starter-v2/server/generated/runtime-profile.ts +5 -0
- package/templates/database-starter-v2/server/package.json +17 -0
- package/templates/database-starter-v2/server/tsconfig.json +14 -0
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createRequire, isBuiltin } from 'node:module';
|
|
3
|
+
import { existsSync, lstatSync, realpathSync, readdirSync, readFileSync, mkdirSync, rmSync, writeFileSync, } from 'node:fs';
|
|
4
|
+
import { resolve, join, relative, isAbsolute, dirname } from 'node:path';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { build as esbuild, version as esbuildVersion } from 'esbuild';
|
|
7
|
+
import { parse as parseJS } from '@babel/parser';
|
|
8
|
+
import babelTraverse from '@babel/traverse';
|
|
9
|
+
import { parse as parseJSONC } from 'jsonc-parser';
|
|
10
|
+
// Shared by CLI contract discovery and the builder. Comments and unrelated
|
|
11
|
+
// identifiers never determine the runtime ABI.
|
|
12
|
+
export function defineFunctionCall(ast) {
|
|
13
|
+
const bindings = new Set(), namespaces = new Set();
|
|
14
|
+
for (const statement of ast.program.body) {
|
|
15
|
+
if (statement.type !== 'ImportDeclaration' ||
|
|
16
|
+
statement.importKind === 'type' ||
|
|
17
|
+
statement.source.value !== '@lark-base-open/base-database-app-sdk/server')
|
|
18
|
+
continue;
|
|
19
|
+
for (const specifier of statement.specifiers) {
|
|
20
|
+
if (specifier.type === 'ImportNamespaceSpecifier')
|
|
21
|
+
namespaces.add(specifier.local.name);
|
|
22
|
+
if (specifier.type === 'ImportSpecifier' &&
|
|
23
|
+
specifier.importKind !== 'type' &&
|
|
24
|
+
(specifier.imported.name ?? specifier.imported.value) ===
|
|
25
|
+
'defineFunction')
|
|
26
|
+
bindings.add(specifier.local.name);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const unwrap = (value) => {
|
|
30
|
+
while (value &&
|
|
31
|
+
[
|
|
32
|
+
'TSAsExpression',
|
|
33
|
+
'TSSatisfiesExpression',
|
|
34
|
+
'TSNonNullExpression',
|
|
35
|
+
'ParenthesizedExpression',
|
|
36
|
+
].includes(value.type))
|
|
37
|
+
value = value.expression;
|
|
38
|
+
return value;
|
|
39
|
+
};
|
|
40
|
+
const exported = ast.program.body.find((s) => s.type === 'ExportDefaultDeclaration');
|
|
41
|
+
const specifier = ast.program.body
|
|
42
|
+
.flatMap((s) => s.type === 'ExportNamedDeclaration' && !s.source ? s.specifiers : [])
|
|
43
|
+
.find((s) => (s.exported?.name ?? s.exported?.value) === 'default');
|
|
44
|
+
let value = unwrap(exported?.declaration ?? specifier?.local);
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
while (value?.type === 'Identifier' && !seen.has(value.name)) {
|
|
47
|
+
seen.add(value.name);
|
|
48
|
+
const name = value.name;
|
|
49
|
+
value = unwrap(ast.program.body
|
|
50
|
+
.flatMap((s) => {
|
|
51
|
+
const d = s.type === 'ExportNamedDeclaration' ? s.declaration : s;
|
|
52
|
+
return d?.type === 'VariableDeclaration' && d.kind === 'const'
|
|
53
|
+
? d.declarations
|
|
54
|
+
: [];
|
|
55
|
+
})
|
|
56
|
+
.find((d) => d.id.type === 'Identifier' && d.id.name === name)?.init);
|
|
57
|
+
}
|
|
58
|
+
if (value?.type !== 'CallExpression')
|
|
59
|
+
return undefined;
|
|
60
|
+
const callee = value.callee;
|
|
61
|
+
const matches = callee.type === 'Identifier'
|
|
62
|
+
? bindings.has(callee.name)
|
|
63
|
+
: callee.type === 'MemberExpression' &&
|
|
64
|
+
!callee.computed &&
|
|
65
|
+
callee.object.type === 'Identifier' &&
|
|
66
|
+
namespaces.has(callee.object.name) &&
|
|
67
|
+
callee.property.type === 'Identifier' &&
|
|
68
|
+
callee.property.name === 'defineFunction';
|
|
69
|
+
return matches ? value : undefined;
|
|
70
|
+
}
|
|
71
|
+
export function classifyFunctionAST(ast) {
|
|
72
|
+
if (defineFunctionCall(ast))
|
|
73
|
+
return 'sdk';
|
|
74
|
+
const hasSDK = ast.program.body.some((s) => s.type === 'ImportDeclaration' &&
|
|
75
|
+
s.importKind !== 'type' &&
|
|
76
|
+
s.source.value === '@lark-base-open/base-database-app-sdk/server');
|
|
77
|
+
const exported = ast.program.body.find((s) => s.type === 'ExportDefaultDeclaration');
|
|
78
|
+
if (exported &&
|
|
79
|
+
[
|
|
80
|
+
'FunctionDeclaration',
|
|
81
|
+
'FunctionExpression',
|
|
82
|
+
'ArrowFunctionExpression',
|
|
83
|
+
].includes(exported.declaration.type))
|
|
84
|
+
return 'handler';
|
|
85
|
+
return hasSDK || !exported ? 'unknown' : 'handler';
|
|
86
|
+
}
|
|
87
|
+
export function isDefineFunctionSource(source) {
|
|
88
|
+
const kind = classifyFunctionAST(parseJS(source, { sourceType: 'module', plugins: ['typescript', 'jsx'] }));
|
|
89
|
+
if (kind === 'unknown')
|
|
90
|
+
fail('FUNCTION_ENTRY_UNKNOWN: 无法确认默认导出;请直接默认导出 SDK defineFunction(...) 或函数 handler,再运行 contracts generate 和 check。');
|
|
91
|
+
return kind === 'sdk';
|
|
92
|
+
}
|
|
93
|
+
export function parseProjectConfig(source, label = 'base-database.jsonc') {
|
|
94
|
+
const errors = [];
|
|
95
|
+
const value = parseJSONC(source, errors, { allowTrailingComma: true });
|
|
96
|
+
if (errors.length ||
|
|
97
|
+
!value ||
|
|
98
|
+
typeof value !== 'object' ||
|
|
99
|
+
Array.isArray(value))
|
|
100
|
+
fail(`Invalid ${label}: expected a JSONC object without parse errors`);
|
|
101
|
+
if (value.template !== undefined &&
|
|
102
|
+
(!value.template ||
|
|
103
|
+
typeof value.template !== 'object' ||
|
|
104
|
+
Array.isArray(value.template) ||
|
|
105
|
+
![1, 2].includes(value.template.version) ||
|
|
106
|
+
(value.template.name !== undefined &&
|
|
107
|
+
value.template.name !== 'database-starter')))
|
|
108
|
+
fail(`Invalid ${label}: unsupported template. Fix template only for a known starter version; otherwise use a CLI that supports this template. Do not remove it to bypass validation`);
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
export const BUILDER_VERSION = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8')).version;
|
|
112
|
+
export const ERROR_MARKER = 'BASE_DATABASE_BUILD_ERROR:';
|
|
113
|
+
const hash = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
114
|
+
const readJSON = (file) => JSON.parse(readFileSync(file, 'utf8'));
|
|
115
|
+
function fail(message) {
|
|
116
|
+
throw new Error(message);
|
|
117
|
+
}
|
|
118
|
+
export function diagnostic(error, stage = 'build', functionId = null) {
|
|
119
|
+
const loc = error.errors?.[0]?.location;
|
|
120
|
+
const match = String(error.message).match(/([^\n]+)\((\d+),(\d+)\): error/);
|
|
121
|
+
functionId ??=
|
|
122
|
+
match?.[1]?.match(/functions[\/\\]([^\/\\]+)[\/\\]/)?.[1] ?? null;
|
|
123
|
+
return {
|
|
124
|
+
builderVersion: BUILDER_VERSION,
|
|
125
|
+
stage,
|
|
126
|
+
functionId,
|
|
127
|
+
file: loc?.file ?? match?.[1] ?? null,
|
|
128
|
+
line: loc?.line ?? (match ? +match[2] : null),
|
|
129
|
+
column: loc?.column ?? (match ? +match[3] : null),
|
|
130
|
+
message: String(error.message),
|
|
131
|
+
hint: `builderVersion=${BUILDER_VERSION}; 修复源码、依赖或产物错误。`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export function layout(root) {
|
|
135
|
+
const config = existsSync(join(root, 'base-database.jsonc'))
|
|
136
|
+
? parseProjectConfig(readFileSync(join(root, 'base-database.jsonc'), 'utf8'))
|
|
137
|
+
: {};
|
|
138
|
+
const version = config.template === undefined ? 1 : config.template?.version;
|
|
139
|
+
if (![1, 2].includes(version))
|
|
140
|
+
fail('Unknown template.version');
|
|
141
|
+
return {
|
|
142
|
+
version,
|
|
143
|
+
server: version === 2 ? 'server' : 'server-functions',
|
|
144
|
+
config,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
export function safePath(root, name) {
|
|
148
|
+
if (typeof name !== 'string' ||
|
|
149
|
+
!name ||
|
|
150
|
+
isAbsolute(name) ||
|
|
151
|
+
name.includes('\\') ||
|
|
152
|
+
name.split('/').some((p) => p === '..' || p === '.' || !p))
|
|
153
|
+
fail(`Invalid artifact path: ${name}`);
|
|
154
|
+
let file = root;
|
|
155
|
+
if (lstatSync(root).isSymbolicLink())
|
|
156
|
+
fail('Artifact root is a symlink');
|
|
157
|
+
for (const part of name.split('/')) {
|
|
158
|
+
file = join(file, part);
|
|
159
|
+
if (lstatSync(file).isSymbolicLink())
|
|
160
|
+
fail(`Artifact symlink: ${name}`);
|
|
161
|
+
}
|
|
162
|
+
if (!lstatSync(file).isFile())
|
|
163
|
+
fail(`Not a regular file: ${name}`);
|
|
164
|
+
return file;
|
|
165
|
+
}
|
|
166
|
+
export function inventory(root, exclude = []) {
|
|
167
|
+
const files = [];
|
|
168
|
+
let total = 0;
|
|
169
|
+
const visit = (dir, depth = 0) => {
|
|
170
|
+
if (lstatSync(dir).isSymbolicLink())
|
|
171
|
+
fail(`Artifact symlink: ${relative(root, dir)}`);
|
|
172
|
+
for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
173
|
+
const file = join(dir, entry.name), name = relative(root, file);
|
|
174
|
+
if (entry.isSymbolicLink())
|
|
175
|
+
fail(`Artifact symlink: ${name}`);
|
|
176
|
+
if (entry.isDirectory())
|
|
177
|
+
visit(file, depth + 1);
|
|
178
|
+
else {
|
|
179
|
+
safePath(root, name);
|
|
180
|
+
if (exclude.includes(name))
|
|
181
|
+
continue;
|
|
182
|
+
const bytes = readFileSync(file);
|
|
183
|
+
total += bytes.length;
|
|
184
|
+
files.push({ path: name, bytes: bytes.length, sha256: hash(bytes) });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
visit(root);
|
|
189
|
+
return files;
|
|
190
|
+
}
|
|
191
|
+
export function functionInventory(directory, requiredFiles = []) {
|
|
192
|
+
if (!existsSync(directory))
|
|
193
|
+
return [];
|
|
194
|
+
if (lstatSync(directory).isSymbolicLink())
|
|
195
|
+
fail('Function directory is a symlink');
|
|
196
|
+
return readdirSync(directory, { withFileTypes: true })
|
|
197
|
+
.filter((e) => !e.isFile())
|
|
198
|
+
.map((e) => {
|
|
199
|
+
if (!e.isDirectory() ||
|
|
200
|
+
e.isSymbolicLink() ||
|
|
201
|
+
!/^[A-Za-z0-9_-]{1,64}$/.test(e.name))
|
|
202
|
+
fail(`Invalid Function ID: ${e.name}`);
|
|
203
|
+
for (const file of requiredFiles) {
|
|
204
|
+
const path = join(directory, e.name, file);
|
|
205
|
+
if (!existsSync(path) ||
|
|
206
|
+
lstatSync(path).isSymbolicLink() ||
|
|
207
|
+
!lstatSync(path).isFile())
|
|
208
|
+
fail(`FUNCTION_SOURCE_INCOMPLETE: ${path} is required; restore the declaration or remove the entire Function directory`);
|
|
209
|
+
}
|
|
210
|
+
return e.name;
|
|
211
|
+
})
|
|
212
|
+
.sort();
|
|
213
|
+
}
|
|
214
|
+
export function ids(root, server) {
|
|
215
|
+
return functionInventory(join(root, server, 'functions'), [
|
|
216
|
+
'index.ts',
|
|
217
|
+
'meta.json',
|
|
218
|
+
]);
|
|
219
|
+
}
|
|
220
|
+
function walk(value, fn) {
|
|
221
|
+
if (!value || typeof value !== 'object')
|
|
222
|
+
return;
|
|
223
|
+
if (typeof value.type === 'string')
|
|
224
|
+
fn(value);
|
|
225
|
+
for (const [key, child] of Object.entries(value))
|
|
226
|
+
if (!['loc', 'start', 'end', 'tokens', 'comments'].includes(key)) {
|
|
227
|
+
if (Array.isArray(child))
|
|
228
|
+
child.forEach((v) => walk(v, fn));
|
|
229
|
+
else
|
|
230
|
+
walk(child, fn);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
export function run(command, args, cwd) {
|
|
234
|
+
const result = spawnSync(command, args, {
|
|
235
|
+
cwd,
|
|
236
|
+
encoding: 'utf8',
|
|
237
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
238
|
+
});
|
|
239
|
+
if (result.error || result.status !== 0)
|
|
240
|
+
fail(result.error?.message ??
|
|
241
|
+
`${command} failed (${result.status})\n${result.stdout}\n${result.stderr}`);
|
|
242
|
+
}
|
|
243
|
+
// Package ownership comes from resolved package installations, never a substring
|
|
244
|
+
// in a source path. Canonical roots also cover pnpm and workspace symlinks.
|
|
245
|
+
function optionalDependencyResolver(root, server) {
|
|
246
|
+
const owners = new Set();
|
|
247
|
+
const missing = new Set();
|
|
248
|
+
const approved = new Set();
|
|
249
|
+
const stubbed = new Set();
|
|
250
|
+
const canonical = (file) => realpathSync(file);
|
|
251
|
+
const excluded = new Set([canonical(root), canonical(server)]);
|
|
252
|
+
const packageName = (name) => /^(?:@[a-zA-Z0-9_.-]+\/)?[a-zA-Z0-9_][a-zA-Z0-9_.-]*(?:\/|$)/.exec(name)?.[0].replace(/\/$/, '');
|
|
253
|
+
const key = (importer, name, kind) => JSON.stringify([canonical(importer), name, kind]);
|
|
254
|
+
function owner(file) {
|
|
255
|
+
let dir = dirname(canonical(file));
|
|
256
|
+
while (true) {
|
|
257
|
+
if (owners.has(dir))
|
|
258
|
+
return dir;
|
|
259
|
+
const parent = dirname(dir);
|
|
260
|
+
if (parent === dir)
|
|
261
|
+
return null;
|
|
262
|
+
dir = parent;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function installation(name, importer) {
|
|
266
|
+
// Node package self-references are installed even without a node_modules
|
|
267
|
+
// link. Nested manifest copies do not define dependency ownership.
|
|
268
|
+
if (existsSync(importer)) {
|
|
269
|
+
let dir = dirname(canonical(importer));
|
|
270
|
+
while (true) {
|
|
271
|
+
const manifest = join(dir, 'package.json');
|
|
272
|
+
if (existsSync(manifest)) {
|
|
273
|
+
if (readJSON(manifest).name === name)
|
|
274
|
+
return dir;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
const parent = dirname(dir);
|
|
278
|
+
if (parent === dir)
|
|
279
|
+
break;
|
|
280
|
+
dir = parent;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const req = createRequire(importer);
|
|
284
|
+
for (const dir of req.resolve.paths(name) ?? []) {
|
|
285
|
+
const candidate = join(dir, name);
|
|
286
|
+
// Include broken symlinks: an installed but broken package is not absent.
|
|
287
|
+
if (existsSync(candidate) || lstatSync(candidate, { throwIfNoEntry: false }))
|
|
288
|
+
return candidate;
|
|
289
|
+
}
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
stubbed,
|
|
294
|
+
approve(metafile) {
|
|
295
|
+
for (const [file, input] of Object.entries(metafile.inputs)) {
|
|
296
|
+
if (!existsSync(resolve(root, file)))
|
|
297
|
+
continue;
|
|
298
|
+
for (const item of input.imports) {
|
|
299
|
+
if (!item.external || isBuiltin(item.path))
|
|
300
|
+
continue;
|
|
301
|
+
const k = key(resolve(root, file), item.path, item.kind);
|
|
302
|
+
if (!missing.has(k) || !owners.has(owner(resolve(root, file))))
|
|
303
|
+
fail(`Only Node built-ins may be external: ${file}: ${item.path}`);
|
|
304
|
+
// esbuild only preserves unresolved imports when it recognizes error
|
|
305
|
+
// handling. A required use (even of the same name) fails pass one.
|
|
306
|
+
approved.add(k);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return approved.size > 0;
|
|
310
|
+
},
|
|
311
|
+
setup(b) {
|
|
312
|
+
b.onResolve({ filter: /.*/ }, async (args) => {
|
|
313
|
+
if (args.pluginData?.optionalResolution || args.namespace === 'optional-missing')
|
|
314
|
+
return;
|
|
315
|
+
if (args.importer && existsSync(args.importer) && approved.has(key(args.importer, args.path, args.kind))) {
|
|
316
|
+
stubbed.add(args.path);
|
|
317
|
+
return { path: args.path, namespace: 'optional-missing' };
|
|
318
|
+
}
|
|
319
|
+
const name = packageName(args.path);
|
|
320
|
+
if (!name || isBuiltin(args.path) || !args.importer)
|
|
321
|
+
return;
|
|
322
|
+
const result = await b.resolve(args.path, {
|
|
323
|
+
importer: args.importer, resolveDir: args.resolveDir,
|
|
324
|
+
kind: args.kind, namespace: args.namespace,
|
|
325
|
+
pluginData: { optionalResolution: true },
|
|
326
|
+
});
|
|
327
|
+
const installed = installation(name, args.importer);
|
|
328
|
+
if (!result.errors.length && result.path && !result.external) {
|
|
329
|
+
if (installed) {
|
|
330
|
+
const dir = canonical(installed);
|
|
331
|
+
const file = canonical(result.path);
|
|
332
|
+
const rel = relative(dir, file);
|
|
333
|
+
if (!excluded.has(dir) && rel && !rel.startsWith('../') && !isAbsolute(rel))
|
|
334
|
+
owners.add(dir);
|
|
335
|
+
}
|
|
336
|
+
// Use esbuild's original result; no custom resolution or externals.
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
if (result.errors.length && !installed && existsSync(args.importer) &&
|
|
340
|
+
['require-call', 'dynamic-import'].includes(args.kind))
|
|
341
|
+
missing.add(key(args.importer, args.path, args.kind));
|
|
342
|
+
// Let esbuild classify failure itself, including its try/import.catch
|
|
343
|
+
// handling. Returning resolver errors here would erase that distinction.
|
|
344
|
+
});
|
|
345
|
+
// ESM initialization retries after a throw. A CommonJS stub would cache
|
|
346
|
+
// its partial exports and incorrectly succeed on the second require.
|
|
347
|
+
b.onLoad({ filter: /.*/, namespace: 'optional-missing' }, (args) => ({
|
|
348
|
+
contents: `const error = new Error(${JSON.stringify("Cannot find module '")} + ${JSON.stringify(args.path)} + "'"); error.code = 'MODULE_NOT_FOUND'; throw error; export {};`,
|
|
349
|
+
loader: 'js',
|
|
350
|
+
}));
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
export async function buildProject(root, options = {}) {
|
|
355
|
+
root = resolve(root);
|
|
356
|
+
const info = layout(root), server = join(root, info.server), output = join(server, 'dist');
|
|
357
|
+
let stage = 'typecheck', functionId = null;
|
|
358
|
+
try {
|
|
359
|
+
if (existsSync(server) && lstatSync(server).isSymbolicLink())
|
|
360
|
+
fail('Server directory is a symlink');
|
|
361
|
+
const functions = ids(root, info.server);
|
|
362
|
+
if (functions.length) {
|
|
363
|
+
const require = createRequire(join(server, 'package.json'));
|
|
364
|
+
const tsc = join(dirname(require.resolve('typescript/package.json')), 'bin/tsc');
|
|
365
|
+
run(process.execPath, [
|
|
366
|
+
tsc,
|
|
367
|
+
'--noEmit',
|
|
368
|
+
'--noCheck',
|
|
369
|
+
'false',
|
|
370
|
+
'--project',
|
|
371
|
+
join(server, 'tsconfig.json'),
|
|
372
|
+
], root);
|
|
373
|
+
}
|
|
374
|
+
stage = 'bundle';
|
|
375
|
+
if (existsSync(output) && lstatSync(output).isSymbolicLink())
|
|
376
|
+
fail('Output directory is a symlink');
|
|
377
|
+
const cacheFile = join(root, '.base-database-cli/state/platform-builder-cache.json');
|
|
378
|
+
let previous;
|
|
379
|
+
if (options.incremental &&
|
|
380
|
+
existsSync(join(output, 'build-manifest.json'))) {
|
|
381
|
+
const cached = readJSON(join(output, 'build-manifest.json'));
|
|
382
|
+
if (cached.functions?.some((fn) => typeof fn.id !== 'string' || !/^[A-Za-z0-9_-]{1,64}$/.test(fn.id)))
|
|
383
|
+
fail('Invalid cached Function ID');
|
|
384
|
+
try {
|
|
385
|
+
previous = readBuildCache(output);
|
|
386
|
+
const cache = readJSON(cacheFile);
|
|
387
|
+
if (cache.manifestDigest !==
|
|
388
|
+
hash(readFileSync(join(output, 'build-manifest.json'))))
|
|
389
|
+
fail('Build cache mismatch');
|
|
390
|
+
previous.functions = previous.functions.map((fn) => ({
|
|
391
|
+
...fn,
|
|
392
|
+
inputHashes: cache.functions.find((item) => item.id === fn.id)
|
|
393
|
+
?.inputHashes,
|
|
394
|
+
}));
|
|
395
|
+
if (previous.builderVersion !== BUILDER_VERSION ||
|
|
396
|
+
previous.esbuildVersion !== esbuildVersion)
|
|
397
|
+
previous = undefined;
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
previous = undefined;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (!previous)
|
|
404
|
+
rmSync(output, { recursive: true, force: true });
|
|
405
|
+
else
|
|
406
|
+
for (const fn of previous.functions)
|
|
407
|
+
if (!functions.includes(fn.id))
|
|
408
|
+
rmSync(join(output, 'functions', fn.id), {
|
|
409
|
+
recursive: true,
|
|
410
|
+
force: true,
|
|
411
|
+
});
|
|
412
|
+
mkdirSync(output, { recursive: true });
|
|
413
|
+
rmSync(join(output, 'build-manifest.json'), { force: true });
|
|
414
|
+
writeFileSync(join(output, 'package.json'), '{"type":"commonjs"}\n');
|
|
415
|
+
const entries = [];
|
|
416
|
+
const sourceHashes = {};
|
|
417
|
+
let rebuilt = 0;
|
|
418
|
+
for (const id of functions) {
|
|
419
|
+
options.signal?.throwIfAborted();
|
|
420
|
+
functionId = id;
|
|
421
|
+
const entry = safePath(server, `functions/${id}/index.ts`);
|
|
422
|
+
const cached = previous?.functions.find((fn) => fn.id === id);
|
|
423
|
+
if (cached?.inputHashes &&
|
|
424
|
+
!cached.stubbedModules?.length &&
|
|
425
|
+
!isDefineFunctionSource(readFileSync(entry, 'utf8')) &&
|
|
426
|
+
Object.entries(cached.inputHashes).every(([p, h]) => existsSync(resolve(root, p)) &&
|
|
427
|
+
hash(readFileSync(resolve(root, p))) === h)) {
|
|
428
|
+
entries.push(cached);
|
|
429
|
+
Object.assign(sourceHashes, cached.inputHashes);
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
rebuilt++;
|
|
433
|
+
rmSync(join(output, 'functions', id), { recursive: true, force: true });
|
|
434
|
+
const source = readFileSync(entry, 'utf8');
|
|
435
|
+
const sdk = isDefineFunctionSource(source);
|
|
436
|
+
let profile = options.profile ?? {
|
|
437
|
+
platformEnv: info.config.runtimeProfile ?? 'online',
|
|
438
|
+
brand: options.env?.LARKSUITE_CLI_BRAND ?? 'feishu',
|
|
439
|
+
};
|
|
440
|
+
const profilePath = join(server, 'generated/runtime-profile.ts');
|
|
441
|
+
if (sdk && existsSync(profilePath)) {
|
|
442
|
+
const ast = parseJS(readFileSync(profilePath, 'utf8'), {
|
|
443
|
+
sourceType: 'module',
|
|
444
|
+
plugins: ['typescript'],
|
|
445
|
+
});
|
|
446
|
+
const declaration = ast.program.body[0]?.declaration?.declarations?.[0];
|
|
447
|
+
const expression = declaration?.init?.type === 'TSAsExpression'
|
|
448
|
+
? declaration.init.expression
|
|
449
|
+
: declaration?.init;
|
|
450
|
+
if (declaration?.id?.name !== 'runtimeProfile' ||
|
|
451
|
+
expression?.type !== 'ObjectExpression')
|
|
452
|
+
fail('FAAS_RUNTIME_PROFILE_INVALID');
|
|
453
|
+
profile = {};
|
|
454
|
+
for (const property of expression.properties) {
|
|
455
|
+
if (property.type !== 'ObjectProperty' ||
|
|
456
|
+
property.computed ||
|
|
457
|
+
property.key.type !== 'Identifier' ||
|
|
458
|
+
property.value.type !== 'StringLiteral')
|
|
459
|
+
fail('FAAS_RUNTIME_PROFILE_INVALID');
|
|
460
|
+
profile[property.key.name] = property.value.value;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (typeof profile.platformEnv !== 'string' ||
|
|
464
|
+
!/^[a-z][a-z0-9_-]*$/.test(profile.platformEnv) ||
|
|
465
|
+
!['feishu', 'lark'].includes(profile.brand))
|
|
466
|
+
fail('FAAS_RUNTIME_PROFILE_INVALID');
|
|
467
|
+
const stdin = sdk
|
|
468
|
+
? {
|
|
469
|
+
contents: `import handler from ${JSON.stringify(entry)}; import {bindFunctionRuntime} from '@lark-base-open/base-database-app-sdk/server'; export default bindFunctionRuntime(handler, ${JSON.stringify(profile)});`,
|
|
470
|
+
resolveDir: server,
|
|
471
|
+
sourcefile: 'platform-entry.ts',
|
|
472
|
+
loader: 'ts',
|
|
473
|
+
}
|
|
474
|
+
: undefined;
|
|
475
|
+
const optional = optionalDependencyResolver(root, server);
|
|
476
|
+
const buildOptions = {
|
|
477
|
+
absWorkingDir: root,
|
|
478
|
+
...(stdin ? { stdin } : { entryPoints: [entry] }),
|
|
479
|
+
outfile: join(output, 'functions', id, 'index.js'),
|
|
480
|
+
bundle: true,
|
|
481
|
+
format: 'cjs',
|
|
482
|
+
platform: 'node',
|
|
483
|
+
target: 'node22',
|
|
484
|
+
metafile: true,
|
|
485
|
+
logLevel: 'silent',
|
|
486
|
+
sourcemap: 'external',
|
|
487
|
+
sourcesContent: false,
|
|
488
|
+
assetNames: 'assets/[name]-[hash]',
|
|
489
|
+
loader: {
|
|
490
|
+
'.json': 'json',
|
|
491
|
+
'.txt': 'file',
|
|
492
|
+
'.wasm': 'file',
|
|
493
|
+
'.bin': 'file',
|
|
494
|
+
'.png': 'file',
|
|
495
|
+
'.svg': 'file',
|
|
496
|
+
},
|
|
497
|
+
plugins: [
|
|
498
|
+
{
|
|
499
|
+
name: 'platform-contract',
|
|
500
|
+
setup(b) {
|
|
501
|
+
b.onResolve({
|
|
502
|
+
filter: /^(react(?:-dom)?(?:\/|$)|@lark-base-open\/base-database-app-sdk\/(page|components|testing|vite)(?:\/|$))/,
|
|
503
|
+
}, (args) => {
|
|
504
|
+
fail(`FAAS_UI_DEPENDENCY_UNSUPPORTED: ${args.path}`);
|
|
505
|
+
});
|
|
506
|
+
b.onResolve({ filter: /\.node$/ }, (args) => {
|
|
507
|
+
fail(`DEPENDENCY_RUNTIME_UNSUPPORTED: ${args.path}`);
|
|
508
|
+
});
|
|
509
|
+
b.onLoad({ filter: /\.node$/ }, (args) => {
|
|
510
|
+
fail(`DEPENDENCY_RUNTIME_UNSUPPORTED: ${args.path}`);
|
|
511
|
+
});
|
|
512
|
+
b.onLoad({ filter: /\.[cm]?[jt]sx?$/, namespace: 'file' }, (args) => {
|
|
513
|
+
const ast = parseJS(readFileSync(args.path, 'utf8'), {
|
|
514
|
+
sourceType: 'unambiguous',
|
|
515
|
+
plugins: ['typescript', 'jsx'],
|
|
516
|
+
});
|
|
517
|
+
const traverse = babelTraverse.default ?? babelTraverse;
|
|
518
|
+
traverse(ast, {
|
|
519
|
+
enter(path) {
|
|
520
|
+
const n = path.node;
|
|
521
|
+
const call = n.type === 'CallExpression' &&
|
|
522
|
+
((n.callee?.name === 'require' &&
|
|
523
|
+
!path.scope.getBinding('require')) ||
|
|
524
|
+
n.callee?.type === 'Import');
|
|
525
|
+
if ((call &&
|
|
526
|
+
(n.arguments?.length !== 1 ||
|
|
527
|
+
n.arguments[0]?.type !== 'StringLiteral')) ||
|
|
528
|
+
(n.type === 'ImportExpression' &&
|
|
529
|
+
n.source?.type !== 'StringLiteral'))
|
|
530
|
+
fail(`${n.callee?.name === 'require' ? 'FAAS_DYNAMIC_REQUIRE_UNSUPPORTED' : 'FAAS_DYNAMIC_IMPORT_UNSUPPORTED'}: ${args.path}:${n.loc?.start.line}`);
|
|
531
|
+
if (n.type === 'MetaProperty' &&
|
|
532
|
+
n.meta?.name === 'import' &&
|
|
533
|
+
n.property?.name === 'meta')
|
|
534
|
+
fail(`FAAS_IMPORT_META_UNSUPPORTED: ${args.path}:${n.loc?.start.line}`);
|
|
535
|
+
},
|
|
536
|
+
});
|
|
537
|
+
return undefined;
|
|
538
|
+
});
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
{ name: 'optional-dependencies', setup: optional.setup },
|
|
542
|
+
],
|
|
543
|
+
};
|
|
544
|
+
// Pass one must succeed without stubs. Its input edges, not our AST,
|
|
545
|
+
// determine whether esbuild accepted a missing import as optional.
|
|
546
|
+
let result = await esbuild({ ...buildOptions, write: false });
|
|
547
|
+
if (optional.approve(result.metafile))
|
|
548
|
+
result = await esbuild(buildOptions);
|
|
549
|
+
else
|
|
550
|
+
for (const file of result.outputFiles) {
|
|
551
|
+
mkdirSync(dirname(file.path), { recursive: true });
|
|
552
|
+
writeFileSync(file.path, file.contents);
|
|
553
|
+
}
|
|
554
|
+
const externalModules = [
|
|
555
|
+
...new Set(Object.values(result.metafile.outputs).flatMap((o) => o.imports.filter((i) => i.external).map((i) => i.path))),
|
|
556
|
+
].sort();
|
|
557
|
+
if (externalModules.some((p) => !isBuiltin(p)))
|
|
558
|
+
fail('Only Node built-ins may be external');
|
|
559
|
+
const inputHashes = {};
|
|
560
|
+
for (const file of [
|
|
561
|
+
...Object.keys(result.metafile.inputs),
|
|
562
|
+
'base-database.jsonc',
|
|
563
|
+
'package.json',
|
|
564
|
+
'package-lock.json',
|
|
565
|
+
`${info.server}/package.json`,
|
|
566
|
+
`${info.server}/tsconfig.json`,
|
|
567
|
+
`${info.server}/package-lock.json`,
|
|
568
|
+
`${info.server}/generated/runtime-profile.ts`,
|
|
569
|
+
])
|
|
570
|
+
if (existsSync(resolve(root, file)))
|
|
571
|
+
inputHashes[file] = hash(readFileSync(resolve(root, file)));
|
|
572
|
+
Object.assign(sourceHashes, inputHashes);
|
|
573
|
+
const files = Object.keys(result.metafile.outputs).map((p) => {
|
|
574
|
+
const name = relative(output, resolve(root, p)), bytes = readFileSync(safePath(output, name));
|
|
575
|
+
return { path: name, bytes: bytes.length, sha256: hash(bytes) };
|
|
576
|
+
});
|
|
577
|
+
entries.push({
|
|
578
|
+
id,
|
|
579
|
+
entry: `functions/${id}/index.js`,
|
|
580
|
+
export: 'default',
|
|
581
|
+
type: sdk ? 'defineFunction' : 'handler',
|
|
582
|
+
externalModules,
|
|
583
|
+
stubbedModules: [...optional.stubbed].sort(),
|
|
584
|
+
files,
|
|
585
|
+
inputHashes,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
const commonFiles = inventory(output).filter((f) => !entries.some((e) => e.files.some((g) => g.path === f.path)));
|
|
589
|
+
const lock = join(info.version === 2 ? root : server, 'package-lock.json');
|
|
590
|
+
const manifest = {
|
|
591
|
+
contract: 'base-database-functions',
|
|
592
|
+
contractVersion: 1,
|
|
593
|
+
nodeMajor: 22,
|
|
594
|
+
format: 'cjs',
|
|
595
|
+
builderVersion: BUILDER_VERSION,
|
|
596
|
+
esbuildVersion,
|
|
597
|
+
templateVersion: info.version,
|
|
598
|
+
serverDirectory: info.server,
|
|
599
|
+
sourceDigest: hash(JSON.stringify(sourceHashes)),
|
|
600
|
+
lockfileDigest: existsSync(lock) ? hash(readFileSync(lock)) : null,
|
|
601
|
+
runtimeBindingABI: 'bindFunctionRuntime@1',
|
|
602
|
+
functions: entries.map(({ inputHashes, ...fn }) => fn),
|
|
603
|
+
commonFiles,
|
|
604
|
+
totalBytes: inventory(output).reduce((sum, f) => sum + f.bytes, 0),
|
|
605
|
+
};
|
|
606
|
+
writeFileSync(join(output, 'build-manifest.json'), JSON.stringify(manifest, null, 2) + '\n');
|
|
607
|
+
stage = 'verify';
|
|
608
|
+
readBuildCache(output, functions);
|
|
609
|
+
if (options.incremental) {
|
|
610
|
+
for (const path of [
|
|
611
|
+
join(root, '.base-database-cli'),
|
|
612
|
+
dirname(cacheFile),
|
|
613
|
+
cacheFile,
|
|
614
|
+
])
|
|
615
|
+
if (existsSync(path) && lstatSync(path).isSymbolicLink())
|
|
616
|
+
fail('Build cache symlink');
|
|
617
|
+
mkdirSync(dirname(cacheFile), { recursive: true });
|
|
618
|
+
writeFileSync(cacheFile, JSON.stringify({
|
|
619
|
+
manifestDigest: hash(readFileSync(join(output, 'build-manifest.json'))),
|
|
620
|
+
functions: entries.map(({ id, inputHashes }) => ({
|
|
621
|
+
id,
|
|
622
|
+
inputHashes,
|
|
623
|
+
})),
|
|
624
|
+
}));
|
|
625
|
+
}
|
|
626
|
+
return {
|
|
627
|
+
ok: true,
|
|
628
|
+
functions: functions.length,
|
|
629
|
+
rebuilt,
|
|
630
|
+
output,
|
|
631
|
+
builderVersion: BUILDER_VERSION,
|
|
632
|
+
stubbedModules: [...new Set(entries.flatMap((fn) => fn.stubbedModules ?? []))].sort(),
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
catch (error) {
|
|
636
|
+
if (existsSync(server) &&
|
|
637
|
+
!lstatSync(server).isSymbolicLink() &&
|
|
638
|
+
existsSync(output) &&
|
|
639
|
+
!lstatSync(output).isSymbolicLink())
|
|
640
|
+
rmSync(output, { recursive: true, force: true });
|
|
641
|
+
error.diagnostic = diagnostic(error, stage, functionId);
|
|
642
|
+
throw error;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
/** Local cache integrity only; deployment policy belongs to the platform. */
|
|
646
|
+
export function readBuildCache(output, expectedIds) {
|
|
647
|
+
const manifest = readJSON(safePath(output, 'build-manifest.json'));
|
|
648
|
+
if (manifest.contract !== 'base-database-functions' ||
|
|
649
|
+
manifest.contractVersion !== 1 ||
|
|
650
|
+
!Array.isArray(manifest.functions) ||
|
|
651
|
+
!Array.isArray(manifest.commonFiles))
|
|
652
|
+
fail('Invalid build manifest schema');
|
|
653
|
+
const files = [...manifest.commonFiles];
|
|
654
|
+
for (const fn of manifest.functions) {
|
|
655
|
+
if (typeof fn.id !== 'string' ||
|
|
656
|
+
!/^[A-Za-z0-9_-]{1,64}$/.test(fn.id) ||
|
|
657
|
+
fn.entry !== `functions/${fn.id}/index.js` ||
|
|
658
|
+
!Array.isArray(fn.files))
|
|
659
|
+
fail('Invalid cached Function ID');
|
|
660
|
+
files.push(...fn.files);
|
|
661
|
+
}
|
|
662
|
+
if (expectedIds &&
|
|
663
|
+
JSON.stringify(manifest.functions.map((f) => f.id).sort()) !==
|
|
664
|
+
JSON.stringify([...expectedIds].sort()))
|
|
665
|
+
fail('Function entry set mismatch');
|
|
666
|
+
for (const file of files) {
|
|
667
|
+
const data = readFileSync(safePath(output, file.path));
|
|
668
|
+
if (data.length !== file.bytes || hash(data) !== file.sha256)
|
|
669
|
+
fail(`Artifact hash/size mismatch: ${file.path}`);
|
|
670
|
+
}
|
|
671
|
+
return manifest;
|
|
672
|
+
}
|
|
673
|
+
//# sourceMappingURL=core.mjs.map
|