@mondaydotcomorg/z2h-cli 0.27.1 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/__tests__/ast.test.d.ts +2 -0
- package/dist/backend/__tests__/ast.test.d.ts.map +1 -0
- package/dist/backend/__tests__/parse.test.d.ts +2 -0
- package/dist/backend/__tests__/parse.test.d.ts.map +1 -0
- package/dist/backend/__tests__/validate.test.d.ts +2 -0
- package/dist/backend/__tests__/validate.test.d.ts.map +1 -0
- package/dist/backend/ast.d.ts +41 -0
- package/dist/backend/ast.d.ts.map +1 -0
- package/dist/backend/ast.js +152 -0
- package/dist/backend/detect-integrations.d.ts +8 -4
- package/dist/backend/detect-integrations.d.ts.map +1 -1
- package/dist/backend/detect-integrations.js +12 -15
- package/dist/backend/handler-declaration.d.ts +17 -0
- package/dist/backend/handler-declaration.d.ts.map +1 -0
- package/dist/backend/handler-declaration.js +83 -0
- package/dist/backend/handlers.d.ts +2 -0
- package/dist/backend/handlers.d.ts.map +1 -1
- package/dist/backend/handlers.js +7 -36
- package/dist/backend/messages.d.ts +10 -0
- package/dist/backend/messages.d.ts.map +1 -0
- package/dist/backend/messages.js +14 -0
- package/dist/backend/parse.d.ts +11 -0
- package/dist/backend/parse.d.ts.map +1 -0
- package/dist/backend/parse.js +66 -0
- package/dist/backend/rules/__tests__/snowflake-static-sql.test.d.ts +2 -0
- package/dist/backend/rules/__tests__/snowflake-static-sql.test.d.ts.map +1 -0
- package/dist/backend/rules/index.d.ts +5 -0
- package/dist/backend/rules/index.d.ts.map +1 -0
- package/dist/backend/rules/index.js +8 -0
- package/dist/backend/rules/snowflake-static-sql.d.ts +15 -0
- package/dist/backend/rules/snowflake-static-sql.d.ts.map +1 -0
- package/dist/backend/rules/snowflake-static-sql.js +75 -0
- package/dist/backend/rules/types.d.ts +29 -0
- package/dist/backend/rules/types.d.ts.map +1 -0
- package/dist/backend/rules/types.js +1 -0
- package/dist/backend/validate.d.ts +23 -0
- package/dist/backend/validate.d.ts.map +1 -0
- package/dist/backend/validate.js +64 -0
- package/dist/commands/__tests__/backend-validate.test.d.ts +2 -0
- package/dist/commands/__tests__/backend-validate.test.d.ts.map +1 -0
- package/dist/commands/backend.d.ts +6 -0
- package/dist/commands/backend.d.ts.map +1 -1
- package/dist/commands/backend.js +22 -2
- package/dist/constants.d.ts +1 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -6
- package/dist/esm/backend/__tests__/ast.test.d.ts +2 -0
- package/dist/esm/backend/__tests__/ast.test.d.ts.map +1 -0
- package/dist/esm/backend/__tests__/parse.test.d.ts +2 -0
- package/dist/esm/backend/__tests__/parse.test.d.ts.map +1 -0
- package/dist/esm/backend/__tests__/validate.test.d.ts +2 -0
- package/dist/esm/backend/__tests__/validate.test.d.ts.map +1 -0
- package/dist/esm/backend/ast.d.ts +41 -0
- package/dist/esm/backend/ast.d.ts.map +1 -0
- package/dist/esm/backend/ast.mjs +146 -0
- package/dist/esm/backend/detect-integrations.d.ts +8 -4
- package/dist/esm/backend/detect-integrations.d.ts.map +1 -1
- package/dist/esm/backend/detect-integrations.mjs +12 -15
- package/dist/esm/backend/handler-declaration.d.ts +17 -0
- package/dist/esm/backend/handler-declaration.d.ts.map +1 -0
- package/dist/esm/backend/handler-declaration.mjs +78 -0
- package/dist/esm/backend/handlers.d.ts +2 -0
- package/dist/esm/backend/handlers.d.ts.map +1 -1
- package/dist/esm/backend/handlers.mjs +7 -36
- package/dist/esm/backend/messages.d.ts +10 -0
- package/dist/esm/backend/messages.d.ts.map +1 -0
- package/dist/esm/backend/messages.mjs +12 -0
- package/dist/esm/backend/parse.d.ts +11 -0
- package/dist/esm/backend/parse.d.ts.map +1 -0
- package/dist/esm/backend/parse.mjs +64 -0
- package/dist/esm/backend/rules/__tests__/snowflake-static-sql.test.d.ts +2 -0
- package/dist/esm/backend/rules/__tests__/snowflake-static-sql.test.d.ts.map +1 -0
- package/dist/esm/backend/rules/index.d.ts +5 -0
- package/dist/esm/backend/rules/index.d.ts.map +1 -0
- package/dist/esm/backend/rules/index.mjs +6 -0
- package/dist/esm/backend/rules/snowflake-static-sql.d.ts +15 -0
- package/dist/esm/backend/rules/snowflake-static-sql.d.ts.map +1 -0
- package/dist/esm/backend/rules/snowflake-static-sql.mjs +73 -0
- package/dist/esm/backend/rules/types.d.ts +29 -0
- package/dist/esm/backend/rules/types.d.ts.map +1 -0
- package/dist/esm/backend/rules/types.mjs +1 -0
- package/dist/esm/backend/validate.d.ts +23 -0
- package/dist/esm/backend/validate.d.ts.map +1 -0
- package/dist/esm/backend/validate.mjs +62 -0
- package/dist/esm/commands/__tests__/backend-validate.test.d.ts +2 -0
- package/dist/esm/commands/__tests__/backend-validate.test.d.ts.map +1 -0
- package/dist/esm/commands/backend.d.ts +6 -0
- package/dist/esm/commands/backend.d.ts.map +1 -1
- package/dist/esm/commands/backend.mjs +23 -4
- package/dist/esm/constants.d.ts +1 -2
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.mjs +3 -6
- package/dist/esm/index.mjs +6 -2
- package/dist/index.js +5 -1
- package/package.json +4 -2
- package/src/backend/__tests__/ast.test.ts +53 -0
- package/src/backend/__tests__/detect-integrations.test.ts +12 -30
- package/src/backend/__tests__/handlers.test.ts +21 -34
- package/src/backend/__tests__/parse.test.ts +25 -0
- package/src/backend/__tests__/validate.test.ts +246 -0
- package/src/backend/ast.ts +204 -0
- package/src/backend/detect-integrations.ts +12 -14
- package/src/backend/handler-declaration.ts +104 -0
- package/src/backend/handlers.ts +9 -44
- package/src/backend/messages.ts +16 -0
- package/src/backend/parse.ts +82 -0
- package/src/backend/rules/__tests__/snowflake-static-sql.test.ts +175 -0
- package/src/backend/rules/index.ts +7 -0
- package/src/backend/rules/snowflake-static-sql.ts +78 -0
- package/src/backend/rules/types.ts +30 -0
- package/src/backend/validate.ts +73 -0
- package/src/commands/__tests__/backend-deploy.test.ts +5 -3
- package/src/commands/__tests__/backend-validate.test.ts +71 -0
- package/src/commands/backend.ts +23 -3
- package/src/constants.ts +5 -6
- package/src/index.ts +9 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.test.d.ts","sourceRoot":"","sources":["../../../src/backend/__tests__/ast.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.test.d.ts","sourceRoot":"","sources":["../../../src/backend/__tests__/parse.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.test.d.ts","sourceRoot":"","sources":["../../../src/backend/__tests__/validate.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,152 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const acornWalk = require('acorn-walk');
|
|
4
|
+
|
|
5
|
+
/** 1-based line a node starts on; requires the program to be parsed with `locations: true`. */
|
|
6
|
+
const lineOf = (node) => node.loc?.start.line;
|
|
7
|
+
/**
|
|
8
|
+
* Every identifier a binding pattern introduces, with its property path from the
|
|
9
|
+
* pattern's value. `path` is deliberately not defaulted: callers pass `[]` for
|
|
10
|
+
* the root and `undefined` once the pattern stops being a plain property read.
|
|
11
|
+
*/
|
|
12
|
+
function patternBindings(pattern, path) {
|
|
13
|
+
switch (pattern.type) {
|
|
14
|
+
case 'Identifier':
|
|
15
|
+
return [{ name: pattern.name, path }];
|
|
16
|
+
case 'ObjectPattern':
|
|
17
|
+
return pattern.properties.flatMap(p => {
|
|
18
|
+
if (p.type === 'RestElement') {
|
|
19
|
+
return patternBindings(p.argument, undefined);
|
|
20
|
+
}
|
|
21
|
+
const key = propertyKey(p.key, p.computed);
|
|
22
|
+
return patternBindings(p.value, path && key !== undefined ? [...path, key] : undefined);
|
|
23
|
+
});
|
|
24
|
+
case 'ArrayPattern':
|
|
25
|
+
return pattern.elements.flatMap(e => (e ? patternBindings(e, undefined) : []));
|
|
26
|
+
case 'AssignmentPattern':
|
|
27
|
+
return patternBindings(pattern.left, undefined);
|
|
28
|
+
case 'RestElement':
|
|
29
|
+
return patternBindings(pattern.argument, undefined);
|
|
30
|
+
default:
|
|
31
|
+
// MemberExpression targets (`obj.x = …`) bind no names.
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function propertyKey(key, computed) {
|
|
36
|
+
if (key.type === 'Literal' && typeof key.value === 'string') {
|
|
37
|
+
return key.value;
|
|
38
|
+
}
|
|
39
|
+
if (!computed && key.type === 'Identifier') {
|
|
40
|
+
return key.name;
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
/** Every identifier a binding pattern introduces (`{ a, b: [c] } = …` → a, c). */
|
|
45
|
+
const patternNames = (pattern) => patternBindings(pattern, []).map(b => b.name);
|
|
46
|
+
function collectBindings(program) {
|
|
47
|
+
const declarations = new Map();
|
|
48
|
+
const unpinnable = new Set();
|
|
49
|
+
const markUnpinnable = (pattern) => patternNames(pattern).forEach(n => unpinnable.add(n));
|
|
50
|
+
acornWalk.full(program, node => {
|
|
51
|
+
switch (node.type) {
|
|
52
|
+
case 'VariableDeclaration':
|
|
53
|
+
for (const declarator of node.declarations) {
|
|
54
|
+
for (const { name, path } of patternBindings(declarator.id, [])) {
|
|
55
|
+
const list = declarations.get(name) ?? [];
|
|
56
|
+
list.push({ kind: node.kind, init: declarator.init, path });
|
|
57
|
+
declarations.set(name, list);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
case 'AssignmentExpression':
|
|
62
|
+
markUnpinnable(node.left);
|
|
63
|
+
break;
|
|
64
|
+
case 'UpdateExpression':
|
|
65
|
+
if (node.argument.type === 'Identifier') {
|
|
66
|
+
unpinnable.add(node.argument.name);
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
case 'ForInStatement':
|
|
70
|
+
case 'ForOfStatement':
|
|
71
|
+
if (node.left.type !== 'VariableDeclaration') {
|
|
72
|
+
markUnpinnable(node.left);
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
case 'FunctionDeclaration':
|
|
76
|
+
case 'FunctionExpression':
|
|
77
|
+
case 'ArrowFunctionExpression':
|
|
78
|
+
node.params.forEach(markUnpinnable);
|
|
79
|
+
break;
|
|
80
|
+
case 'CatchClause':
|
|
81
|
+
if (node.param) {
|
|
82
|
+
markUnpinnable(node.param);
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return { declarations, unpinnable };
|
|
88
|
+
}
|
|
89
|
+
/** `a.b.c` / `a['b'].c` → root `a`, parts `[b, c]`; `undefined` if any link is dynamic or the root is not an identifier. */
|
|
90
|
+
function memberChain(node) {
|
|
91
|
+
if (node.type === 'ChainExpression') {
|
|
92
|
+
return memberChain(node.expression);
|
|
93
|
+
}
|
|
94
|
+
const parts = [];
|
|
95
|
+
let cur = node;
|
|
96
|
+
while (cur.type === 'MemberExpression') {
|
|
97
|
+
const key = propertyKey(cur.property, cur.computed);
|
|
98
|
+
if (key === undefined) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
parts.unshift(key);
|
|
102
|
+
cur = cur.object;
|
|
103
|
+
}
|
|
104
|
+
return cur.type === 'Identifier' ? { root: cur.name, parts } : undefined;
|
|
105
|
+
}
|
|
106
|
+
function createPathResolver(bindings, contextParam) {
|
|
107
|
+
function resolveChain(node, resolving) {
|
|
108
|
+
const chain = memberChain(node);
|
|
109
|
+
if (!chain) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
return [resolveRoot(chain.root, resolving), ...chain.parts].join('.');
|
|
113
|
+
}
|
|
114
|
+
function resolveRoot(name, resolving) {
|
|
115
|
+
if (contextParam !== undefined && name === contextParam) {
|
|
116
|
+
return 'ctx';
|
|
117
|
+
}
|
|
118
|
+
if (bindings.unpinnable.has(name) || resolving.has(name)) {
|
|
119
|
+
return name;
|
|
120
|
+
}
|
|
121
|
+
const decls = bindings.declarations.get(name);
|
|
122
|
+
if (!decls || decls.length !== 1) {
|
|
123
|
+
return name;
|
|
124
|
+
}
|
|
125
|
+
const [{ kind, init, path }] = decls;
|
|
126
|
+
if (kind !== 'const' || !init || !path) {
|
|
127
|
+
return name;
|
|
128
|
+
}
|
|
129
|
+
const base = resolveChain(init, new Set(resolving).add(name));
|
|
130
|
+
return base ? [base, ...path].join('.') : name;
|
|
131
|
+
}
|
|
132
|
+
return { pathOf: node => resolveChain(node, new Set()) };
|
|
133
|
+
}
|
|
134
|
+
/** Every `ctx.api.v1.<domain>` the program touches (known or not), through `paths`. */
|
|
135
|
+
function collectApiDomains(program, paths) {
|
|
136
|
+
const apiDomains = new Set();
|
|
137
|
+
acornWalk.simple(program, {
|
|
138
|
+
MemberExpression(node) {
|
|
139
|
+
const [obj, api, version, domain] = paths.pathOf(node)?.split('.') ?? [];
|
|
140
|
+
if (obj === 'ctx' && api === 'api' && version === 'v1' && domain) {
|
|
141
|
+
apiDomains.add(domain);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
return apiDomains;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
exports.collectApiDomains = collectApiDomains;
|
|
149
|
+
exports.collectBindings = collectBindings;
|
|
150
|
+
exports.createPathResolver = createPathResolver;
|
|
151
|
+
exports.lineOf = lineOf;
|
|
152
|
+
exports.patternNames = patternNames;
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import { type DeclaredIntegration } from '@mondaydotcomorg/z2h-shared-utils';
|
|
1
2
|
/**
|
|
2
|
-
* Integrations inferred from handler
|
|
3
|
+
* Integrations inferred from handler sources at deploy time — whatever
|
|
3
4
|
* `ctx.api.v1.<name>` the handlers touch (from `DECLARED_INTEGRATIONS`) is
|
|
4
5
|
* registered with the platform. There is no `integrations.json` and no declare
|
|
5
6
|
* CLI step.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* `apiDomains` comes from the handler parse in `validateHandlerSource`, so a
|
|
9
|
+
* domain mentioned only in a comment or string does not count as usage.
|
|
10
|
+
*
|
|
11
|
+
* Only OAuth-gated integrations belong in the result. `db`, `llm`, and
|
|
12
|
+
* `snowflake` are always-on channels and are never declared.
|
|
9
13
|
*/
|
|
10
|
-
export declare function
|
|
14
|
+
export declare function detectIntegrations(apiDomains: Iterable<string>): DeclaredIntegration[];
|
|
11
15
|
//# sourceMappingURL=detect-integrations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-integrations.d.ts","sourceRoot":"","sources":["../../src/backend/detect-integrations.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"detect-integrations.d.ts","sourceRoot":"","sources":["../../src/backend/detect-integrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAKpG;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,mBAAmB,EAAE,CAEtF"}
|
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const z2hSharedUtils = require('@mondaydotcomorg/z2h-shared-utils');
|
|
4
4
|
|
|
5
|
+
const declared = new Set(z2hSharedUtils.DECLARED_INTEGRATIONS);
|
|
6
|
+
const isDeclaredIntegration = (domain) => declared.has(domain);
|
|
5
7
|
/**
|
|
6
|
-
* Integrations inferred from handler
|
|
8
|
+
* Integrations inferred from handler sources at deploy time — whatever
|
|
7
9
|
* `ctx.api.v1.<name>` the handlers touch (from `DECLARED_INTEGRATIONS`) is
|
|
8
10
|
* registered with the platform. There is no `integrations.json` and no declare
|
|
9
11
|
* CLI step.
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
+
* `apiDomains` comes from the handler parse in `validateHandlerSource`, so a
|
|
14
|
+
* domain mentioned only in a comment or string does not count as usage.
|
|
15
|
+
*
|
|
16
|
+
* Only OAuth-gated integrations belong in the result. `db`, `llm`, and
|
|
17
|
+
* `snowflake` are always-on channels and are never declared.
|
|
13
18
|
*/
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
for (const source of Object.values(handlers)) {
|
|
17
|
-
for (const name of constants.DECLARED_INTEGRATIONS) {
|
|
18
|
-
if (new RegExp(`\\bctx\\.api\\.v1\\.${name}\\b`).test(source)) {
|
|
19
|
-
found.add(name);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return [...found].sort();
|
|
19
|
+
function detectIntegrations(apiDomains) {
|
|
20
|
+
return [...new Set(apiDomains)].filter(isDeclaredIntegration).sort();
|
|
24
21
|
}
|
|
25
22
|
|
|
26
|
-
exports.
|
|
23
|
+
exports.detectIntegrations = detectIntegrations;
|
|
@@ -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,83 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
/** True for the `handler` identifier specifically — used everywhere a node might name the binding. */
|
|
4
|
+
const isHandlerIdentifier = (candidate) => candidate?.type === 'Identifier' && candidate.name === 'handler';
|
|
5
|
+
/** The top-level declaration that binds `handler`, if any. */
|
|
6
|
+
function findHandlerDeclaration(body) {
|
|
7
|
+
for (const node of body) {
|
|
8
|
+
if (node.type === 'FunctionDeclaration' && isHandlerIdentifier(node.id)) {
|
|
9
|
+
return { kind: 'function', params: node.params };
|
|
10
|
+
}
|
|
11
|
+
if (node.type === 'VariableDeclaration') {
|
|
12
|
+
const declarator = node.declarations.find(d => isHandlerIdentifier(d.id));
|
|
13
|
+
if (declarator) {
|
|
14
|
+
return { kind: 'variable', init: declarator.init };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
/** `export function handler`, `export const handler`, `export { handler }`, `export default handler`. */
|
|
21
|
+
function exportsHandler(node) {
|
|
22
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
23
|
+
return ((node.declaration != null && findHandlerDeclaration([node.declaration]) !== undefined) ||
|
|
24
|
+
node.specifiers.some(s => isHandlerIdentifier(s.local) || isHandlerIdentifier(s.exported)));
|
|
25
|
+
}
|
|
26
|
+
if (node.type === 'ExportDefaultDeclaration') {
|
|
27
|
+
const { declaration } = node;
|
|
28
|
+
return ((declaration.type === 'FunctionDeclaration' && isHandlerIdentifier(declaration.id)) ||
|
|
29
|
+
isHandlerIdentifier(declaration));
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
/** Peel `wrap(fn)`, `(0, fn)`, and the like until a function expression remains. */
|
|
34
|
+
function asFunction(expr) {
|
|
35
|
+
if (!expr) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
switch (expr.type) {
|
|
39
|
+
case 'FunctionExpression':
|
|
40
|
+
case 'ArrowFunctionExpression':
|
|
41
|
+
return expr;
|
|
42
|
+
case 'SequenceExpression':
|
|
43
|
+
return asFunction(expr.expressions.at(-1));
|
|
44
|
+
case 'CallExpression': {
|
|
45
|
+
for (const arg of expr.arguments) {
|
|
46
|
+
if (arg.type === 'SpreadElement') {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const fn = asFunction(arg);
|
|
50
|
+
if (fn) {
|
|
51
|
+
return fn;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
default:
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function firstParamName(params) {
|
|
61
|
+
let first = params[0];
|
|
62
|
+
if (first?.type === 'AssignmentPattern') {
|
|
63
|
+
first = first.left;
|
|
64
|
+
}
|
|
65
|
+
return first?.type === 'Identifier' ? first.name : undefined;
|
|
66
|
+
}
|
|
67
|
+
/** First positional parameter of `handler`, when it's a plain identifier (`ctx`, `context`, …). */
|
|
68
|
+
function handlerContextParamName(declaration) {
|
|
69
|
+
const params = declaration.kind === 'function' ? declaration.params : asFunction(declaration.init)?.params;
|
|
70
|
+
return params ? firstParamName(params) : undefined;
|
|
71
|
+
}
|
|
72
|
+
/** Values that can never be a function, so `const handler = <this>` is caught at build time. */
|
|
73
|
+
const NEVER_A_FUNCTION = new Set([
|
|
74
|
+
'Literal',
|
|
75
|
+
'ObjectExpression',
|
|
76
|
+
'ArrayExpression',
|
|
77
|
+
'TemplateLiteral',
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
exports.NEVER_A_FUNCTION = NEVER_A_FUNCTION;
|
|
81
|
+
exports.exportsHandler = exportsHandler;
|
|
82
|
+
exports.findHandlerDeclaration = findHandlerDeclaration;
|
|
83
|
+
exports.handlerContextParamName = handlerContextParamName;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export interface ReadHandlersResult {
|
|
2
2
|
/** handler name (filename without .js) → JS source */
|
|
3
3
|
handlers: Record<string, string>;
|
|
4
|
+
/** Every `ctx.api.v1.<domain>` any handler touches, sorted and unique. */
|
|
5
|
+
apiDomains: string[];
|
|
4
6
|
}
|
|
5
7
|
/**
|
|
6
8
|
* Reads every `backend/handlers/*.js` file inside `consumerDir` and returns a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/backend/handlers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/backend/handlers.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,0EAA0E;IAC1E,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA2BnF"}
|
package/dist/backend/handlers.js
CHANGED
|
@@ -3,35 +3,13 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
const promises = require('node:fs/promises');
|
|
5
5
|
const fs = require('fs-extra');
|
|
6
|
+
const backend_validate = require('./validate.js');
|
|
6
7
|
|
|
7
8
|
const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
8
9
|
|
|
9
10
|
const path__default = /*#__PURE__*/_interopDefault(path);
|
|
10
11
|
|
|
11
12
|
const HANDLERS_DIR = path__default.default.join('backend', 'handlers');
|
|
12
|
-
/**
|
|
13
|
-
* True if the source defines a `handler` function or variable at the top level.
|
|
14
|
-
* Handlers run in a V8 isolate with no module system — `handler` must be a plain
|
|
15
|
-
* global, not an export.
|
|
16
|
-
*/
|
|
17
|
-
function definesHandler(source) {
|
|
18
|
-
return /\bfunction\s+handler\b/.test(source) || /\b(const|let|var)\s+handler\b/.test(source);
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* True if `handler` is declared with an `export` prefix. It reads as valid source but
|
|
22
|
-
* the isolate has no module system, so an exported handler is simply invisible to the
|
|
23
|
-
* runner — caught here rather than failing opaquely at invoke time.
|
|
24
|
-
*/
|
|
25
|
-
function exportsHandler(source) {
|
|
26
|
-
return /\bexport\s+(default\s+)?(async\s+)?(function\s+handler\b|(?:const|let|var)\s+handler\b)/.test(source);
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* True if the source contains any `import` or `require` call. Neither works in
|
|
30
|
-
* the V8 isolate — reach the outside world through `ctx` capabilities instead.
|
|
31
|
-
*/
|
|
32
|
-
function hasImports(source) {
|
|
33
|
-
return /^\s*import\b/m.test(source) || /\brequire\s*\(/.test(source);
|
|
34
|
-
}
|
|
35
13
|
/**
|
|
36
14
|
* Reads every `backend/handlers/*.js` file inside `consumerDir` and returns a
|
|
37
15
|
* map from handler name (filename without `.js`) to source. No compilation —
|
|
@@ -40,7 +18,7 @@ function hasImports(source) {
|
|
|
40
18
|
async function readHandlers(consumerDir) {
|
|
41
19
|
const handlersDir = path__default.default.join(consumerDir, HANDLERS_DIR);
|
|
42
20
|
if (!(await fs.pathExists(handlersDir))) {
|
|
43
|
-
return { handlers: {} };
|
|
21
|
+
return { handlers: {}, apiDomains: [] };
|
|
44
22
|
}
|
|
45
23
|
const entries = await promises.readdir(handlersDir);
|
|
46
24
|
const tsFiles = entries.filter(f => f.endsWith('.ts'));
|
|
@@ -49,25 +27,18 @@ async function readHandlers(consumerDir) {
|
|
|
49
27
|
}
|
|
50
28
|
const jsFiles = entries.filter(f => f.endsWith('.js'));
|
|
51
29
|
if (jsFiles.length === 0) {
|
|
52
|
-
return { handlers: {} };
|
|
30
|
+
return { handlers: {}, apiDomains: [] };
|
|
53
31
|
}
|
|
54
32
|
const handlers = {};
|
|
33
|
+
const apiDomains = new Set();
|
|
55
34
|
for (const file of jsFiles) {
|
|
56
35
|
const handlerName = path__default.default.parse(file).name;
|
|
57
36
|
const source = await promises.readFile(path__default.default.join(handlersDir, file), 'utf8');
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
throw new Error(`${file} must not export handler — the isolate has no module system; declare it as a plain global, e.g. async function handler(ctx) { ... }`);
|
|
61
|
-
}
|
|
62
|
-
if (!definesHandler(source)) {
|
|
63
|
-
throw new Error(`${file} must define a handler — e.g. async function handler(ctx) { ... }`);
|
|
64
|
-
}
|
|
65
|
-
if (hasImports(source)) {
|
|
66
|
-
throw new Error(`${file} must not import other files or packages — handlers are self-contained; reach the outside world through ctx capabilities`);
|
|
67
|
-
}
|
|
37
|
+
const validation = backend_validate.validateHandlerSource(file, source);
|
|
38
|
+
validation.apiDomains.forEach(d => apiDomains.add(d));
|
|
68
39
|
handlers[handlerName] = source;
|
|
69
40
|
}
|
|
70
|
-
return { handlers };
|
|
41
|
+
return { handlers, apiDomains: [...apiDomains].sort() };
|
|
71
42
|
}
|
|
72
43
|
|
|
73
44
|
exports.readHandlers = readHandlers;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const messages: {
|
|
2
|
+
mustDefine: (file: string) => string;
|
|
3
|
+
notAFunction: (file: string) => string;
|
|
4
|
+
mustNotImport: (file: string) => string;
|
|
5
|
+
mustNotExportHandler: (file: string) => string;
|
|
6
|
+
mustNotExport: (file: string) => string;
|
|
7
|
+
moduleOnlySyntax: (file: string, detail: string) => string;
|
|
8
|
+
syntaxError: (file: string, line: number, column: number, detail: string) => string;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/backend/messages.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ;uBACA,MAAM;yBACJ,MAAM;0BACL,MAAM;iCAEC,MAAM;0BAEb,MAAM;6BAEH,MAAM,UAAU,MAAM;wBAE3B,MAAM,QAAQ,MAAM,UAAU,MAAM,UAAU,MAAM;CAEzE,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const EXAMPLE = 'async function handler(ctx) { ... }';
|
|
4
|
+
const messages = {
|
|
5
|
+
mustDefine: (file) => `${file} must define a handler — e.g. ${EXAMPLE}`,
|
|
6
|
+
notAFunction: (file) => `${file} declares handler but it is not a function — e.g. ${EXAMPLE}`,
|
|
7
|
+
mustNotImport: (file) => `${file} must not import other files or packages — handlers are self-contained; reach the outside world through ctx capabilities`,
|
|
8
|
+
mustNotExportHandler: (file) => `${file} must not export handler — the isolate has no module system; declare it as a plain global, e.g. ${EXAMPLE}`,
|
|
9
|
+
mustNotExport: (file) => `${file} must not use export — the isolate has no module system; declare handler as a plain global, e.g. ${EXAMPLE}`,
|
|
10
|
+
moduleOnlySyntax: (file, detail) => `${file} uses module-only syntax (${detail}) — handlers run as plain scripts, so top-level await and import.meta are not available`,
|
|
11
|
+
syntaxError: (file, line, column, detail) => `${file}:${line}:${column}: not valid JavaScript — ${detail}`,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
exports.messages = messages;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Program } from 'acorn';
|
|
2
|
+
/**
|
|
3
|
+
* The runner compiles handlers with `isolate.compileScript`, i.e. the classic
|
|
4
|
+
* *script* grammar — so that is the grammar we validate against. `import`/`export`
|
|
5
|
+
* declarations and top-level `await`/`import.meta` are only legal under the
|
|
6
|
+
* *module* grammar, so a handler using any of them fails the script parse; we
|
|
7
|
+
* re-parse it as a module purely to name which of those four things it was,
|
|
8
|
+
* instead of surfacing V8's bare "Unexpected token 'export'".
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseOrThrow(file: string, code: string): Program;
|
|
11
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/backend/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8D,KAAK,OAAO,EAAkB,MAAM,OAAO,CAAC;AAkDjH;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAuBhE"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const acorn = require('acorn');
|
|
4
|
+
const zod = require('zod');
|
|
5
|
+
const util_tracker = require('../util/tracker.js');
|
|
6
|
+
const backend_handlerDeclaration = require('./handler-declaration.js');
|
|
7
|
+
const backend_messages = require('./messages.js');
|
|
8
|
+
|
|
9
|
+
const acornLocSchema = zod.z.object({ line: zod.z.number(), column: zod.z.number() });
|
|
10
|
+
function isAcornSyntaxError(err) {
|
|
11
|
+
return err instanceof SyntaxError && acornLocSchema.safeParse(err.loc).success;
|
|
12
|
+
}
|
|
13
|
+
function tryParse(code, sourceType) {
|
|
14
|
+
try {
|
|
15
|
+
return { ok: true, program: acorn.parse(code, { ecmaVersion: 'latest', sourceType, locations: true }) };
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
if (isAcornSyntaxError(err)) {
|
|
19
|
+
return { ok: false, error: err };
|
|
20
|
+
}
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Acorn appends ` (line:col)` to its messages; we report the position ourselves. */
|
|
25
|
+
const stripPosition = (message) => message.replace(/ \(\d+:\d+\)$/, '');
|
|
26
|
+
const isImportLike = (node) => node.type === 'ImportDeclaration' ||
|
|
27
|
+
node.type === 'ExportAllDeclaration' ||
|
|
28
|
+
(node.type === 'ExportNamedDeclaration' && node.source != null);
|
|
29
|
+
const isExport = (node) => node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration';
|
|
30
|
+
/** Reports the failure, then returns the error for the caller to throw. */
|
|
31
|
+
function parseError(message) {
|
|
32
|
+
void util_tracker.trackEvent('z2h_cli_handler_parse_error', { error: message });
|
|
33
|
+
return new Error(message);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The runner compiles handlers with `isolate.compileScript`, i.e. the classic
|
|
37
|
+
* *script* grammar — so that is the grammar we validate against. `import`/`export`
|
|
38
|
+
* declarations and top-level `await`/`import.meta` are only legal under the
|
|
39
|
+
* *module* grammar, so a handler using any of them fails the script parse; we
|
|
40
|
+
* re-parse it as a module purely to name which of those four things it was,
|
|
41
|
+
* instead of surfacing V8's bare "Unexpected token 'export'".
|
|
42
|
+
*/
|
|
43
|
+
function parseOrThrow(file, code) {
|
|
44
|
+
const asScript = tryParse(code, 'script');
|
|
45
|
+
if (asScript.ok) {
|
|
46
|
+
return asScript.program;
|
|
47
|
+
}
|
|
48
|
+
const asModule = tryParse(code, 'module');
|
|
49
|
+
if (asModule.ok) {
|
|
50
|
+
const { body } = asModule.program;
|
|
51
|
+
if (body.some(backend_handlerDeclaration.exportsHandler)) {
|
|
52
|
+
throw parseError(backend_messages.messages.mustNotExportHandler(file));
|
|
53
|
+
}
|
|
54
|
+
if (body.some(isImportLike)) {
|
|
55
|
+
throw parseError(backend_messages.messages.mustNotImport(file));
|
|
56
|
+
}
|
|
57
|
+
if (body.some(isExport)) {
|
|
58
|
+
throw parseError(backend_messages.messages.mustNotExport(file));
|
|
59
|
+
}
|
|
60
|
+
throw parseError(backend_messages.messages.moduleOnlySyntax(file, stripPosition(asScript.error.message)));
|
|
61
|
+
}
|
|
62
|
+
const { line, column } = asScript.error.loc;
|
|
63
|
+
throw parseError(backend_messages.messages.syntaxError(file, line, column, stripPosition(asScript.error.message)));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
exports.parseOrThrow = parseOrThrow;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snowflake-static-sql.test.d.ts","sourceRoot":"","sources":["../../../../src/backend/rules/__tests__/snowflake-static-sql.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { HandlerRule } from './types';
|
|
2
|
+
export type { HandlerRule, RuleContext } from './types';
|
|
3
|
+
/** Every rule the validator runs, in order. Add new capability rules here. */
|
|
4
|
+
export declare const HANDLER_RULES: readonly HandlerRule[];
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/backend/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG3C,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAExD,8EAA8E;AAC9E,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAAmC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const backend_rules_snowflakeStaticSql = require('./snowflake-static-sql.js');
|
|
4
|
+
|
|
5
|
+
/** Every rule the validator runs, in order. Add new capability rules here. */
|
|
6
|
+
const HANDLER_RULES = [new backend_rules_snowflakeStaticSql.SnowflakeStaticSqlRule()];
|
|
7
|
+
|
|
8
|
+
exports.HANDLER_RULES = HANDLER_RULES;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HandlerRule, RuleContext } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* `ctx.api.v1.snowflake.query(sql, params?, options?)` — the SQL text must live
|
|
4
|
+
* in the handler. The frontend chooses *which* handler to call and supplies
|
|
5
|
+
* `params` / `options`; it never ships SQL. Binding placeholders (`:name`) are
|
|
6
|
+
* how client values reach the query.
|
|
7
|
+
*
|
|
8
|
+
* The call is matched through `const` aliases of the channel
|
|
9
|
+
* (`const sf = ctx.api.v1.snowflake; sf.query(…)`), see `PathResolver`.
|
|
10
|
+
*/
|
|
11
|
+
export declare class SnowflakeStaticSqlRule implements HandlerRule {
|
|
12
|
+
readonly name = "snowflake/static-sql";
|
|
13
|
+
check({ file, program, bindings, paths }: RuleContext): string[];
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=snowflake-static-sql.d.ts.map
|