@orkestrel/scaffold 0.0.4 → 0.0.6
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/bin/scaffold.js +57 -22
- package/dist/bin/scaffold.js.map +1 -1
- package/dist/host/CLAUDE.md +18 -4
- package/dist/host/claude/agents/codex.md +16 -0
- package/dist/host/claude/rules/workspace.md +8 -8
- package/dist/host/guides/src/scaffold.md +144 -42
- package/dist/host/scripts/codex.sh +5 -0
- package/dist/host/tests/setupPolicy.ts +178 -4
- package/dist/src/core/index.cjs +285 -129
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +109 -25
- package/dist/src/core/index.d.ts +109 -25
- package/dist/src/core/index.js +283 -130
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +11 -5
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +4 -2
- package/dist/src/server/index.d.ts +4 -2
- package/dist/src/server/index.js +12 -6
- package/dist/src/server/index.js.map +1 -1
- package/package.json +5 -5
|
@@ -55,6 +55,42 @@ export const DATA_SOURCE_FILES: readonly string[] = Object.freeze([
|
|
|
55
55
|
'validators.ts',
|
|
56
56
|
])
|
|
57
57
|
|
|
58
|
+
/** Worker-only value globals that WebWorker typing must not expose to core implementations. */
|
|
59
|
+
export const WORKER_SCOPE_VALUE_GLOBALS: readonly string[] = Object.freeze([
|
|
60
|
+
'name',
|
|
61
|
+
'onrtctransform',
|
|
62
|
+
'close',
|
|
63
|
+
'postMessage',
|
|
64
|
+
'dispatchEvent',
|
|
65
|
+
'location',
|
|
66
|
+
'onerror',
|
|
67
|
+
'onlanguagechange',
|
|
68
|
+
'onoffline',
|
|
69
|
+
'ononline',
|
|
70
|
+
'onrejectionhandled',
|
|
71
|
+
'onunhandledrejection',
|
|
72
|
+
'self',
|
|
73
|
+
'importScripts',
|
|
74
|
+
'fonts',
|
|
75
|
+
'caches',
|
|
76
|
+
'crossOriginIsolated',
|
|
77
|
+
'indexedDB',
|
|
78
|
+
'isSecureContext',
|
|
79
|
+
'origin',
|
|
80
|
+
'scheduler',
|
|
81
|
+
'createImageBitmap',
|
|
82
|
+
'reportError',
|
|
83
|
+
'cancelAnimationFrame',
|
|
84
|
+
'requestAnimationFrame',
|
|
85
|
+
'onmessage',
|
|
86
|
+
'onmessageerror',
|
|
87
|
+
'addEventListener',
|
|
88
|
+
'removeEventListener',
|
|
89
|
+
])
|
|
90
|
+
|
|
91
|
+
/** Virtual source text used while binding one policy-inspected module. */
|
|
92
|
+
export const POLICY_SOURCE_TEXTS: Map<string, string> = new Map()
|
|
93
|
+
|
|
58
94
|
/** One script block extracted from a Vue SFC by the official compiler. */
|
|
59
95
|
export interface VueScriptBlockInterface {
|
|
60
96
|
readonly content: string
|
|
@@ -162,6 +198,126 @@ export function hasAllowedTripleSlashReference(path: string, source: ts.SourceFi
|
|
|
162
198
|
)
|
|
163
199
|
}
|
|
164
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Whether the policy compiler can read one source path.
|
|
203
|
+
*
|
|
204
|
+
* @param path - The source path to inspect
|
|
205
|
+
* @returns `true` when the virtual or physical source exists
|
|
206
|
+
*/
|
|
207
|
+
export function hasPolicySource(path: string): boolean {
|
|
208
|
+
return POLICY_SOURCE_TEXTS.has(path) || ts.sys.fileExists(path)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Read one virtual or physical policy source.
|
|
213
|
+
*
|
|
214
|
+
* @param path - The source path to read
|
|
215
|
+
* @returns The source text when present
|
|
216
|
+
*/
|
|
217
|
+
export function readPolicySource(path: string): string | undefined {
|
|
218
|
+
return POLICY_SOURCE_TEXTS.get(path) ?? ts.sys.readFile(path)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Parse one virtual or physical policy source for the compiler host.
|
|
223
|
+
*
|
|
224
|
+
* @param path - The source path to parse
|
|
225
|
+
* @param language - The requested TypeScript language target
|
|
226
|
+
* @returns The parsed source file when present
|
|
227
|
+
*/
|
|
228
|
+
export function createPolicySource(
|
|
229
|
+
path: string,
|
|
230
|
+
language: ts.ScriptTarget | ts.CreateSourceFileOptions,
|
|
231
|
+
): ts.SourceFile | undefined {
|
|
232
|
+
const content = readPolicySource(path)
|
|
233
|
+
return content === undefined ? undefined : ts.createSourceFile(path, content, language, true)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Bind one policy-inspected module without loading ambient host declarations.
|
|
238
|
+
*
|
|
239
|
+
* @param path - The source path used in diagnostics
|
|
240
|
+
* @param content - The TypeScript source text to bind
|
|
241
|
+
* @returns A one-file TypeScript program whose checker resolves lexical bindings
|
|
242
|
+
*/
|
|
243
|
+
export function createPolicyProgram(path: string, content: string): ts.Program {
|
|
244
|
+
const options: ts.CompilerOptions = {
|
|
245
|
+
allowJs: true,
|
|
246
|
+
noLib: true,
|
|
247
|
+
noResolve: true,
|
|
248
|
+
target: ts.ScriptTarget.Latest,
|
|
249
|
+
types: [],
|
|
250
|
+
}
|
|
251
|
+
POLICY_SOURCE_TEXTS.set(path, content)
|
|
252
|
+
const host = ts.createCompilerHost(options)
|
|
253
|
+
host.fileExists = hasPolicySource
|
|
254
|
+
host.readFile = readPolicySource
|
|
255
|
+
host.getSourceFile = createPolicySource
|
|
256
|
+
const program = ts.createProgram([path], options, host)
|
|
257
|
+
POLICY_SOURCE_TEXTS.delete(path)
|
|
258
|
+
return program
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Whether an identifier is a standalone runtime value reference.
|
|
263
|
+
*
|
|
264
|
+
* @param node - The identifier occurrence to classify
|
|
265
|
+
* @param checker - The binder used to distinguish lexical values from ambient globals
|
|
266
|
+
* @returns `true` only when the occurrence reads or writes a runtime value
|
|
267
|
+
*/
|
|
268
|
+
export function isValueReferenceIdentifier(node: ts.Identifier, checker: ts.TypeChecker): boolean {
|
|
269
|
+
if (ts.isPartOfTypeNode(node)) return false
|
|
270
|
+
|
|
271
|
+
let ancestor: ts.Node = node.parent
|
|
272
|
+
while (!ts.isSourceFile(ancestor) && !ts.isStatement(ancestor)) {
|
|
273
|
+
if (ts.isTypeQueryNode(ancestor)) return false
|
|
274
|
+
if (ts.isComputedPropertyName(ancestor) && ts.isTypeElement(ancestor.parent)) return false
|
|
275
|
+
ancestor = ancestor.parent
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const parent = node.parent
|
|
279
|
+
if (
|
|
280
|
+
(ts.isPropertyAccessExpression(parent) && parent.name === node) ||
|
|
281
|
+
(ts.isPropertyAssignment(parent) && parent.name === node) ||
|
|
282
|
+
(ts.isPropertyDeclaration(parent) && parent.name === node) ||
|
|
283
|
+
(ts.isPropertySignature(parent) && parent.name === node) ||
|
|
284
|
+
(ts.isMethodDeclaration(parent) && parent.name === node) ||
|
|
285
|
+
(ts.isMethodSignature(parent) && parent.name === node) ||
|
|
286
|
+
(ts.isGetAccessorDeclaration(parent) && parent.name === node) ||
|
|
287
|
+
(ts.isSetAccessorDeclaration(parent) && parent.name === node) ||
|
|
288
|
+
(ts.isVariableDeclaration(parent) && parent.name === node) ||
|
|
289
|
+
(ts.isParameter(parent) && parent.name === node) ||
|
|
290
|
+
(ts.isBindingElement(parent) && (parent.name === node || parent.propertyName === node)) ||
|
|
291
|
+
(ts.isFunctionDeclaration(parent) && parent.name === node) ||
|
|
292
|
+
(ts.isFunctionExpression(parent) && parent.name === node) ||
|
|
293
|
+
(ts.isClassDeclaration(parent) && parent.name === node) ||
|
|
294
|
+
(ts.isClassExpression(parent) && parent.name === node) ||
|
|
295
|
+
(ts.isInterfaceDeclaration(parent) && parent.name === node) ||
|
|
296
|
+
(ts.isTypeAliasDeclaration(parent) && parent.name === node) ||
|
|
297
|
+
(ts.isTypeParameterDeclaration(parent) && parent.name === node) ||
|
|
298
|
+
(ts.isEnumDeclaration(parent) && parent.name === node) ||
|
|
299
|
+
(ts.isEnumMember(parent) && parent.name === node) ||
|
|
300
|
+
(ts.isModuleDeclaration(parent) && parent.name === node) ||
|
|
301
|
+
ts.isImportClause(parent) ||
|
|
302
|
+
ts.isImportSpecifier(parent) ||
|
|
303
|
+
ts.isNamespaceImport(parent) ||
|
|
304
|
+
ts.isImportEqualsDeclaration(parent) ||
|
|
305
|
+
ts.isExportSpecifier(parent) ||
|
|
306
|
+
ts.isNamespaceExport(parent) ||
|
|
307
|
+
ts.isNamespaceExportDeclaration(parent) ||
|
|
308
|
+
(ts.isLabeledStatement(parent) && parent.label === node) ||
|
|
309
|
+
(ts.isBreakOrContinueStatement(parent) && parent.label === node) ||
|
|
310
|
+
(ts.isJsxAttribute(parent) && parent.name === node)
|
|
311
|
+
) {
|
|
312
|
+
return false
|
|
313
|
+
}
|
|
314
|
+
return (
|
|
315
|
+
(ts.isShorthandPropertyAssignment(parent)
|
|
316
|
+
? checker.getShorthandAssignmentValueSymbol(parent)
|
|
317
|
+
: checker.getSymbolAtLocation(node)) === undefined
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
|
|
165
321
|
/** Inspect a Vue single-file component for syntax that can bypass declared import policy. */
|
|
166
322
|
export function inspectVueCodingLaw(
|
|
167
323
|
path: string,
|
|
@@ -187,7 +343,22 @@ export function inspectVueCodingLaw(
|
|
|
187
343
|
}
|
|
188
344
|
|
|
189
345
|
/** Add syntax-wide coding-law violations while traversing one source tree. */
|
|
190
|
-
export function inspectCodingNode(
|
|
346
|
+
export function inspectCodingNode(
|
|
347
|
+
path: string,
|
|
348
|
+
node: ts.Node,
|
|
349
|
+
violations: string[],
|
|
350
|
+
checker: ts.TypeChecker,
|
|
351
|
+
): void {
|
|
352
|
+
if (
|
|
353
|
+
/^(?:app|src)[\\/]core[\\/]/u.test(path) &&
|
|
354
|
+
ts.isIdentifier(node) &&
|
|
355
|
+
WORKER_SCOPE_VALUE_GLOBALS.includes(node.text) &&
|
|
356
|
+
isValueReferenceIdentifier(node, checker)
|
|
357
|
+
) {
|
|
358
|
+
violations.push(
|
|
359
|
+
`${path}:${formatPolicyPosition(node)} forbids worker-scope global ${node.text} in core`,
|
|
360
|
+
)
|
|
361
|
+
}
|
|
191
362
|
if (
|
|
192
363
|
ts.isAsExpression(node) ||
|
|
193
364
|
ts.isTypeAssertionExpression(node) ||
|
|
@@ -242,13 +413,16 @@ export function inspectCodingNode(path: string, node: ts.Node, violations: strin
|
|
|
242
413
|
) {
|
|
243
414
|
violations.push(`${path}:${formatPolicyPosition(node)} forbids hidden function assignments`)
|
|
244
415
|
}
|
|
245
|
-
ts.forEachChild(node, (child) => inspectCodingNode(path, child, violations))
|
|
416
|
+
ts.forEachChild(node, (child) => inspectCodingNode(path, child, violations, checker))
|
|
246
417
|
}
|
|
247
418
|
|
|
248
419
|
/** Inspect one TypeScript source module for repository coding-law violations. */
|
|
249
420
|
export function inspectCodingLaw(path: string, content: string): readonly string[] {
|
|
250
421
|
const violations: string[] = []
|
|
251
|
-
const
|
|
422
|
+
const program = createPolicyProgram(path, content)
|
|
423
|
+
const source = program.getSourceFile(path)
|
|
424
|
+
if (source === undefined) throw new Error(`Policy source was not bound at ${path}`)
|
|
425
|
+
const checker = program.getTypeChecker()
|
|
252
426
|
const file = basename(path)
|
|
253
427
|
|
|
254
428
|
if (/\.[cm]?jsx?$/u.test(path)) {
|
|
@@ -362,7 +536,7 @@ export function inspectCodingLaw(path: string, content: string): readonly string
|
|
|
362
536
|
}
|
|
363
537
|
}
|
|
364
538
|
|
|
365
|
-
inspectCodingNode(path, source, violations)
|
|
539
|
+
inspectCodingNode(path, source, violations, checker)
|
|
366
540
|
return violations
|
|
367
541
|
}
|
|
368
542
|
|