busa-sdk 0.98.0 → 0.99.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/airapp-check.d.ts +67 -1
- package/dist/airapp-check.js +167 -4
- package/dist/airapp-gate.d.ts +1 -1
- package/dist/airapp-node-D2GcMwde.d.ts +152 -0
- package/dist/airapp-node.d.ts +1 -151
- package/dist/airapp.d.ts +305 -2
- package/dist/airapp.js +319 -2
- package/dist/{client-DtzuVlPK.d.ts → client-BUzSZV3L.d.ts} +26326 -26234
- package/dist/index.d.ts +2 -2
- package/dist/index.js +69 -6
- package/dist/{template-CMJMkyse.js → template-Bnkq4diD.js} +34 -6
- package/package.json +1 -1
package/dist/airapp-check.d.ts
CHANGED
|
@@ -74,6 +74,21 @@ interface AirAppSources {
|
|
|
74
74
|
config?: string;
|
|
75
75
|
/** The slug the package ships this app under, for the `resourceKey` rule. */
|
|
76
76
|
shippedSlug?: string;
|
|
77
|
+
/**
|
|
78
|
+
* The app's files, one entry each, paths relative to the app's root — for the
|
|
79
|
+
* rules that report a file and line (`airapp/forced-review`).
|
|
80
|
+
*
|
|
81
|
+
* Pass every file; the rule picks its own corpus with
|
|
82
|
+
* {@link isAirAppWriteRuleSource} (script sources only, never `vendor/`,
|
|
83
|
+
* `node_modules/`, tests, or `scripts/`), so a caller cannot widen it by
|
|
84
|
+
* accident. Omitted, the rule falls back to `browserLogic` + `server`, which
|
|
85
|
+
* still covers the provider and the host but can only quote the offending
|
|
86
|
+
* line, not name its file.
|
|
87
|
+
*/
|
|
88
|
+
appSources?: readonly {
|
|
89
|
+
path: string;
|
|
90
|
+
content: string;
|
|
91
|
+
}[];
|
|
77
92
|
}
|
|
78
93
|
/**
|
|
79
94
|
* The documented interactive page budget. A higher one may be justified — say so in
|
|
@@ -91,6 +106,57 @@ declare const AIRAPP_DEFAULT_READ_LIMIT = 50;
|
|
|
91
106
|
* satisfy the rule.
|
|
92
107
|
*/
|
|
93
108
|
declare const stripComments: (source: string) => string;
|
|
109
|
+
/**
|
|
110
|
+
* The source with every comment, string, template-literal text, and regex
|
|
111
|
+
* literal blanked to spaces — newlines kept, so an offset still maps to its line.
|
|
112
|
+
*
|
|
113
|
+
* Stricter than {@link stripComments}, and for a rule that matches an
|
|
114
|
+
* identifier that has to be: the prose and messages explaining `autoMerge` name
|
|
115
|
+
* it constantly (`"pass autoMerge: false to force review"`), and a rule that
|
|
116
|
+
* fires on its own explanation stops being read. Template-literal
|
|
117
|
+
* *interpolations* stay code — `${autoMerge}` is a real use.
|
|
118
|
+
*/
|
|
119
|
+
declare const codeOnly: (source: string) => string;
|
|
120
|
+
/** The reason an `allow-forced-review` comment has to give, at minimum, in characters. */
|
|
121
|
+
declare const AIRAPP_FORCED_REVIEW_MIN_REASON = 20;
|
|
122
|
+
/** Where `autoMerge` was set to something other than literal `true`. */
|
|
123
|
+
interface AirAppForcedReview {
|
|
124
|
+
/** 1-based. */
|
|
125
|
+
line: number;
|
|
126
|
+
/** The offending line, trimmed. */
|
|
127
|
+
text: string;
|
|
128
|
+
problem: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Every `autoMerge` in `source`'s code that is not a literal `true`, with the
|
|
132
|
+
* line it is on — an explicit `false`, a value worked out at runtime
|
|
133
|
+
* (`autoMerge: isStandaloneLocalRuntime()`), or a variable passed through
|
|
134
|
+
* (`{ autoMerge }`). Comments, strings, template text, and regexes never match.
|
|
135
|
+
*
|
|
136
|
+
* Why it is a rule: a person's own write follows their permission. The app
|
|
137
|
+
* leaves `autoMerge` unset; Busabase merges it for someone with write access and
|
|
138
|
+
* opens a ChangeRequest for someone without. An app that forces review makes
|
|
139
|
+
* every owner approve their own click in the inbox — a fix that had to be
|
|
140
|
+
* applied to 49 shipped copies by hand.
|
|
141
|
+
*
|
|
142
|
+
* Forcing review is still right when the write is something outgoing a second
|
|
143
|
+
* person must approve before it is sent. Say so on the line, or the line above,
|
|
144
|
+
* and the rule lets it through:
|
|
145
|
+
*
|
|
146
|
+
* ```js
|
|
147
|
+
* autoMerge: false, // allow-forced-review: drafts an outgoing contract that legal must approve
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* The reason must be at least {@link AIRAPP_FORCED_REVIEW_MIN_REASON} characters.
|
|
151
|
+
*/
|
|
152
|
+
declare const findForcedReview: (source: string) => AirAppForcedReview[];
|
|
153
|
+
/**
|
|
154
|
+
* Whether `path` (relative to the app's root, `/`-separated) belongs to the
|
|
155
|
+
* corpus `airapp/forced-review` scans: a script source of the app's own, not a
|
|
156
|
+
* vendored bundle, a test, or a dev script. The same corpus as
|
|
157
|
+
* `scripts/check-app-writes.mjs` in busabase/templates.
|
|
158
|
+
*/
|
|
159
|
+
declare const isAirAppWriteRuleSource: (path: string) => boolean;
|
|
94
160
|
/**
|
|
95
161
|
* Objects inside a `bases: [ ... ]` literal, brace-matched rather than regexed across
|
|
96
162
|
* the array, so a nested `fields:` entry is never mistaken for a Base.
|
|
@@ -114,4 +180,4 @@ declare const scanAirAppConfig: (source: string) => {
|
|
|
114
180
|
*/
|
|
115
181
|
declare const checkAirApp: (sources: AirAppSources) => AirAppFinding[];
|
|
116
182
|
//#endregion
|
|
117
|
-
export { AIRAPP_DEFAULT_READ_LIMIT, AirAppFinding, AirAppFindingSeverity, AirAppSources, checkAirApp, scanAirAppConfig, stripComments };
|
|
183
|
+
export { AIRAPP_DEFAULT_READ_LIMIT, AIRAPP_FORCED_REVIEW_MIN_REASON, AirAppFinding, AirAppFindingSeverity, AirAppForcedReview, AirAppSources, checkAirApp, codeOnly, findForcedReview, isAirAppWriteRuleSource, scanAirAppConfig, stripComments };
|
package/dist/airapp-check.js
CHANGED
|
@@ -14,6 +14,10 @@ const FORBIDDEN_DEPENDENCIES = [
|
|
|
14
14
|
"webpack",
|
|
15
15
|
"parcel"
|
|
16
16
|
];
|
|
17
|
+
/** Code that branches on where the app is running. */
|
|
18
|
+
const RUNTIME_BRANCH = /createAirAppConnectGate|getRuntime\s*\(|detectAirAppRuntime\s*\(|shouldUseLocalAirAppGateway|\bhosted\b/;
|
|
19
|
+
/** The SDK's browser probe of `__airapp/runtime` (`busabase-sdk/airapp`). */
|
|
20
|
+
const SDK_RUNTIME_PROBE = /\bdetectAirAppRuntime\s*\(/;
|
|
17
21
|
/**
|
|
18
22
|
* The documented interactive page budget. A higher one may be justified — say so in
|
|
19
23
|
* review — so this is a warning rather than a hard bound. A generator checking its
|
|
@@ -30,6 +34,163 @@ const AIRAPP_DEFAULT_READ_LIMIT = 50;
|
|
|
30
34
|
* satisfy the rule.
|
|
31
35
|
*/
|
|
32
36
|
const stripComments = (source) => source.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:'"`\\])\/\/[^\n]*/g, "$1 ");
|
|
37
|
+
const REGEX_PREFIX = /[(,=:[!&|?{};+\-*%<>~^]$/;
|
|
38
|
+
const REGEX_KEYWORD = /\b(?:return|typeof|instanceof|in|of|new|delete|void|throw|case|do|else|yield|await)$/;
|
|
39
|
+
/**
|
|
40
|
+
* The source with every comment, string, template-literal text, and regex
|
|
41
|
+
* literal blanked to spaces — newlines kept, so an offset still maps to its line.
|
|
42
|
+
*
|
|
43
|
+
* Stricter than {@link stripComments}, and for a rule that matches an
|
|
44
|
+
* identifier that has to be: the prose and messages explaining `autoMerge` name
|
|
45
|
+
* it constantly (`"pass autoMerge: false to force review"`), and a rule that
|
|
46
|
+
* fires on its own explanation stops being read. Template-literal
|
|
47
|
+
* *interpolations* stay code — `${autoMerge}` is a real use.
|
|
48
|
+
*/
|
|
49
|
+
const codeOnly = (source) => {
|
|
50
|
+
const out = source.split("");
|
|
51
|
+
const blank = (from, to) => {
|
|
52
|
+
for (let index = from; index < to; index += 1) if (out[index] !== "\n") out[index] = " ";
|
|
53
|
+
};
|
|
54
|
+
const templateDepth = [];
|
|
55
|
+
let index = 0;
|
|
56
|
+
while (index < source.length) {
|
|
57
|
+
const char = source[index];
|
|
58
|
+
const next = source[index + 1];
|
|
59
|
+
if (char === "/" && next === "/") {
|
|
60
|
+
const end = source.indexOf("\n", index);
|
|
61
|
+
const stop = end === -1 ? source.length : end;
|
|
62
|
+
blank(index, stop);
|
|
63
|
+
index = stop;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (char === "/" && next === "*") {
|
|
67
|
+
const end = source.indexOf("*/", index + 2);
|
|
68
|
+
const stop = end === -1 ? source.length : end + 2;
|
|
69
|
+
blank(index, stop);
|
|
70
|
+
index = stop;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (char === "\"" || char === "'") {
|
|
74
|
+
let cursor = index + 1;
|
|
75
|
+
while (cursor < source.length && source[cursor] !== char && source[cursor] !== "\n") cursor += source[cursor] === "\\" ? 2 : 1;
|
|
76
|
+
blank(index + 1, cursor);
|
|
77
|
+
index = cursor + 1;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (char === "`" || char === "}" && templateDepth.at(-1) === 0) {
|
|
81
|
+
if (char === "}") templateDepth.pop();
|
|
82
|
+
let cursor = index + 1;
|
|
83
|
+
while (cursor < source.length && source[cursor] !== "`") {
|
|
84
|
+
if (source[cursor] === "\\") {
|
|
85
|
+
cursor += 2;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (source[cursor] === "$" && source[cursor + 1] === "{") break;
|
|
89
|
+
cursor += 1;
|
|
90
|
+
}
|
|
91
|
+
blank(index + 1, cursor);
|
|
92
|
+
if (source[cursor] === "$") {
|
|
93
|
+
templateDepth.push(0);
|
|
94
|
+
index = cursor + 2;
|
|
95
|
+
} else index = cursor + 1;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (char === "/") {
|
|
99
|
+
const before = source.slice(Math.max(0, index - 12), index).trimEnd();
|
|
100
|
+
if (!before || REGEX_PREFIX.test(before) || REGEX_KEYWORD.test(before)) {
|
|
101
|
+
let cursor = index + 1;
|
|
102
|
+
let inClass = false;
|
|
103
|
+
while (cursor < source.length && source[cursor] !== "\n") {
|
|
104
|
+
const current = source[cursor];
|
|
105
|
+
if (current === "\\") {
|
|
106
|
+
cursor += 2;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (current === "[") inClass = true;
|
|
110
|
+
else if (current === "]") inClass = false;
|
|
111
|
+
else if (current === "/" && !inClass) break;
|
|
112
|
+
cursor += 1;
|
|
113
|
+
}
|
|
114
|
+
blank(index + 1, cursor);
|
|
115
|
+
index = cursor + 1;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (templateDepth.length) {
|
|
120
|
+
if (char === "{") templateDepth[templateDepth.length - 1] += 1;
|
|
121
|
+
else if (char === "}") templateDepth[templateDepth.length - 1] -= 1;
|
|
122
|
+
}
|
|
123
|
+
index += 1;
|
|
124
|
+
}
|
|
125
|
+
return out.join("");
|
|
126
|
+
};
|
|
127
|
+
/** The reason an `allow-forced-review` comment has to give, at minimum, in characters. */
|
|
128
|
+
const AIRAPP_FORCED_REVIEW_MIN_REASON = 20;
|
|
129
|
+
const ALLOW_FORCED_REVIEW = /allow-forced-review:\s*(.*)$/;
|
|
130
|
+
/**
|
|
131
|
+
* Every `autoMerge` in `source`'s code that is not a literal `true`, with the
|
|
132
|
+
* line it is on — an explicit `false`, a value worked out at runtime
|
|
133
|
+
* (`autoMerge: isStandaloneLocalRuntime()`), or a variable passed through
|
|
134
|
+
* (`{ autoMerge }`). Comments, strings, template text, and regexes never match.
|
|
135
|
+
*
|
|
136
|
+
* Why it is a rule: a person's own write follows their permission. The app
|
|
137
|
+
* leaves `autoMerge` unset; Busabase merges it for someone with write access and
|
|
138
|
+
* opens a ChangeRequest for someone without. An app that forces review makes
|
|
139
|
+
* every owner approve their own click in the inbox — a fix that had to be
|
|
140
|
+
* applied to 49 shipped copies by hand.
|
|
141
|
+
*
|
|
142
|
+
* Forcing review is still right when the write is something outgoing a second
|
|
143
|
+
* person must approve before it is sent. Say so on the line, or the line above,
|
|
144
|
+
* and the rule lets it through:
|
|
145
|
+
*
|
|
146
|
+
* ```js
|
|
147
|
+
* autoMerge: false, // allow-forced-review: drafts an outgoing contract that legal must approve
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* The reason must be at least {@link AIRAPP_FORCED_REVIEW_MIN_REASON} characters.
|
|
151
|
+
*/
|
|
152
|
+
const findForcedReview = (source) => {
|
|
153
|
+
const code = codeOnly(source);
|
|
154
|
+
const lines = source.split("\n");
|
|
155
|
+
const allowReason = (line) => line?.match(ALLOW_FORCED_REVIEW)?.[1]?.trim() ?? null;
|
|
156
|
+
const findings = [];
|
|
157
|
+
for (const match of code.matchAll(/\bautoMerge\b/g)) {
|
|
158
|
+
const offset = match.index ?? 0;
|
|
159
|
+
const after = code.slice(offset + match[0].length);
|
|
160
|
+
if (/^\s*[:=](?!=)\s*true\b/.test(after)) continue;
|
|
161
|
+
const lineIndex = code.slice(0, offset).split("\n").length - 1;
|
|
162
|
+
const reason = allowReason(lines[lineIndex]) ?? (lineIndex > 0 ? allowReason(lines[lineIndex - 1]) : null);
|
|
163
|
+
if (reason !== null && reason.length >= 20) continue;
|
|
164
|
+
findings.push({
|
|
165
|
+
line: lineIndex + 1,
|
|
166
|
+
text: (lines[lineIndex] ?? "").trim(),
|
|
167
|
+
problem: reason === null ? "forces review on a person's own write (leave autoMerge unset), or says why with an allow-forced-review comment" : `allow-forced-review needs a reason of at least 20 characters`
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return findings;
|
|
171
|
+
};
|
|
172
|
+
/** Directories that are not the app's runtime: third-party bundles, the app's own tests and dev checks. */
|
|
173
|
+
const NOT_APP_RUNTIME = /* @__PURE__ */ new Set([
|
|
174
|
+
"node_modules",
|
|
175
|
+
"vendor",
|
|
176
|
+
"test",
|
|
177
|
+
"tests",
|
|
178
|
+
"scripts",
|
|
179
|
+
".git"
|
|
180
|
+
]);
|
|
181
|
+
/**
|
|
182
|
+
* Whether `path` (relative to the app's root, `/`-separated) belongs to the
|
|
183
|
+
* corpus `airapp/forced-review` scans: a script source of the app's own, not a
|
|
184
|
+
* vendored bundle, a test, or a dev script. The same corpus as
|
|
185
|
+
* `scripts/check-app-writes.mjs` in busabase/templates.
|
|
186
|
+
*/
|
|
187
|
+
const isAirAppWriteRuleSource = (path) => {
|
|
188
|
+
const segments = path.split(/[\\/]/).filter(Boolean);
|
|
189
|
+
const name = segments.at(-1) ?? "";
|
|
190
|
+
if (!/\.(?:[cm]?js|jsx|ts|tsx)$/.test(name)) return false;
|
|
191
|
+
if (/\.test\.[cm]?[jt]sx?$/.test(name)) return false;
|
|
192
|
+
return !segments.slice(0, -1).some((segment) => NOT_APP_RUNTIME.has(segment));
|
|
193
|
+
};
|
|
33
194
|
/**
|
|
34
195
|
* Objects inside a `bases: [ ... ]` literal, brace-matched rather than regexed across
|
|
35
196
|
* the array, so a nested `fields:` entry is never mistaken for a Base.
|
|
@@ -119,17 +280,17 @@ const checkAirApp = (sources) => {
|
|
|
119
280
|
if (/(?:src|href)="\/(?!\/)|from\s+["']\/(?!\/)/.test(code)) error("airapp/absolute-asset", "An absolute asset path. Under the Local Node engine the app is proxied onto a sub-path of Busabase's origin, so a leading slash resolves against Busabase itself and 404s.");
|
|
120
281
|
if (/while\s*\(\s*cursor\s*\)|client\.bases\.list\s*\(/.test(code) || /while\s*\(\s*true\s*\)[\s\S]{0,400}?(?:records\.list|readPage)\s*\(/.test(code)) error("airapp/unbounded-read", "Unbounded loading or runtime Base discovery. Every interactive read gets an explicit budget and fetches one page per user action.");
|
|
121
282
|
if (/\bmax\w*pages?\w*\s*=\s*\d+[\s\S]{0,400}?(?:client\.)?records\.list\s*\(/i.test(code)) error("airapp/eager-multi-page", "A capped loop fetches several pages of records in one function call. A cap bounds the damage but does not fix the shape: this still hides a multi-page scan behind one loading state instead of fetching one page per user action.");
|
|
122
|
-
const branches =
|
|
283
|
+
const branches = RUNTIME_BRANCH.test(code) || sources.server !== void 0 && /createAirAppConnectGate/.test(sources.server);
|
|
123
284
|
if (/location\s*\.\s*(?:hostname|host)\b/.test(code)) error("airapp/runtime-hostname", "Hostname-based runtime detection. Both directions are wrong: a Busabase-hosted AirApp is served from `localhost` on Desktop, and a standalone `npm run dev` is reached over a LAN IP or a signed dev tunnel. Read the runtime from `__airapp/runtime`.");
|
|
124
285
|
if (/(?:===|!==|==|!=)\s*["'`][^"'`]*(?:localhost|127\.0\.0\.1)|(?:includes|startsWith|endsWith|indexOf|search|match|test)\s*\(\s*\/?["'`]?[^"'`)]*(?:localhost|127\.0\.0\.1)/.test(code)) error("airapp/runtime-loopback", "A loopback host comparison. Runtime detection must not depend on the URL at all.");
|
|
125
|
-
if (branches && !logic.includes("__airapp/runtime")) error("airapp/runtime-probe", "This app branches on where it is running but never probes `__airapp/runtime`, so that branch is deciding on something else.");
|
|
286
|
+
if (branches && !logic.includes("__airapp/runtime") && !SDK_RUNTIME_PROBE.test(code)) error("airapp/runtime-probe", "This app branches on where it is running but never probes `__airapp/runtime`, so that branch is deciding on something else.");
|
|
126
287
|
if (/["'`]\/__airapp\/runtime/.test(logic)) error("airapp/runtime-probe-relative", "The runtime probe has a leading slash. It must be relative (`__airapp/runtime`) — a leading slash resolves against Busabase's root under the Local Node sub-path proxy.");
|
|
127
288
|
}
|
|
128
289
|
if (sources.server !== void 0) {
|
|
129
290
|
const server = sources.server;
|
|
130
291
|
const serverCode = stripComments(server);
|
|
131
292
|
const readsRuntimeEnv = (sources.serverLanguage ? sources.serverLanguage === "python" : /^\s*(?:import|from)\s+\w+|def\s+\w+\s*\(/m.test(server)) ? /BUSABASE_AIRAPP_RUNTIME/.test(serverCode) : /(?:read|describe)BusabaseAirAppRuntime\s*\(|process\.env\.BUSABASE_AIRAPP_RUNTIME\b/.test(serverCode);
|
|
132
|
-
const clientBranches = sources.browserLogic !== void 0 &&
|
|
293
|
+
const clientBranches = sources.browserLogic !== void 0 && RUNTIME_BRANCH.test(stripComments(sources.browserLogic));
|
|
133
294
|
if (clientBranches && !readsRuntimeEnv) error("airapp/runtime-env", "The server never reads `BUSABASE_AIRAPP_RUNTIME`, directly or through the SDK, so it has nothing to serve at `__airapp/runtime`.");
|
|
134
295
|
if (/AIRAPP_HOSTED_RUNTIMES\s*=\s*new Set|hosted:\s*\w*RUNTIMES?\w*\.has\s*\(/.test(serverCode)) error("airapp/runtime-engine-list", "Hosting is decided from a hardcoded list of engine names. Use presence, not membership — a private list is what broke 66 apps when `local-node` was renamed `local`.");
|
|
135
296
|
if (clientBranches && !/["'`]\/__airapp\/runtime["'`]/.test(server)) error("airapp/runtime-route", "Browser code branches on the runtime but the server does not serve `/__airapp/runtime`.");
|
|
@@ -141,6 +302,8 @@ const checkAirApp = (sources) => {
|
|
|
141
302
|
if (/Bearer\s+(?!\$\{)[A-Za-z0-9_.-]{8,}/.test(downloads)) error("airapp/credential", "A literal Bearer token in browser source.");
|
|
142
303
|
if (/["'`]\s*Bearer\s*\$\{/.test(downloads)) error("airapp/credential", "Browser code builds an Authorization header. A deployed AirApp uses the viewer's ambient same-origin session and needs none.");
|
|
143
304
|
}
|
|
305
|
+
const forcedReviewCorpus = sources.appSources ? sources.appSources.filter((file) => isAirAppWriteRuleSource(file.path)) : [sources.browserLogic, sources.server].filter((content) => content !== void 0).map((content) => ({ content }));
|
|
306
|
+
for (const file of forcedReviewCorpus) for (const found of findForcedReview(file.content)) error("airapp/forced-review", `${file.path ? `${file.path}:${found.line}: ` : ""}\`${found.text}\` ${found.problem}. Leave \`autoMerge\` unset: Busabase merges a person's write when they have write access and opens a ChangeRequest when they do not.`);
|
|
144
307
|
if (sources.config !== void 0) {
|
|
145
308
|
const config = scanAirAppConfig(sources.config);
|
|
146
309
|
if (sources.shippedSlug !== void 0 && config.resourceKey !== null && config.resourceKey !== sources.shippedSlug) error("airapp/resource-key", `Config declares resourceKey "${config.resourceKey}" but the package ships this app as "${sources.shippedSlug}". Install stamps nodes with the shipped slug, so the app would not recognise its own node.`);
|
|
@@ -156,4 +319,4 @@ const checkAirApp = (sources) => {
|
|
|
156
319
|
return findings;
|
|
157
320
|
};
|
|
158
321
|
//#endregion
|
|
159
|
-
export { AIRAPP_DEFAULT_READ_LIMIT, checkAirApp, scanAirAppConfig, stripComments };
|
|
322
|
+
export { AIRAPP_DEFAULT_READ_LIMIT, AIRAPP_FORCED_REVIEW_MIN_REASON, checkAirApp, codeOnly, findForcedReview, isAirAppWriteRuleSource, scanAirAppConfig, stripComments };
|
package/dist/airapp-gate.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BusabaseAirAppAuthStatus } from "./airapp-node.js";
|
|
1
|
+
import { a as BusabaseAirAppAuthStatus } from "./airapp-node-D2GcMwde.js";
|
|
2
2
|
//#region src/airapp-gate.d.ts
|
|
3
3
|
/** Which screen the gate owes the operator right now. */
|
|
4
4
|
type AirAppGateScreen = "connect" | "space" | "ready";
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { t as BusabaseAirAppCredentialStoreOptions } from "./oauth-node-DGiWkh_l.js";
|
|
2
|
+
//#region src/airapp-node.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The env var Busabase injects into every AirApp process it spawns. Nobody else
|
|
5
|
+
* sets it, which is what makes its **absence** the positive fact "standalone".
|
|
6
|
+
*/
|
|
7
|
+
declare const BUSABASE_AIRAPP_RUNTIME_ENV = "BUSABASE_AIRAPP_RUNTIME";
|
|
8
|
+
/**
|
|
9
|
+
* Every runtime Busabase hosts an AirApp in, as known to *this* SDK version.
|
|
10
|
+
*
|
|
11
|
+
* **Informational. Not what decides whether an app is hosted.** That separation
|
|
12
|
+
* is the whole point of this constant existing here rather than being pasted
|
|
13
|
+
* into each app, and getting it backwards is a bug we have already shipped once:
|
|
14
|
+
* every generated AirApp used to carry its own hardcoded copy of this list and
|
|
15
|
+
* answer `hosted` from membership in it. When the `local-node` engine was
|
|
16
|
+
* renamed to `local`, all of them started answering "standalone" *inside a
|
|
17
|
+
* hosted preview* — showing their own connection gate, calling `/api/v1` with no
|
|
18
|
+
* credential, and leaving the operator with nothing to act on.
|
|
19
|
+
*
|
|
20
|
+
* Moving the list into the SDK does not by itself fix that. An app pins an SDK
|
|
21
|
+
* version, so a list that gates behaviour would simply rot on a slower clock:
|
|
22
|
+
* add an engine, and every app on an older SDK is wrong again until it upgrades.
|
|
23
|
+
*
|
|
24
|
+
* So the list names runtimes; {@link isBusabaseAirAppHosted} decides hosting,
|
|
25
|
+
* from presence alone, and needs no upgrade to stay correct.
|
|
26
|
+
*/
|
|
27
|
+
declare const BUSABASE_AIRAPP_RUNTIMES: readonly ["browser", "local", "remote", "embed"];
|
|
28
|
+
/**
|
|
29
|
+
* Names Busabase has emitted in the past and may still be emitting to an app
|
|
30
|
+
* pinned to an older deployment. Kept separate from the list above so the
|
|
31
|
+
* current vocabulary stays readable, and exported so an app that genuinely
|
|
32
|
+
* wants to branch (a demo that prints where it is running, say) can normalise
|
|
33
|
+
* instead of growing its own if-chain. Nothing here gates `hosted`.
|
|
34
|
+
*/
|
|
35
|
+
declare const BUSABASE_AIRAPP_RUNTIME_ALIASES: Record<string, BusabaseAirAppRuntime>;
|
|
36
|
+
type BusabaseAirAppRuntime = (typeof BUSABASE_AIRAPP_RUNTIMES)[number];
|
|
37
|
+
/** Read the injected runtime, normalised. `""` means nobody hosted this process. */
|
|
38
|
+
declare const readBusabaseAirAppRuntime: (env?: Record<string, string | undefined>) => string;
|
|
39
|
+
/**
|
|
40
|
+
* Whether Busabase is hosting this process.
|
|
41
|
+
*
|
|
42
|
+
* Presence, never membership — see {@link BUSABASE_AIRAPP_RUNTIMES}. An engine
|
|
43
|
+
* this SDK has never heard of still means "Busabase spawned me", because only
|
|
44
|
+
* Busabase sets the variable at all.
|
|
45
|
+
*/
|
|
46
|
+
declare const isBusabaseAirAppHosted: (runtime?: string) => boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Narrow a runtime string to one this SDK knows, or `null`.
|
|
49
|
+
*
|
|
50
|
+
* For code that wants to *branch* on a specific engine — an app that renders
|
|
51
|
+
* differently under a public embed, say. Deliberately returns `null` rather than
|
|
52
|
+
* throwing on an unknown value: a newer Busabase naming an engine this SDK
|
|
53
|
+
* predates is normal, not an error, and the caller should fall back to generic
|
|
54
|
+
* behaviour rather than break.
|
|
55
|
+
*/
|
|
56
|
+
declare const asKnownBusabaseAirAppRuntime: (runtime: string) => BusabaseAirAppRuntime | null;
|
|
57
|
+
/** The body every AirApp serves at `__airapp/runtime`. */
|
|
58
|
+
interface BusabaseAirAppRuntimeReport {
|
|
59
|
+
/**
|
|
60
|
+
* The engine name verbatim, or `"standalone"` when nothing hosted this process.
|
|
61
|
+
*
|
|
62
|
+
* Deliberately a plain string and not the enum: a newer Busabase naming an engine
|
|
63
|
+
* this SDK predates must still round-trip, and narrowing here would erase it.
|
|
64
|
+
*/
|
|
65
|
+
runtime: string;
|
|
66
|
+
/**
|
|
67
|
+
* The same value narrowed to what this SDK knows, or `null` for an engine it has
|
|
68
|
+
* never heard of. This is the field to branch on when behaviour genuinely differs
|
|
69
|
+
* per engine — and `null` is a normal answer, not an error.
|
|
70
|
+
*/
|
|
71
|
+
knownRuntime: BusabaseAirAppRuntime | null;
|
|
72
|
+
/** Presence of the injected variable. Never membership of {@link BUSABASE_AIRAPP_RUNTIMES}. */
|
|
73
|
+
hosted: boolean;
|
|
74
|
+
/** Whether a standalone run is proxying to a Busabase base URL. A separate axis. */
|
|
75
|
+
devProxy: boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Build the `__airapp/runtime` body.
|
|
79
|
+
*
|
|
80
|
+
* This exists because the one line it replaces — `hosted: …` — has now regressed
|
|
81
|
+
* twice. Both times a correct app was copied from an older source and came back
|
|
82
|
+
* deciding hosting from a hardcoded list of engine names, and both times every test
|
|
83
|
+
* stayed green, because nothing asserted the shape of that decision.
|
|
84
|
+
*
|
|
85
|
+
* An app that calls this cannot get it wrong: `hosted` is presence by construction,
|
|
86
|
+
* and the engine name is reported both verbatim (so an unknown one survives) and
|
|
87
|
+
* narrowed (so code that wants to branch on a specific engine has a type).
|
|
88
|
+
*/
|
|
89
|
+
declare const describeBusabaseAirAppRuntime: (env?: Record<string, string | undefined>) => BusabaseAirAppRuntimeReport;
|
|
90
|
+
declare const BUSABASE_AIRAPP_GATEWAY_REASONS: {
|
|
91
|
+
readonly authRequired: "AUTH_REQUIRED";
|
|
92
|
+
readonly authUnavailable: "AUTH_UNAVAILABLE";
|
|
93
|
+
readonly connectionRequired: "CONNECTION_REQUIRED";
|
|
94
|
+
readonly oauthCallbackInvalid: "OAUTH_CALLBACK_INVALID";
|
|
95
|
+
readonly spaceNotAllowed: "SPACE_NOT_ALLOWED";
|
|
96
|
+
readonly spaceSelectionRequired: "SPACE_SELECTION_REQUIRED";
|
|
97
|
+
};
|
|
98
|
+
type GatewayReason = (typeof BUSABASE_AIRAPP_GATEWAY_REASONS)[keyof typeof BUSABASE_AIRAPP_GATEWAY_REASONS];
|
|
99
|
+
interface AuthSpace {
|
|
100
|
+
id: string;
|
|
101
|
+
name: string;
|
|
102
|
+
slug?: string | null;
|
|
103
|
+
plan?: string | null;
|
|
104
|
+
}
|
|
105
|
+
interface GatewayTarget {
|
|
106
|
+
baseUrl: string;
|
|
107
|
+
accessToken: string;
|
|
108
|
+
selectedSpace?: AuthSpace;
|
|
109
|
+
source: "airapp-oauth-local" | "environment" | "open-server";
|
|
110
|
+
}
|
|
111
|
+
interface BusabaseAirAppLocalGatewayOptions {
|
|
112
|
+
appId: string;
|
|
113
|
+
cloudBaseUrl?: string;
|
|
114
|
+
clientId?: string;
|
|
115
|
+
environment?: Record<string, string | undefined>;
|
|
116
|
+
fetch?: typeof fetch;
|
|
117
|
+
now?: () => number;
|
|
118
|
+
oauthPendingTtlMs?: number;
|
|
119
|
+
requestTimeoutMs?: number;
|
|
120
|
+
credentialStore?: BusabaseAirAppCredentialStoreOptions;
|
|
121
|
+
successPath?: string;
|
|
122
|
+
errorPath?: string;
|
|
123
|
+
}
|
|
124
|
+
interface BusabaseAirAppAuthStatus {
|
|
125
|
+
connected: boolean;
|
|
126
|
+
cloudBaseUrl: string;
|
|
127
|
+
baseUrl?: string;
|
|
128
|
+
source?: GatewayTarget["source"];
|
|
129
|
+
readiness: "needs_connection" | "needs_auth" | "needs_space" | "ready" | "retry";
|
|
130
|
+
action: "connect" | "reconnect" | "select_space" | "continue" | "retry";
|
|
131
|
+
requiresSpace?: boolean;
|
|
132
|
+
selectedSpace?: AuthSpace | null;
|
|
133
|
+
space?: AuthSpace | null;
|
|
134
|
+
spaces?: AuthSpace[];
|
|
135
|
+
reason?: GatewayReason;
|
|
136
|
+
message?: string;
|
|
137
|
+
}
|
|
138
|
+
declare class BusabaseAirAppLocalGateway {
|
|
139
|
+
#private;
|
|
140
|
+
constructor(options: BusabaseAirAppLocalGatewayOptions);
|
|
141
|
+
get cloudBaseUrl(): string;
|
|
142
|
+
status(): Promise<BusabaseAirAppAuthStatus>;
|
|
143
|
+
statusResponse: () => Promise<Response>;
|
|
144
|
+
start: (request: Request) => Promise<Response>;
|
|
145
|
+
callback: (request: Request) => Promise<Response>;
|
|
146
|
+
selectSpace: (request: Request) => Promise<Response>;
|
|
147
|
+
logout: (request: Request) => Promise<Response>;
|
|
148
|
+
proxy: (request: Request) => Promise<Response>;
|
|
149
|
+
}
|
|
150
|
+
declare const createBusabaseAirAppLocalGateway: (options: BusabaseAirAppLocalGatewayOptions) => BusabaseAirAppLocalGateway;
|
|
151
|
+
//#endregion
|
|
152
|
+
export { BusabaseAirAppAuthStatus as a, BusabaseAirAppRuntime as c, createBusabaseAirAppLocalGateway as d, describeBusabaseAirAppRuntime as f, BUSABASE_AIRAPP_RUNTIME_ENV as i, BusabaseAirAppRuntimeReport as l, readBusabaseAirAppRuntime as m, BUSABASE_AIRAPP_RUNTIMES as n, BusabaseAirAppLocalGateway as o, isBusabaseAirAppHosted as p, BUSABASE_AIRAPP_RUNTIME_ALIASES as r, BusabaseAirAppLocalGatewayOptions as s, BUSABASE_AIRAPP_GATEWAY_REASONS as t, asKnownBusabaseAirAppRuntime as u };
|
package/dist/airapp-node.d.ts
CHANGED
|
@@ -1,152 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
//#region src/airapp-node.d.ts
|
|
3
|
-
/**
|
|
4
|
-
* The env var Busabase injects into every AirApp process it spawns. Nobody else
|
|
5
|
-
* sets it, which is what makes its **absence** the positive fact "standalone".
|
|
6
|
-
*/
|
|
7
|
-
declare const BUSABASE_AIRAPP_RUNTIME_ENV = "BUSABASE_AIRAPP_RUNTIME";
|
|
8
|
-
/**
|
|
9
|
-
* Every runtime Busabase hosts an AirApp in, as known to *this* SDK version.
|
|
10
|
-
*
|
|
11
|
-
* **Informational. Not what decides whether an app is hosted.** That separation
|
|
12
|
-
* is the whole point of this constant existing here rather than being pasted
|
|
13
|
-
* into each app, and getting it backwards is a bug we have already shipped once:
|
|
14
|
-
* every generated AirApp used to carry its own hardcoded copy of this list and
|
|
15
|
-
* answer `hosted` from membership in it. When the `local-node` engine was
|
|
16
|
-
* renamed to `local`, all of them started answering "standalone" *inside a
|
|
17
|
-
* hosted preview* — showing their own connection gate, calling `/api/v1` with no
|
|
18
|
-
* credential, and leaving the operator with nothing to act on.
|
|
19
|
-
*
|
|
20
|
-
* Moving the list into the SDK does not by itself fix that. An app pins an SDK
|
|
21
|
-
* version, so a list that gates behaviour would simply rot on a slower clock:
|
|
22
|
-
* add an engine, and every app on an older SDK is wrong again until it upgrades.
|
|
23
|
-
*
|
|
24
|
-
* So the list names runtimes; {@link isBusabaseAirAppHosted} decides hosting,
|
|
25
|
-
* from presence alone, and needs no upgrade to stay correct.
|
|
26
|
-
*/
|
|
27
|
-
declare const BUSABASE_AIRAPP_RUNTIMES: readonly ["browser", "local", "remote", "embed"];
|
|
28
|
-
/**
|
|
29
|
-
* Names Busabase has emitted in the past and may still be emitting to an app
|
|
30
|
-
* pinned to an older deployment. Kept separate from the list above so the
|
|
31
|
-
* current vocabulary stays readable, and exported so an app that genuinely
|
|
32
|
-
* wants to branch (a demo that prints where it is running, say) can normalise
|
|
33
|
-
* instead of growing its own if-chain. Nothing here gates `hosted`.
|
|
34
|
-
*/
|
|
35
|
-
declare const BUSABASE_AIRAPP_RUNTIME_ALIASES: Record<string, BusabaseAirAppRuntime>;
|
|
36
|
-
type BusabaseAirAppRuntime = (typeof BUSABASE_AIRAPP_RUNTIMES)[number];
|
|
37
|
-
/** Read the injected runtime, normalised. `""` means nobody hosted this process. */
|
|
38
|
-
declare const readBusabaseAirAppRuntime: (env?: Record<string, string | undefined>) => string;
|
|
39
|
-
/**
|
|
40
|
-
* Whether Busabase is hosting this process.
|
|
41
|
-
*
|
|
42
|
-
* Presence, never membership — see {@link BUSABASE_AIRAPP_RUNTIMES}. An engine
|
|
43
|
-
* this SDK has never heard of still means "Busabase spawned me", because only
|
|
44
|
-
* Busabase sets the variable at all.
|
|
45
|
-
*/
|
|
46
|
-
declare const isBusabaseAirAppHosted: (runtime?: string) => boolean;
|
|
47
|
-
/**
|
|
48
|
-
* Narrow a runtime string to one this SDK knows, or `null`.
|
|
49
|
-
*
|
|
50
|
-
* For code that wants to *branch* on a specific engine — an app that renders
|
|
51
|
-
* differently under a public embed, say. Deliberately returns `null` rather than
|
|
52
|
-
* throwing on an unknown value: a newer Busabase naming an engine this SDK
|
|
53
|
-
* predates is normal, not an error, and the caller should fall back to generic
|
|
54
|
-
* behaviour rather than break.
|
|
55
|
-
*/
|
|
56
|
-
declare const asKnownBusabaseAirAppRuntime: (runtime: string) => BusabaseAirAppRuntime | null;
|
|
57
|
-
/** The body every AirApp serves at `__airapp/runtime`. */
|
|
58
|
-
interface BusabaseAirAppRuntimeReport {
|
|
59
|
-
/**
|
|
60
|
-
* The engine name verbatim, or `"standalone"` when nothing hosted this process.
|
|
61
|
-
*
|
|
62
|
-
* Deliberately a plain string and not the enum: a newer Busabase naming an engine
|
|
63
|
-
* this SDK predates must still round-trip, and narrowing here would erase it.
|
|
64
|
-
*/
|
|
65
|
-
runtime: string;
|
|
66
|
-
/**
|
|
67
|
-
* The same value narrowed to what this SDK knows, or `null` for an engine it has
|
|
68
|
-
* never heard of. This is the field to branch on when behaviour genuinely differs
|
|
69
|
-
* per engine — and `null` is a normal answer, not an error.
|
|
70
|
-
*/
|
|
71
|
-
knownRuntime: BusabaseAirAppRuntime | null;
|
|
72
|
-
/** Presence of the injected variable. Never membership of {@link BUSABASE_AIRAPP_RUNTIMES}. */
|
|
73
|
-
hosted: boolean;
|
|
74
|
-
/** Whether a standalone run is proxying to a Busabase base URL. A separate axis. */
|
|
75
|
-
devProxy: boolean;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Build the `__airapp/runtime` body.
|
|
79
|
-
*
|
|
80
|
-
* This exists because the one line it replaces — `hosted: …` — has now regressed
|
|
81
|
-
* twice. Both times a correct app was copied from an older source and came back
|
|
82
|
-
* deciding hosting from a hardcoded list of engine names, and both times every test
|
|
83
|
-
* stayed green, because nothing asserted the shape of that decision.
|
|
84
|
-
*
|
|
85
|
-
* An app that calls this cannot get it wrong: `hosted` is presence by construction,
|
|
86
|
-
* and the engine name is reported both verbatim (so an unknown one survives) and
|
|
87
|
-
* narrowed (so code that wants to branch on a specific engine has a type).
|
|
88
|
-
*/
|
|
89
|
-
declare const describeBusabaseAirAppRuntime: (env?: Record<string, string | undefined>) => BusabaseAirAppRuntimeReport;
|
|
90
|
-
declare const BUSABASE_AIRAPP_GATEWAY_REASONS: {
|
|
91
|
-
readonly authRequired: "AUTH_REQUIRED";
|
|
92
|
-
readonly authUnavailable: "AUTH_UNAVAILABLE";
|
|
93
|
-
readonly connectionRequired: "CONNECTION_REQUIRED";
|
|
94
|
-
readonly oauthCallbackInvalid: "OAUTH_CALLBACK_INVALID";
|
|
95
|
-
readonly spaceNotAllowed: "SPACE_NOT_ALLOWED";
|
|
96
|
-
readonly spaceSelectionRequired: "SPACE_SELECTION_REQUIRED";
|
|
97
|
-
};
|
|
98
|
-
type GatewayReason = (typeof BUSABASE_AIRAPP_GATEWAY_REASONS)[keyof typeof BUSABASE_AIRAPP_GATEWAY_REASONS];
|
|
99
|
-
interface AuthSpace {
|
|
100
|
-
id: string;
|
|
101
|
-
name: string;
|
|
102
|
-
slug?: string | null;
|
|
103
|
-
plan?: string | null;
|
|
104
|
-
}
|
|
105
|
-
interface GatewayTarget {
|
|
106
|
-
baseUrl: string;
|
|
107
|
-
accessToken: string;
|
|
108
|
-
selectedSpace?: AuthSpace;
|
|
109
|
-
source: "airapp-oauth-local" | "environment" | "open-server";
|
|
110
|
-
}
|
|
111
|
-
interface BusabaseAirAppLocalGatewayOptions {
|
|
112
|
-
appId: string;
|
|
113
|
-
cloudBaseUrl?: string;
|
|
114
|
-
clientId?: string;
|
|
115
|
-
environment?: Record<string, string | undefined>;
|
|
116
|
-
fetch?: typeof fetch;
|
|
117
|
-
now?: () => number;
|
|
118
|
-
oauthPendingTtlMs?: number;
|
|
119
|
-
requestTimeoutMs?: number;
|
|
120
|
-
credentialStore?: BusabaseAirAppCredentialStoreOptions;
|
|
121
|
-
successPath?: string;
|
|
122
|
-
errorPath?: string;
|
|
123
|
-
}
|
|
124
|
-
interface BusabaseAirAppAuthStatus {
|
|
125
|
-
connected: boolean;
|
|
126
|
-
cloudBaseUrl: string;
|
|
127
|
-
baseUrl?: string;
|
|
128
|
-
source?: GatewayTarget["source"];
|
|
129
|
-
readiness: "needs_connection" | "needs_auth" | "needs_space" | "ready" | "retry";
|
|
130
|
-
action: "connect" | "reconnect" | "select_space" | "continue" | "retry";
|
|
131
|
-
requiresSpace?: boolean;
|
|
132
|
-
selectedSpace?: AuthSpace | null;
|
|
133
|
-
space?: AuthSpace | null;
|
|
134
|
-
spaces?: AuthSpace[];
|
|
135
|
-
reason?: GatewayReason;
|
|
136
|
-
message?: string;
|
|
137
|
-
}
|
|
138
|
-
declare class BusabaseAirAppLocalGateway {
|
|
139
|
-
#private;
|
|
140
|
-
constructor(options: BusabaseAirAppLocalGatewayOptions);
|
|
141
|
-
get cloudBaseUrl(): string;
|
|
142
|
-
status(): Promise<BusabaseAirAppAuthStatus>;
|
|
143
|
-
statusResponse: () => Promise<Response>;
|
|
144
|
-
start: (request: Request) => Promise<Response>;
|
|
145
|
-
callback: (request: Request) => Promise<Response>;
|
|
146
|
-
selectSpace: (request: Request) => Promise<Response>;
|
|
147
|
-
logout: (request: Request) => Promise<Response>;
|
|
148
|
-
proxy: (request: Request) => Promise<Response>;
|
|
149
|
-
}
|
|
150
|
-
declare const createBusabaseAirAppLocalGateway: (options: BusabaseAirAppLocalGatewayOptions) => BusabaseAirAppLocalGateway;
|
|
151
|
-
//#endregion
|
|
1
|
+
import { a as BusabaseAirAppAuthStatus, c as BusabaseAirAppRuntime, d as createBusabaseAirAppLocalGateway, f as describeBusabaseAirAppRuntime, i as BUSABASE_AIRAPP_RUNTIME_ENV, l as BusabaseAirAppRuntimeReport, m as readBusabaseAirAppRuntime, n as BUSABASE_AIRAPP_RUNTIMES, o as BusabaseAirAppLocalGateway, p as isBusabaseAirAppHosted, r as BUSABASE_AIRAPP_RUNTIME_ALIASES, s as BusabaseAirAppLocalGatewayOptions, t as BUSABASE_AIRAPP_GATEWAY_REASONS, u as asKnownBusabaseAirAppRuntime } from "./airapp-node-D2GcMwde.js";
|
|
152
2
|
export { BUSABASE_AIRAPP_GATEWAY_REASONS, BUSABASE_AIRAPP_RUNTIMES, BUSABASE_AIRAPP_RUNTIME_ALIASES, BUSABASE_AIRAPP_RUNTIME_ENV, BusabaseAirAppAuthStatus, BusabaseAirAppLocalGateway, BusabaseAirAppLocalGatewayOptions, BusabaseAirAppRuntime, BusabaseAirAppRuntimeReport, asKnownBusabaseAirAppRuntime, createBusabaseAirAppLocalGateway, describeBusabaseAirAppRuntime, isBusabaseAirAppHosted, readBusabaseAirAppRuntime };
|