@beignet/cli 0.0.52 → 0.0.53
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 +26 -0
- package/README.md +131 -1
- package/dist/analysis/layers.d.ts +2 -0
- package/dist/analysis/layers.d.ts.map +1 -1
- package/dist/analysis/layers.js +5 -0
- package/dist/analysis/layers.js.map +1 -1
- package/dist/analysis/port-wiring.d.ts +4 -2
- package/dist/analysis/port-wiring.d.ts.map +1 -1
- package/dist/analysis/port-wiring.js +39 -10
- package/dist/analysis/port-wiring.js.map +1 -1
- package/dist/analysis/source-index.d.ts +2 -1
- package/dist/analysis/source-index.d.ts.map +1 -1
- package/dist/analysis/source-index.js +34 -5
- package/dist/analysis/source-index.js.map +1 -1
- package/dist/analysis/workspace-routes.d.ts +8 -0
- package/dist/analysis/workspace-routes.d.ts.map +1 -0
- package/dist/analysis/workspace-routes.js +291 -0
- package/dist/analysis/workspace-routes.js.map +1 -0
- package/dist/analysis/workspace.d.ts +24 -3
- package/dist/analysis/workspace.d.ts.map +1 -1
- package/dist/analysis/workspace.js +166 -21
- package/dist/analysis/workspace.js.map +1 -1
- package/dist/app-map-changes.d.ts.map +1 -1
- package/dist/app-map-changes.js +19 -7
- package/dist/app-map-changes.js.map +1 -1
- package/dist/app-map.d.ts.map +1 -1
- package/dist/app-map.js +58 -55
- package/dist/app-map.js.map +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +19 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +89 -21
- package/dist/inspect.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +106 -38
- package/dist/lint.js.map +1 -1
- package/dist/make/shared.js +1 -1
- package/dist/make.js +18 -1
- package/dist/make.js.map +1 -1
- package/dist/provider-add.js +2 -2
- package/dist/provider-add.js.map +1 -1
- package/dist/provider-audit.d.ts.map +1 -1
- package/dist/provider-audit.js +22 -6
- package/dist/provider-audit.js.map +1 -1
- package/dist/templates/shared.js +3 -3
- package/package.json +3 -2
- package/skills/app-structure/SKILL.md +42 -0
- package/src/analysis/layers.ts +9 -0
- package/src/analysis/port-wiring.ts +66 -10
- package/src/analysis/source-index.ts +56 -4
- package/src/analysis/workspace-routes.ts +385 -0
- package/src/analysis/workspace.ts +281 -34
- package/src/app-map-changes.ts +31 -5
- package/src/app-map.ts +75 -75
- package/src/config.ts +33 -0
- package/src/index.ts +25 -0
- package/src/inspect.ts +123 -16
- package/src/lint.ts +146 -43
- package/src/make/shared.ts +1 -1
- package/src/make.ts +31 -1
- package/src/provider-add.ts +2 -2
- package/src/provider-audit.ts +30 -10
- package/src/templates/shared.ts +3 -3
|
@@ -77,6 +77,15 @@ beignet db migrate
|
|
|
77
77
|
beignet db status
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
+
## Encryption keys
|
|
81
|
+
|
|
82
|
+
`beignet encryption key` prints a fresh key for `@beignet/core/encryption`;
|
|
83
|
+
`--json` returns `schemaVersion` and `key`. It does not read or overwrite env
|
|
84
|
+
files. Keep the output out of shared logs and wire the persistent key through
|
|
85
|
+
server env config and `infra/port-wiring.ts`. Retain keys needed by existing
|
|
86
|
+
records and backups during rotation. The starter does not configure encryption
|
|
87
|
+
until the app needs it; no encryption generator or provider preset is required.
|
|
88
|
+
|
|
80
89
|
## Explicit Registration
|
|
81
90
|
|
|
82
91
|
Hand-written files do not run until registered:
|
|
@@ -174,6 +183,39 @@ root at `src/client/index.ts`. Other registries and app-bound builders are not
|
|
|
174
183
|
relocated implicitly; override each path the app has moved, such as
|
|
175
184
|
`listeners`, `schedules`, `tasks`, `outbox`, and their matching builder paths.
|
|
176
185
|
|
|
186
|
+
## Workspace Source Packages
|
|
187
|
+
|
|
188
|
+
An app may set `workspace.packages` to explicit relative source-package
|
|
189
|
+
paths, including transitive dependencies. Each included package must have a
|
|
190
|
+
unique manifest name, belong to the app's declared dependency graph, and use a
|
|
191
|
+
separate root. Read the package's own Beignet config and TypeScript config
|
|
192
|
+
before changing its files. Package exports must resolve to source; do not guess
|
|
193
|
+
source from `dist` or declaration files.
|
|
194
|
+
|
|
195
|
+
Lint follows imports and reexports across these packages using each owner's
|
|
196
|
+
layers. Relative imports, TypeScript aliases, and `package.json#imports` aliases
|
|
197
|
+
that resolve outside the configured source roots also require the owning source
|
|
198
|
+
package in `workspace.packages`. Aliases to installed external dependencies remain
|
|
199
|
+
external imports.
|
|
200
|
+
Shared feature names in map/explain are qualified as
|
|
201
|
+
`@scope/package:feature`, and source evidence remains app-relative, including
|
|
202
|
+
`../`. Routes/doctor follow explicit server route composition and report
|
|
203
|
+
unresolved workspace registration instead of executing it. Trace the exported
|
|
204
|
+
server or the value returned by the exported Next.js server loader; unused
|
|
205
|
+
factory calls do not establish registration. Verified per-file Next.js handlers
|
|
206
|
+
do not require central registration. AppPorts and initialPorts may be reexported
|
|
207
|
+
through source entrypoints, including wiring aliases used by inferred AppPorts.
|
|
208
|
+
Import-then-export barrels and type-only port exports are supported. Match
|
|
209
|
+
contracts by their resolved declaration, including loader-local aliases, rather
|
|
210
|
+
than assuming export names are unique across packages.
|
|
211
|
+
|
|
212
|
+
Keep startup, lifecycle provider registration, env, database commands, and
|
|
213
|
+
operational registries app-owned. Those doctor checks still use app conventions;
|
|
214
|
+
a worker-only package can run lint without pretending to expose an HTTP server.
|
|
215
|
+
Generators and doctor repairs stay within the selected app. Never put a sibling
|
|
216
|
+
package path into `paths.*` to make a generator write there. See the CLI README's
|
|
217
|
+
workspace source packages section for the supported scope and fixture.
|
|
218
|
+
|
|
177
219
|
## Lint And Doctor
|
|
178
220
|
|
|
179
221
|
Start unfamiliar app work with `beignet map`. Its human output summarizes each
|
package/src/analysis/layers.ts
CHANGED
|
@@ -7,6 +7,15 @@ import {
|
|
|
7
7
|
} from "../config.js";
|
|
8
8
|
import { isAppTestFile } from "../test-discovery.js";
|
|
9
9
|
import { stripSourceFileExtension } from "./source-files.js";
|
|
10
|
+
import { type AnalysisWorkspace, projectSource } from "./workspace.js";
|
|
11
|
+
|
|
12
|
+
export function classifyWorkspaceSourcePath(
|
|
13
|
+
file: string,
|
|
14
|
+
workspace: AnalysisWorkspace,
|
|
15
|
+
): SourceLayer {
|
|
16
|
+
const source = projectSource(workspace, file);
|
|
17
|
+
return classifySourcePath(source.file, source.project.config);
|
|
18
|
+
}
|
|
10
19
|
|
|
11
20
|
export type SourceLayer =
|
|
12
21
|
| "app"
|
|
@@ -3,7 +3,15 @@ import path from "node:path";
|
|
|
3
3
|
import ts from "typescript";
|
|
4
4
|
import type { ResolvedBeignetConfig } from "../config.js";
|
|
5
5
|
import { sourceFileScriptKind } from "./source-files.js";
|
|
6
|
-
import
|
|
6
|
+
import {
|
|
7
|
+
createSourceIndex,
|
|
8
|
+
type ResolvedSourceReference,
|
|
9
|
+
type SourceIndex,
|
|
10
|
+
} from "./source-index.js";
|
|
11
|
+
import {
|
|
12
|
+
type AnalysisWorkspace,
|
|
13
|
+
createAnalysisWorkspace,
|
|
14
|
+
} from "./workspace.js";
|
|
7
15
|
|
|
8
16
|
type PortAnalysisWorkspace = Pick<
|
|
9
17
|
AnalysisWorkspace,
|
|
@@ -14,6 +22,7 @@ export type PortWiringAnalysis = {
|
|
|
14
22
|
portsFileExists: boolean;
|
|
15
23
|
portWiringFileExists: boolean;
|
|
16
24
|
properties: Map<string, ts.PropertySignature>;
|
|
25
|
+
propertyFiles: Map<string, string>;
|
|
17
26
|
declared: Set<string>;
|
|
18
27
|
bound: Set<string>;
|
|
19
28
|
deferred: Set<string>;
|
|
@@ -24,6 +33,7 @@ export type PortWiringAnalysis = {
|
|
|
24
33
|
|
|
25
34
|
export async function analyzePortWiring(
|
|
26
35
|
workspace: PortAnalysisWorkspace,
|
|
36
|
+
sourceIndex?: SourceIndex,
|
|
27
37
|
): Promise<PortWiringAnalysis> {
|
|
28
38
|
const result: PortWiringAnalysis = {
|
|
29
39
|
portsFileExists: workspace.fileSet.has(workspace.config.paths.ports),
|
|
@@ -31,6 +41,7 @@ export async function analyzePortWiring(
|
|
|
31
41
|
workspace.config.paths.portWiring,
|
|
32
42
|
),
|
|
33
43
|
properties: new Map(),
|
|
44
|
+
propertyFiles: new Map(),
|
|
34
45
|
declared: new Set(),
|
|
35
46
|
bound: new Set(),
|
|
36
47
|
deferred: new Set(),
|
|
@@ -40,20 +51,31 @@ export async function analyzePortWiring(
|
|
|
40
51
|
};
|
|
41
52
|
|
|
42
53
|
let inferredAppPortsName: string | undefined;
|
|
54
|
+
let inferredAppPortsReference: ResolvedSourceReference | undefined;
|
|
43
55
|
if (result.portsFileExists) {
|
|
44
|
-
const
|
|
56
|
+
const reference = await sourceIndex?.resolveExport(
|
|
45
57
|
workspace.config.paths.ports,
|
|
58
|
+
"AppPorts",
|
|
46
59
|
);
|
|
60
|
+
const portsFile = reference?.file ?? workspace.config.paths.ports;
|
|
61
|
+
const portsName = reference?.exportName ?? "AppPorts";
|
|
62
|
+
const sourceFile = await workspace.readSourceFile(portsFile);
|
|
47
63
|
const declarations = collectTypeDeclarations(sourceFile);
|
|
48
|
-
inferredAppPortsName = inferredTypeQueryName(
|
|
64
|
+
inferredAppPortsName = inferredTypeQueryName(portsName, declarations);
|
|
65
|
+
if (inferredAppPortsName)
|
|
66
|
+
inferredAppPortsReference = await sourceIndex?.resolveName(
|
|
67
|
+
portsFile,
|
|
68
|
+
inferredAppPortsName,
|
|
69
|
+
);
|
|
49
70
|
const collection = inferredAppPortsName
|
|
50
71
|
? { properties: [], indeterminate: false }
|
|
51
|
-
: collectNamedTypeProperties(
|
|
72
|
+
: collectNamedTypeProperties(portsName, declarations, !sourceIndex);
|
|
52
73
|
result.declarationIndeterminate = collection.indeterminate;
|
|
53
74
|
for (const property of collection.properties) {
|
|
54
75
|
const name = propertyName(property.name);
|
|
55
76
|
if (name && !result.properties.has(name)) {
|
|
56
77
|
result.properties.set(name, property);
|
|
78
|
+
result.propertyFiles.set(name, portsFile);
|
|
57
79
|
result.declared.add(name);
|
|
58
80
|
}
|
|
59
81
|
}
|
|
@@ -63,12 +85,27 @@ export async function analyzePortWiring(
|
|
|
63
85
|
if (inferredAppPortsName) result.declarationIndeterminate = true;
|
|
64
86
|
return result;
|
|
65
87
|
}
|
|
66
|
-
const
|
|
88
|
+
const wiringReference = await sourceIndex?.resolveExport(
|
|
67
89
|
workspace.config.paths.portWiring,
|
|
90
|
+
"initialPorts",
|
|
91
|
+
);
|
|
92
|
+
const sourceFile = await workspace.readSourceFile(
|
|
93
|
+
wiringReference?.file ?? workspace.config.paths.portWiring,
|
|
68
94
|
);
|
|
69
95
|
const values = collectVariableInitializers(sourceFile);
|
|
70
|
-
const definitions = portDefinitionCalls(
|
|
71
|
-
|
|
96
|
+
const definitions = portDefinitionCalls(
|
|
97
|
+
sourceFile,
|
|
98
|
+
wiringReference?.declaration &&
|
|
99
|
+
ts.isVariableDeclaration(wiringReference.declaration) &&
|
|
100
|
+
ts.isIdentifier(wiringReference.declaration.name)
|
|
101
|
+
? wiringReference.declaration.name.text
|
|
102
|
+
: wiringReference?.exportName,
|
|
103
|
+
);
|
|
104
|
+
if (
|
|
105
|
+
definitions.indeterminate ||
|
|
106
|
+
(sourceIndex && definitions.calls.length === 0)
|
|
107
|
+
)
|
|
108
|
+
result.indeterminate = true;
|
|
72
109
|
|
|
73
110
|
for (const call of definitions.calls) {
|
|
74
111
|
const objectArgument = call.arguments
|
|
@@ -124,7 +161,13 @@ export async function analyzePortWiring(
|
|
|
124
161
|
if (inferredAppPortsName) {
|
|
125
162
|
if (
|
|
126
163
|
!definitions.indeterminate &&
|
|
127
|
-
|
|
164
|
+
(sourceIndex
|
|
165
|
+
? Boolean(
|
|
166
|
+
inferredAppPortsReference?.declaration &&
|
|
167
|
+
inferredAppPortsReference.declaration ===
|
|
168
|
+
wiringReference?.declaration,
|
|
169
|
+
)
|
|
170
|
+
: definitions.name === inferredAppPortsName)
|
|
128
171
|
) {
|
|
129
172
|
result.appPortsInferredFromWiring = true;
|
|
130
173
|
for (const name of result.bound) result.declared.add(name);
|
|
@@ -137,7 +180,10 @@ export async function analyzePortWiring(
|
|
|
137
180
|
return result;
|
|
138
181
|
}
|
|
139
182
|
|
|
140
|
-
function portDefinitionCalls(
|
|
183
|
+
function portDefinitionCalls(
|
|
184
|
+
sourceFile: ts.SourceFile,
|
|
185
|
+
preferredName?: string,
|
|
186
|
+
): {
|
|
141
187
|
calls: ts.CallExpression[];
|
|
142
188
|
name?: string;
|
|
143
189
|
indeterminate: boolean;
|
|
@@ -172,8 +218,12 @@ function portDefinitionCalls(sourceFile: ts.SourceFile): {
|
|
|
172
218
|
}
|
|
173
219
|
|
|
174
220
|
const canonical = candidates.filter(
|
|
175
|
-
(candidate) =>
|
|
221
|
+
(candidate) =>
|
|
222
|
+
candidate.name === (preferredName ?? "initialPorts") &&
|
|
223
|
+
candidate.exported,
|
|
176
224
|
);
|
|
225
|
+
if (preferredName && canonical.length === 0)
|
|
226
|
+
return { calls: [], indeterminate: true };
|
|
177
227
|
const exported = candidates.filter((candidate) => candidate.exported);
|
|
178
228
|
const selected =
|
|
179
229
|
canonical.length > 0
|
|
@@ -230,6 +280,12 @@ export async function analyzePortWiringFiles(options: {
|
|
|
230
280
|
files: readonly string[];
|
|
231
281
|
config: ResolvedBeignetConfig;
|
|
232
282
|
}): Promise<PortWiringAnalysis> {
|
|
283
|
+
if (options.config.workspace.packages.length > 0) {
|
|
284
|
+
const workspace = await createAnalysisWorkspace(options.targetDir, {
|
|
285
|
+
config: options.config,
|
|
286
|
+
});
|
|
287
|
+
return analyzePortWiring(workspace, createSourceIndex(workspace));
|
|
288
|
+
}
|
|
233
289
|
const sourceFiles = new Map<string, ts.SourceFile>();
|
|
234
290
|
return analyzePortWiring({
|
|
235
291
|
config: options.config,
|
|
@@ -53,6 +53,12 @@ export type SourceIndex = {
|
|
|
53
53
|
resolveExpression(
|
|
54
54
|
file: string,
|
|
55
55
|
expression: ts.Expression,
|
|
56
|
+
locals?: ReadonlyMap<string, ts.Declaration>,
|
|
57
|
+
): Promise<ResolvedSourceReference | undefined>;
|
|
58
|
+
resolveValue(
|
|
59
|
+
file: string,
|
|
60
|
+
expression: ts.Expression,
|
|
61
|
+
locals?: ReadonlyMap<string, ts.Declaration>,
|
|
56
62
|
): Promise<ResolvedSourceReference | undefined>;
|
|
57
63
|
resolveExport(
|
|
58
64
|
file: string,
|
|
@@ -131,7 +137,6 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
|
|
|
131
137
|
ts.isNamedExports(statement.exportClause)
|
|
132
138
|
) {
|
|
133
139
|
for (const element of statement.exportClause.elements) {
|
|
134
|
-
if (statement.isTypeOnly || element.isTypeOnly) continue;
|
|
135
140
|
index.localExports.set(
|
|
136
141
|
element.name.text,
|
|
137
142
|
element.propertyName?.text ?? element.name.text,
|
|
@@ -208,6 +213,9 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
|
|
|
208
213
|
if (localDeclaration) {
|
|
209
214
|
return { file, exportName, declaration: localDeclaration };
|
|
210
215
|
}
|
|
216
|
+
const imported = localName ? index.imports.get(localName) : undefined;
|
|
217
|
+
if (imported?.kind === "named")
|
|
218
|
+
return resolveExport(imported.file, imported.exportName, visited);
|
|
211
219
|
|
|
212
220
|
const reexport = index.reexports.get(exportName);
|
|
213
221
|
if (reexport) {
|
|
@@ -227,7 +235,7 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
|
|
|
227
235
|
return undefined;
|
|
228
236
|
};
|
|
229
237
|
|
|
230
|
-
|
|
238
|
+
const sourceIndex: SourceIndex = {
|
|
231
239
|
async resolveName(file, name) {
|
|
232
240
|
const index = await readIndex(file);
|
|
233
241
|
const local = index.locals.get(name);
|
|
@@ -244,11 +252,12 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
|
|
|
244
252
|
}
|
|
245
253
|
);
|
|
246
254
|
},
|
|
247
|
-
async resolveExpression(file, rawExpression) {
|
|
255
|
+
async resolveExpression(file, rawExpression, locals) {
|
|
248
256
|
const expression = unwrapExpression(rawExpression);
|
|
249
257
|
const index = await readIndex(file);
|
|
250
258
|
if (ts.isIdentifier(expression)) {
|
|
251
|
-
const local =
|
|
259
|
+
const local =
|
|
260
|
+
locals?.get(expression.text) ?? index.locals.get(expression.text);
|
|
252
261
|
if (local) {
|
|
253
262
|
return { file, exportName: expression.text, declaration: local };
|
|
254
263
|
}
|
|
@@ -267,6 +276,11 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
|
|
|
267
276
|
ts.isPropertyAccessExpression(expression) &&
|
|
268
277
|
ts.isIdentifier(expression.expression)
|
|
269
278
|
) {
|
|
279
|
+
if (
|
|
280
|
+
locals?.has(expression.expression.text) ||
|
|
281
|
+
index.locals.has(expression.expression.text)
|
|
282
|
+
)
|
|
283
|
+
return undefined;
|
|
270
284
|
const binding = index.imports.get(expression.expression.text);
|
|
271
285
|
if (binding?.kind !== "namespace") return undefined;
|
|
272
286
|
return (
|
|
@@ -279,6 +293,43 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
|
|
|
279
293
|
|
|
280
294
|
return undefined;
|
|
281
295
|
},
|
|
296
|
+
async resolveValue(file, expression, locals) {
|
|
297
|
+
const visited = new Set<ts.Declaration>();
|
|
298
|
+
async function follow(
|
|
299
|
+
file: string,
|
|
300
|
+
rawExpression: ts.Expression,
|
|
301
|
+
locals?: ReadonlyMap<string, ts.Declaration>,
|
|
302
|
+
): Promise<ResolvedSourceReference | undefined> {
|
|
303
|
+
const expression = unwrapExpression(rawExpression);
|
|
304
|
+
const reference = await sourceIndex.resolveExpression(
|
|
305
|
+
file,
|
|
306
|
+
expression,
|
|
307
|
+
locals,
|
|
308
|
+
);
|
|
309
|
+
const declaration = reference?.declaration;
|
|
310
|
+
if (!reference || !declaration || visited.has(declaration))
|
|
311
|
+
return undefined;
|
|
312
|
+
visited.add(declaration);
|
|
313
|
+
if (!ts.isVariableDeclaration(declaration) || !declaration.initializer)
|
|
314
|
+
return reference;
|
|
315
|
+
if (
|
|
316
|
+
!ts.isVariableDeclarationList(declaration.parent) ||
|
|
317
|
+
!(declaration.parent.flags & ts.NodeFlags.Const)
|
|
318
|
+
)
|
|
319
|
+
return undefined;
|
|
320
|
+
const initializer = unwrapExpression(declaration.initializer);
|
|
321
|
+
if (
|
|
322
|
+
!ts.isIdentifier(initializer) &&
|
|
323
|
+
!ts.isPropertyAccessExpression(initializer)
|
|
324
|
+
)
|
|
325
|
+
return reference;
|
|
326
|
+
const local =
|
|
327
|
+
ts.isIdentifier(expression) &&
|
|
328
|
+
locals?.get(expression.text) === declaration;
|
|
329
|
+
return follow(reference.file, initializer, local ? locals : undefined);
|
|
330
|
+
}
|
|
331
|
+
return follow(file, expression, locals);
|
|
332
|
+
},
|
|
282
333
|
resolveExport(file, exportName) {
|
|
283
334
|
return resolveExport(file, exportName);
|
|
284
335
|
},
|
|
@@ -295,6 +346,7 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
|
|
|
295
346
|
};
|
|
296
347
|
},
|
|
297
348
|
};
|
|
349
|
+
return sourceIndex;
|
|
298
350
|
}
|
|
299
351
|
|
|
300
352
|
export function exportedVariableDeclarations(
|