@prestack/thread 0.1.20 → 0.1.22
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 +193 -0
- package/dist/core/attach.js +21 -0
- package/dist/core/descend.d.ts +2 -0
- package/dist/core/descend.js +18 -3
- package/dist/core/generators/commit.js +55 -3
- package/dist/core/generators/dev.d.ts +1 -0
- package/dist/core/generators/dev.js +68 -0
- package/dist/core/rows.d.ts +5 -0
- package/dist/core/rows.js +45 -0
- package/dist/core/tree.d.ts +1 -0
- package/dist/core/tree.js +7 -1
- package/dist/derivation/generators/build.d.ts +3 -1
- package/dist/derivation/generators/build.js +103 -22
- package/dist/derivation/generators/release.js +74 -26
- package/dist/derivation/release.js +3 -3
- package/dist/integrations/cli.d.ts +3 -9
- package/dist/integrations/cli.js +96 -36
- package/dist/integrations/declared.d.ts +8 -0
- package/dist/integrations/declared.js +22 -0
- package/dist/integrations/pglite.js +13 -3
- package/dist/integrations/target.d.ts +4 -1
- package/dist/integrations/target.js +20 -6
- package/dist/integrity/attach.js +1 -5
- package/dist/integrity/generators/publish.js +2 -1
- package/dist/integrity/slopnators/sl_stale_path.js +10 -0
- package/dist/integrity/slopnators/sl_unlisted_program.js +2 -2
- package/dist/operations/access.js +3 -2
- package/dist/operations/capabilities.d.ts +2 -0
- package/dist/operations/capabilities.js +2 -0
- package/dist/operations/generators/capability.js +234 -24
- package/dist/operations/generators/compile.d.ts +21 -0
- package/dist/operations/generators/compile.js +151 -51
- package/dist/operations/generators/permits.d.ts +2 -1
- package/dist/operations/generators/permits.js +52 -47
- package/dist/operations/generators/seed.d.ts +3 -0
- package/dist/operations/generators/seed.js +25 -3
- package/dist/operations/nodes.d.ts +2 -2
- package/package.json +1 -1
- package/dist/core/actor.ts +0 -21
- package/dist/core/attach.ts +0 -613
- package/dist/core/chain.ts +0 -145
- package/dist/core/condition.ts +0 -227
- package/dist/core/descend.ts +0 -1173
- package/dist/core/errors/catalogue.ts +0 -314
- package/dist/core/errors/classes.ts +0 -51
- package/dist/core/errors/error.ts +0 -152
- package/dist/core/errors/namespaces.ts +0 -19
- package/dist/core/finding.ts +0 -37
- package/dist/core/generators/commit.ts +0 -157
- package/dist/core/init.ts +0 -222
- package/dist/core/invoked.ts +0 -52
- package/dist/core/round.ts +0 -74
- package/dist/core/rows.ts +0 -459
- package/dist/core/scope.ts +0 -20
- package/dist/core/thread.ts +0 -29
- package/dist/core/tree.ts +0 -90
- package/dist/core/types.ts +0 -351
- package/dist/derivation/attach.ts +0 -57
- package/dist/derivation/generators/build.ts +0 -371
- package/dist/derivation/generators/check.ts +0 -118
- package/dist/derivation/generators/determinism.ts +0 -145
- package/dist/derivation/generators/release.ts +0 -236
- package/dist/derivation/release.ts +0 -43
- package/dist/integrations/attach.ts +0 -187
- package/dist/integrations/cli.ts +0 -615
- package/dist/integrations/disk.ts +0 -466
- package/dist/integrations/generators/read.ts +0 -127
- package/dist/integrations/generators/vault.ts +0 -210
- package/dist/integrations/https.ts +0 -216
- package/dist/integrations/pglite.ts +0 -206
- package/dist/integrations/secret.ts +0 -54
- package/dist/integrations/target.ts +0 -656
- package/dist/integrations/vault.ts +0 -148
- package/dist/integrity/attach.ts +0 -150
- package/dist/integrity/check-threads.ts +0 -166
- package/dist/integrity/constraint.ts +0 -1246
- package/dist/integrity/generators/compiler.ts +0 -138
- package/dist/integrity/generators/gate.ts +0 -227
- package/dist/integrity/generators/issues.ts +0 -160
- package/dist/integrity/generators/publish.ts +0 -232
- package/dist/integrity/generators/trial.ts +0 -359
- package/dist/integrity/lint.ts +0 -214
- package/dist/integrity/lsp/bin.ts +0 -44
- package/dist/integrity/lsp/diagnostics.ts +0 -92
- package/dist/integrity/lsp/findings.ts +0 -63
- package/dist/integrity/lsp/lint.ts +0 -124
- package/dist/integrity/lsp/protocol.ts +0 -78
- package/dist/integrity/lsp/server.ts +0 -224
- package/dist/integrity/slopnator.ts +0 -324
- package/dist/integrity/slopnators/sl_broken_lineage.ts +0 -38
- package/dist/integrity/slopnators/sl_declared_elsewhere.ts +0 -60
- package/dist/integrity/slopnators/sl_derived_drift.ts +0 -101
- package/dist/integrity/slopnators/sl_door_in_a_node.ts +0 -250
- package/dist/integrity/slopnators/sl_hardcoded_style.ts +0 -55
- package/dist/integrity/slopnators/sl_inert_island.ts +0 -84
- package/dist/integrity/slopnators/sl_inert_slopnator.ts +0 -93
- package/dist/integrity/slopnators/sl_missing_row.ts +0 -140
- package/dist/integrity/slopnators/sl_no_barrel.ts +0 -48
- package/dist/integrity/slopnators/sl_no_child_process.ts +0 -100
- package/dist/integrity/slopnators/sl_no_comment.ts +0 -43
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +0 -276
- package/dist/integrity/slopnators/sl_no_import_cycle.ts +0 -66
- package/dist/integrity/slopnators/sl_no_such_command.ts +0 -81
- package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +0 -63
- package/dist/integrity/slopnators/sl_outside_an_operation.ts +0 -25
- package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +0 -70
- package/dist/integrity/slopnators/sl_secret_literal.ts +0 -68
- package/dist/integrity/slopnators/sl_stale_path.ts +0 -178
- package/dist/integrity/slopnators/sl_twice_meant.ts +0 -57
- package/dist/integrity/slopnators/sl_twice_parented.ts +0 -82
- package/dist/integrity/slopnators/sl_undeclared_crossing.ts +0 -481
- package/dist/integrity/slopnators/sl_undeclared_destination.ts +0 -113
- package/dist/integrity/slopnators/sl_undeclared_element.ts +0 -44
- package/dist/integrity/slopnators/sl_undeclared_list.ts +0 -358
- package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -75
- package/dist/integrity/slopnators/sl_undeclared_operation.ts +0 -257
- package/dist/integrity/slopnators/sl_undeclared_variant.ts +0 -152
- package/dist/integrity/slopnators/sl_undeclared_webhook.ts +0 -108
- package/dist/integrity/slopnators/sl_underived_route.ts +0 -72
- package/dist/integrity/slopnators/sl_undescribed.ts +0 -65
- package/dist/integrity/slopnators/sl_ungoverned_island.ts +0 -60
- package/dist/integrity/slopnators/sl_unlisted_program.ts +0 -46
- package/dist/integrity/slopnators/sl_unnamed_island.ts +0 -41
- package/dist/integrity/slopnators/sl_unpaired_member.ts +0 -139
- package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +0 -71
- package/dist/integrity/slopnators/sl_unprefixed_member.ts +0 -110
- package/dist/integrity/slopnators/sl_unthreaded.ts +0 -421
- package/dist/integrity/slopnators/sl_untranslated_key.ts +0 -140
- package/dist/integrity/slopnators/sl_unvaulted_secret.ts +0 -132
- package/dist/integrity/slopnators/sl_unversioned.ts +0 -52
- package/dist/integrity/sweep.ts +0 -395
- package/dist/knowledge/attach.ts +0 -327
- package/dist/knowledge/generators/docsgen.ts +0 -186
- package/dist/knowledge/generators/glossary.ts +0 -73
- package/dist/knowledge/glossary.ts +0 -6
- package/dist/operations/access.ts +0 -131
- package/dist/operations/attach.ts +0 -408
- package/dist/operations/capabilities.ts +0 -88
- package/dist/operations/dispatch.ts +0 -294
- package/dist/operations/error-results.ts +0 -31
- package/dist/operations/generators/capability.ts +0 -1054
- package/dist/operations/generators/compile.ts +0 -701
- package/dist/operations/generators/permits.ts +0 -224
- package/dist/operations/generators/seed.ts +0 -359
- package/dist/operations/generators/trial.ts +0 -666
- package/dist/operations/nodes.ts +0 -53
- package/dist/operations/operations.ts +0 -91
- package/dist/operations/result.ts +0 -28
package/dist/core/rows.d.ts
CHANGED
|
@@ -29,4 +29,9 @@ export declare function withTopField(text: string, field: string, value: string)
|
|
|
29
29
|
export declare function withoutField(text: string, where: Bounds, id: string, field: string): string;
|
|
30
30
|
export declare function withoutRow(text: string, where: Bounds, id: string): string;
|
|
31
31
|
export declare function marked(parts: readonly string[]): string;
|
|
32
|
+
export declare const markOf: (row: object) => string;
|
|
33
|
+
export declare function literalIn(text: string): unknown;
|
|
34
|
+
export declare const unreadable: (held: unknown) => boolean;
|
|
35
|
+
export declare function written(held: unknown, pad: string): string;
|
|
36
|
+
export declare function withWritten(text: string, where: Bounds, id: string, field: string, held: unknown): string | undefined;
|
|
32
37
|
export declare function showing(row: object, only?: readonly string[]): string;
|
package/dist/core/rows.js
CHANGED
|
@@ -432,6 +432,51 @@ const lines = (held, pad) => {
|
|
|
432
432
|
export function marked(parts) {
|
|
433
433
|
return createHash('sha256').update(parts.join('\n')).digest('hex').slice(0, 6);
|
|
434
434
|
}
|
|
435
|
+
export const markOf = (row) => {
|
|
436
|
+
const held = row;
|
|
437
|
+
return marked([
|
|
438
|
+
typeof held.id === 'string' ? held.id : '',
|
|
439
|
+
typeof held.at === 'string' ? held.at : '',
|
|
440
|
+
typeof held.describes === 'string' ? held.describes : '',
|
|
441
|
+
held.version === undefined ? '' : String(held.version),
|
|
442
|
+
]);
|
|
443
|
+
};
|
|
444
|
+
const WHOLE = /^-?[0-9][0-9_]*(?:\.[0-9]+)?$/;
|
|
445
|
+
export function literalIn(text) {
|
|
446
|
+
const said = text.trim();
|
|
447
|
+
if (WHOLE.test(said))
|
|
448
|
+
return Number(said.split('_').join(''));
|
|
449
|
+
const held = taken(`const held = ${said}`);
|
|
450
|
+
return held.length === 1 && held[0]?.[1] !== UNREAD ? held[0]?.[1] : UNREAD;
|
|
451
|
+
}
|
|
452
|
+
export const unreadable = (held) => held === UNREAD;
|
|
453
|
+
const BARE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
454
|
+
function inline(held) {
|
|
455
|
+
if (typeof held === 'string')
|
|
456
|
+
return quoted(held);
|
|
457
|
+
if (Array.isArray(held))
|
|
458
|
+
return `[${held.map(inline).join(', ')}]`;
|
|
459
|
+
if (held !== null && typeof held === 'object') {
|
|
460
|
+
const pairs = Object.entries(held).map(([key, one]) => `${BARE.test(key) ? key : quoted(key)}: ${inline(one)}`);
|
|
461
|
+
return pairs.length === 0 ? '{}' : `{ ${pairs.join(', ')} }`;
|
|
462
|
+
}
|
|
463
|
+
return String(held);
|
|
464
|
+
}
|
|
465
|
+
export function written(held, pad) {
|
|
466
|
+
if (!Array.isArray(held) || held.every((one) => typeof one !== 'object' || one === null))
|
|
467
|
+
return inline(held);
|
|
468
|
+
return ['[', ...held.map((one) => `${pad} ${inline(one)},`), `${pad}]`].join('\n');
|
|
469
|
+
}
|
|
470
|
+
export function withWritten(text, where, id, field, held) {
|
|
471
|
+
const row = rowAt(text, where, id);
|
|
472
|
+
if (row === undefined)
|
|
473
|
+
return undefined;
|
|
474
|
+
const named = row.properties.find((one) => ts.isPropertyAssignment(one) && keyed(one) === field) ?? row.properties.at(-1);
|
|
475
|
+
if (named === undefined)
|
|
476
|
+
return undefined;
|
|
477
|
+
const line = text.lastIndexOf('\n', named.getStart()) + 1;
|
|
478
|
+
return fieldOn(text, row, field, written(held, /^[ \t]*/.exec(text.slice(line))?.[0] ?? ''));
|
|
479
|
+
}
|
|
435
480
|
export function showing(row, only = []) {
|
|
436
481
|
const held = row;
|
|
437
482
|
const keys = Object.keys(held);
|
package/dist/core/tree.d.ts
CHANGED
|
@@ -11,5 +11,6 @@ export declare class UnthreadedRepositoryError extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
export declare function folder(): string;
|
|
13
13
|
export declare function findTree(from?: string): Tree;
|
|
14
|
+
export declare function declaresUi(tree: Tree): boolean;
|
|
14
15
|
export declare function strayFiles(root: string, prefix: string, ends: string): readonly string[];
|
|
15
16
|
export declare function inTree(tree: Tree, file: string): string;
|
package/dist/core/tree.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { FOLDER, attached, existsSync, readdirSync, statSync } from '../integrations/disk.js';
|
|
1
|
+
import { FOLDER, attached, existsSync, readFileSync, readdirSync, statSync } from '../integrations/disk.js';
|
|
2
2
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
3
3
|
import { cwd, env } from 'node:process';
|
|
4
|
+
import { holdsRow, listBounds } from './rows.js';
|
|
4
5
|
export { FOLDER };
|
|
5
6
|
const NAMES = ['_threads', 'threads'];
|
|
6
7
|
const ROOT = 'thread.ts';
|
|
@@ -44,6 +45,11 @@ export function findTree(from = folder()) {
|
|
|
44
45
|
at = up;
|
|
45
46
|
}
|
|
46
47
|
}
|
|
48
|
+
export function declaresUi(tree) {
|
|
49
|
+
const held = readFileSync(tree.root, 'utf8');
|
|
50
|
+
const declares = listBounds(held, 'declares');
|
|
51
|
+
return declares !== undefined && holdsRow(held, declares, 'ui');
|
|
52
|
+
}
|
|
47
53
|
export function strayFiles(root, prefix, ends) {
|
|
48
54
|
const skip = new Set(['node_modules', '.git', '.next', 'dist', '_generated', 'coverage']);
|
|
49
55
|
const found = [];
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type BuildOptions } from 'esbuild-wasm/esm/browser.js';
|
|
2
2
|
import { type Pass } from '../../core/descend.ts';
|
|
3
|
-
export declare function bundle(options: BuildOptions
|
|
3
|
+
export declare function bundle(options: BuildOptions & {
|
|
4
|
+
readonly keeping?: Set<string>;
|
|
5
|
+
}): Promise<string>;
|
|
4
6
|
declare function wanted(pass: Pass): readonly {
|
|
5
7
|
entry: string;
|
|
6
8
|
out: string;
|
|
@@ -12,7 +12,7 @@ import { argv, cwd, stderr, stdout } from 'node:process';
|
|
|
12
12
|
import { pathToFileURL } from 'node:url';
|
|
13
13
|
import { build, initialize, stop } from 'esbuild-wasm/esm/browser.js';
|
|
14
14
|
import { descend, reached } from '../../core/descend.js';
|
|
15
|
-
import { existsSync, readFileSync, realpathSync, statSync, writeFileSync } from '../../integrations/disk.js';
|
|
15
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from '../../integrations/disk.js';
|
|
16
16
|
import { exit, invoked } from '../../core/invoked.js';
|
|
17
17
|
import { valuesIn } from '../../core/rows.js';
|
|
18
18
|
import { findTree } from '../../core/tree.js';
|
|
@@ -83,35 +83,110 @@ function exported(held, sub, conditions) {
|
|
|
83
83
|
}
|
|
84
84
|
return undefined;
|
|
85
85
|
}
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
function named(specifier) {
|
|
87
|
+
return specifier.split('/').slice(0, specifier.startsWith('@') ? 2 : 1).join('/');
|
|
88
|
+
}
|
|
89
|
+
function rooted(name, from, top) {
|
|
89
90
|
for (let at = from; inside(top, at); at = dirname(at)) {
|
|
90
91
|
const root = join(at, 'node_modules', name);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const held = JSON.parse(readFileSync(manifest, 'utf8'));
|
|
94
|
-
if (held.exports !== undefined) {
|
|
95
|
-
const found = exported(held.exports, sub, conditions);
|
|
96
|
-
return found === undefined ? undefined : located(join(root, found));
|
|
97
|
-
}
|
|
98
|
-
const main = typeof held.main === 'string' ? held.main : typeof held.module === 'string' ? held.module : 'index.js';
|
|
99
|
-
return located(join(root, sub === '.' ? main : sub));
|
|
100
|
-
}
|
|
92
|
+
if (filed(join(root, 'package.json')))
|
|
93
|
+
return root;
|
|
101
94
|
if (at === top)
|
|
102
95
|
return undefined;
|
|
103
96
|
}
|
|
104
97
|
return undefined;
|
|
105
98
|
}
|
|
106
|
-
|
|
99
|
+
function packaged(specifier, from, conditions, top) {
|
|
100
|
+
const name = named(specifier);
|
|
101
|
+
const sub = `.${specifier.slice(name.length)}`;
|
|
102
|
+
const root = rooted(name, from, top);
|
|
103
|
+
if (root === undefined)
|
|
104
|
+
return undefined;
|
|
105
|
+
const held = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
|
106
|
+
if (held.exports !== undefined) {
|
|
107
|
+
const found = exported(held.exports, sub, conditions);
|
|
108
|
+
return found === undefined ? undefined : located(join(root, found));
|
|
109
|
+
}
|
|
110
|
+
const main = typeof held.main === 'string' ? held.main : typeof held.module === 'string' ? held.module : 'index.js';
|
|
111
|
+
return located(join(root, sub === '.' ? main : sub));
|
|
112
|
+
}
|
|
113
|
+
function binary(at) {
|
|
114
|
+
const bytes = readFileSync(at, 'latin1');
|
|
115
|
+
if (bytes.length < 4)
|
|
116
|
+
return false;
|
|
117
|
+
const byte = (i) => bytes.charCodeAt(i);
|
|
118
|
+
if (byte(0) === 0x7f && bytes.slice(1, 4) === 'ELF')
|
|
119
|
+
return true;
|
|
120
|
+
if (byte(0) === 0x4d && byte(1) === 0x5a)
|
|
121
|
+
return true;
|
|
122
|
+
if (bytes.slice(0, 4) === '\0asm')
|
|
123
|
+
return true;
|
|
124
|
+
if (byte(1) === 0xfa && byte(2) === 0xed && byte(3) === 0xfe && (byte(0) === 0xce || byte(0) === 0xcf))
|
|
125
|
+
return true;
|
|
126
|
+
if (byte(0) === 0xfe && byte(1) === 0xed && byte(2) === 0xfa && (byte(3) === 0xce || byte(3) === 0xcf))
|
|
127
|
+
return true;
|
|
128
|
+
if (byte(0) === 0xca && byte(1) === 0xfe && byte(2) === 0xba && (byte(3) === 0xbe || byte(3) === 0xbf))
|
|
129
|
+
return true;
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
function owned(root) {
|
|
133
|
+
const found = [];
|
|
134
|
+
const seen = new Set();
|
|
135
|
+
const walk = (at) => {
|
|
136
|
+
const real = realpathSync(at);
|
|
137
|
+
if (seen.has(real))
|
|
138
|
+
return;
|
|
139
|
+
seen.add(real);
|
|
140
|
+
for (const one of readdirSync(at)) {
|
|
141
|
+
if (one === 'node_modules')
|
|
142
|
+
continue;
|
|
143
|
+
const path = join(at, one);
|
|
144
|
+
const held = statSync(path, { throwIfNoEntry: false });
|
|
145
|
+
if (held?.isDirectory() === true)
|
|
146
|
+
walk(path);
|
|
147
|
+
else if (held?.isFile() === true)
|
|
148
|
+
found.push(path);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
walk(root);
|
|
152
|
+
return found;
|
|
153
|
+
}
|
|
154
|
+
const staying = new Map();
|
|
155
|
+
function installed(root, top, visiting = new Set()) {
|
|
156
|
+
const cached = staying.get(root);
|
|
157
|
+
if (cached !== undefined)
|
|
158
|
+
return cached;
|
|
159
|
+
if (visiting.has(root))
|
|
160
|
+
return false;
|
|
161
|
+
visiting.add(root);
|
|
162
|
+
let decided = owned(root).some(binary);
|
|
163
|
+
if (!decided) {
|
|
164
|
+
const held = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
|
165
|
+
const names = Object.keys(held.optionalDependencies ?? {});
|
|
166
|
+
decided = names.some((one) => {
|
|
167
|
+
const found = rooted(one, root, top);
|
|
168
|
+
return found !== undefined && installed(found, top, visiting);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
visiting.delete(root);
|
|
172
|
+
staying.set(root, decided);
|
|
173
|
+
return decided;
|
|
174
|
+
}
|
|
175
|
+
const folder = (outside, top, keeping) => ({
|
|
107
176
|
name: 'thread-folder',
|
|
108
177
|
setup(held) {
|
|
109
178
|
held.onResolve({ filter: /.*/ }, (args) => {
|
|
110
179
|
const bare = !args.path.startsWith('.') && !isAbsolute(args.path);
|
|
111
180
|
if (args.path.startsWith('node:') || RUNTIME.has(args.path))
|
|
112
181
|
return { path: args.path, external: true, sideEffects: false };
|
|
113
|
-
if (bare && outside)
|
|
114
|
-
|
|
182
|
+
if (bare && outside) {
|
|
183
|
+
const name = named(args.path);
|
|
184
|
+
const root = rooted(name, args.resolveDir, top);
|
|
185
|
+
if (root !== undefined && installed(root, top)) {
|
|
186
|
+
keeping?.add(name);
|
|
187
|
+
return { path: args.path, external: true };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
115
190
|
const conditions = args.kind === 'require-call' || args.kind === 'require-resolve' ? REQUIRED : IMPORTED;
|
|
116
191
|
const path = resolve(args.resolveDir, args.path);
|
|
117
192
|
const found = bare ? packaged(args.path, args.resolveDir, conditions, top) : inside(top, path) ? located(path) : undefined;
|
|
@@ -141,13 +216,13 @@ export async function bundle(options) {
|
|
|
141
216
|
hosted.self ??= globalThis;
|
|
142
217
|
await initialize({ wasmModule: compiled, worker: false });
|
|
143
218
|
try {
|
|
144
|
-
const { packages, plugins, absWorkingDir = cwd(), ...rest } = options;
|
|
219
|
+
const { packages, plugins, keeping, absWorkingDir = cwd(), ...rest } = options;
|
|
145
220
|
const made = await build({
|
|
146
221
|
absWorkingDir,
|
|
147
222
|
...rest,
|
|
148
223
|
bundle: true,
|
|
149
224
|
write: false,
|
|
150
|
-
plugins: [...(plugins ?? []), folder(packages === 'external', absWorkingDir)],
|
|
225
|
+
plugins: [...(plugins ?? []), folder(packages === 'external', absWorkingDir, keeping)],
|
|
151
226
|
});
|
|
152
227
|
const text = made.outputFiles?.[0]?.text;
|
|
153
228
|
if (text === undefined)
|
|
@@ -323,13 +398,14 @@ async function main() {
|
|
|
323
398
|
}
|
|
324
399
|
const forBrowser = browsing(entry);
|
|
325
400
|
const refusals = forBrowser ? withheld(pass) : new Map();
|
|
401
|
+
const keeping = forBrowser ? undefined : new Set();
|
|
326
402
|
let held;
|
|
327
403
|
try {
|
|
328
404
|
held = await bundle({
|
|
329
405
|
entryPoints: [resolve(ROOT, entry)],
|
|
330
406
|
format: 'esm',
|
|
331
407
|
platform: forBrowser ? 'browser' : 'node',
|
|
332
|
-
...(
|
|
408
|
+
...(keeping === undefined ? {} : { packages: 'external', keeping }),
|
|
333
409
|
outfile: at,
|
|
334
410
|
plugins: [declared(), ...(refusals.size === 0 ? [] : [withholding(refusals)]), ...compiling],
|
|
335
411
|
logLevel: 'silent',
|
|
@@ -339,16 +415,21 @@ async function main() {
|
|
|
339
415
|
stderr.write(`build: ${out} was not written — ${thrown instanceof Error ? thrown.message : String(thrown)}\n`);
|
|
340
416
|
exit(1);
|
|
341
417
|
}
|
|
418
|
+
const installs = keeping === undefined
|
|
419
|
+
? ''
|
|
420
|
+
: keeping.size === 0
|
|
421
|
+
? ' Every package it imports is compiled in, so npm installs nothing to run it.'
|
|
422
|
+
: ` It still needs npm to install ${String(keeping.size)} package${keeping.size === 1 ? '' : 's'}: ${[...keeping].sort().join(', ')}. Each one loads a native file or starts a program that only exists once npm installs it.`;
|
|
342
423
|
if (checking) {
|
|
343
424
|
if (held !== before) {
|
|
344
425
|
stderr.write(`build: ${out} is not what ${BUILDS} writes. A built artifact has no author — run \`${BUILDS}\` and commit the result.\n`);
|
|
345
426
|
exit(1);
|
|
346
427
|
}
|
|
347
|
-
stdout.write(`build: ${out} is up to date — ${String(new Set(reached()).size)} declared file(s) reachable
|
|
428
|
+
stdout.write(`build: ${out} is up to date — ${String(new Set(reached()).size)} declared file(s) reachable.${installs}\n`);
|
|
348
429
|
continue;
|
|
349
430
|
}
|
|
350
431
|
writeFileSync(at, held, 'utf8');
|
|
351
|
-
stdout.write(`build: wrote ${out} from ${entry} — ${String(new Set(reached()).size)} declared file(s) reachable
|
|
432
|
+
stdout.write(`build: wrote ${out} from ${entry} — ${String(new Set(reached()).size)} declared file(s) reachable.${installs}\n`);
|
|
352
433
|
}
|
|
353
434
|
}
|
|
354
435
|
if (invoked(import.meta.url))
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { argv, stderr, stdout } from 'node:process';
|
|
3
|
-
import { existsSync, readFileSync, writeFileSync } from '../../integrations/disk.js';
|
|
3
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from '../../integrations/disk.js';
|
|
4
|
+
import { COPY_AT, PROGRAM } from '../../integrations/cli.js';
|
|
5
|
+
import { VAULT_DIR } from '../../integrations/vault.js';
|
|
4
6
|
import { exit, invoked } from '../../core/invoked.js';
|
|
5
|
-
import {
|
|
7
|
+
import { markOf, valuesIn } from '../../core/rows.js';
|
|
6
8
|
import { ours } from '../../core/scope.js';
|
|
7
|
-
import { findTree, inTree } from '../../core/tree.js';
|
|
9
|
+
import { declaresUi, findTree, inTree } from '../../core/tree.js';
|
|
10
|
+
import { SERVER_OUT } from '../../operations/capabilities.js';
|
|
8
11
|
import { CHECKED_BY, KINDS, LISTENS, SAID, WRITES, WRITTEN_BY, kindOf } from '../release.js';
|
|
9
12
|
const TREE = findTree();
|
|
10
13
|
const MANIFEST = 'package.json';
|
|
14
|
+
const RECEIPT = join(VAULT_DIR, 'gate.json');
|
|
15
|
+
const ROUTE = /^app\/(?:.+\/)?route\.ts$/;
|
|
11
16
|
const addressed = (rows) => rows.flatMap((one) => [...(one.at === undefined ? [] : [one.at]), ...addressed(one.derives ?? [])]);
|
|
12
|
-
const mark = (one) =>
|
|
17
|
+
const mark = (one) => markOf(one);
|
|
13
18
|
function releases() {
|
|
14
19
|
const at = inTree(TREE, 'derivation.ts');
|
|
15
20
|
if (!existsSync(at))
|
|
@@ -32,35 +37,73 @@ function manager() {
|
|
|
32
37
|
return 'npm';
|
|
33
38
|
}
|
|
34
39
|
}
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
|
|
40
|
+
function served() {
|
|
41
|
+
const found = new Set();
|
|
42
|
+
const walk = (held) => {
|
|
43
|
+
if (held === null || typeof held !== 'object')
|
|
44
|
+
return;
|
|
45
|
+
const row = held;
|
|
46
|
+
const routes = Array.isArray(row.derives) ? row.derives.filter((one) => ROUTE.test(one.at ?? '')) : [];
|
|
47
|
+
if (typeof row.at === 'string' && !row.at.startsWith(`${TREE.name}/`) && routes.length > 0)
|
|
48
|
+
found.add(row.at);
|
|
49
|
+
for (const one of Object.values(held))
|
|
50
|
+
walk(one);
|
|
51
|
+
};
|
|
52
|
+
for (const name of readdirSync(TREE.dir).filter((one) => one.endsWith('.ts')).sort()) {
|
|
53
|
+
walk(valuesIn(readFileSync(join(TREE.dir, name), 'utf8')));
|
|
54
|
+
}
|
|
55
|
+
return [...found].sort();
|
|
56
|
+
}
|
|
57
|
+
function runsThread() {
|
|
58
|
+
const at = inTree(TREE, 'targets.ts');
|
|
59
|
+
if (!existsSync(at))
|
|
60
|
+
return false;
|
|
61
|
+
const held = valuesIn(readFileSync(at, 'utf8'));
|
|
62
|
+
return (held.TARGETS ?? []).some((one) => one.id === PROGRAM);
|
|
38
63
|
}
|
|
39
|
-
function dockerfile(
|
|
64
|
+
function dockerfile() {
|
|
40
65
|
const tool = manager();
|
|
41
66
|
const corepack = tool === 'npm' ? [] : ['RUN corepack enable'];
|
|
42
67
|
const lock = tool === 'npm' ? 'package-lock.json*' : `${tool}-lock.yaml*`;
|
|
43
|
-
const
|
|
44
|
-
? 'RUN npm ci --
|
|
45
|
-
: `RUN ${tool} install --
|
|
46
|
-
const
|
|
68
|
+
const install = tool === 'npm'
|
|
69
|
+
? 'RUN npm ci --ignore-scripts || npm install --ignore-scripts'
|
|
70
|
+
: `RUN ${tool} install --frozen-lockfile --ignore-scripts || ${tool} install --ignore-scripts`;
|
|
71
|
+
const thread = runsThread();
|
|
72
|
+
const carried = [RECEIPT, ...served(), ...(thread ? [COPY_AT] : [])];
|
|
73
|
+
const running = ['WORKDIR /app', 'ENV NODE_ENV=production', 'ENV HOSTNAME=0.0.0.0', `ENV PORT=${String(LISTENS)}`];
|
|
74
|
+
const ending = [...(thread ? [`RUN mkdir -p ${TREE.name}`] : []), `EXPOSE ${String(LISTENS)}`];
|
|
75
|
+
if (!declaresUi(TREE)) {
|
|
76
|
+
const production = tool === 'npm'
|
|
77
|
+
? 'RUN npm ci --omit=dev --ignore-scripts || npm install --omit=dev --ignore-scripts'
|
|
78
|
+
: `RUN ${tool} install --prod --frozen-lockfile --ignore-scripts || ${tool} install --prod --ignore-scripts`;
|
|
79
|
+
return [
|
|
80
|
+
'FROM node:22-slim',
|
|
81
|
+
...running,
|
|
82
|
+
...corepack,
|
|
83
|
+
`COPY package.json ${lock} ./`,
|
|
84
|
+
production,
|
|
85
|
+
...[SERVER_OUT, ...carried].map((one) => `COPY ${one} ./${one}`),
|
|
86
|
+
...ending,
|
|
87
|
+
`CMD ${JSON.stringify(['node', SERVER_OUT])}`,
|
|
88
|
+
'',
|
|
89
|
+
].join('\n');
|
|
90
|
+
}
|
|
47
91
|
return [
|
|
48
92
|
'FROM node:22-slim AS build',
|
|
49
93
|
'WORKDIR /app',
|
|
50
94
|
...corepack,
|
|
51
95
|
`COPY package.json ${lock} ./`,
|
|
52
|
-
|
|
96
|
+
install,
|
|
53
97
|
'COPY . .',
|
|
54
|
-
|
|
55
|
-
`RUN ${build}`,
|
|
98
|
+
`RUN ${tool} run build`,
|
|
56
99
|
'',
|
|
57
100
|
'FROM node:22-slim',
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
'
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`CMD ${JSON.stringify(
|
|
101
|
+
...running,
|
|
102
|
+
'COPY --from=build /app/.next/standalone ./',
|
|
103
|
+
'COPY --from=build /app/.next/static ./.next/static',
|
|
104
|
+
...carried.map((one) => `COPY --from=build /app/${one} ./${one}`),
|
|
105
|
+
...ending,
|
|
106
|
+
`CMD ${JSON.stringify(['node', 'server.js'])}`,
|
|
64
107
|
'',
|
|
65
108
|
].join('\n');
|
|
66
109
|
}
|
|
@@ -76,7 +119,10 @@ function compose(id) {
|
|
|
76
119
|
}
|
|
77
120
|
function withheld() {
|
|
78
121
|
return [
|
|
79
|
-
|
|
122
|
+
VAULT_DIR,
|
|
123
|
+
`!${RECEIPT}`,
|
|
124
|
+
...(runsThread() ? [`!${COPY_AT}`] : []),
|
|
125
|
+
'.next',
|
|
80
126
|
'node_modules',
|
|
81
127
|
'.git',
|
|
82
128
|
'',
|
|
@@ -85,7 +131,6 @@ function withheld() {
|
|
|
85
131
|
function made() {
|
|
86
132
|
const found = new Map();
|
|
87
133
|
const rows = releases();
|
|
88
|
-
const { build, start } = scripted();
|
|
89
134
|
for (const one of rows) {
|
|
90
135
|
const id = one.id;
|
|
91
136
|
if (id === undefined)
|
|
@@ -94,7 +139,7 @@ function made() {
|
|
|
94
139
|
if (kind === undefined)
|
|
95
140
|
continue;
|
|
96
141
|
if (kind === 'docker') {
|
|
97
|
-
found.set('Dockerfile', dockerfile(
|
|
142
|
+
found.set('Dockerfile', dockerfile());
|
|
98
143
|
found.set('compose.yaml', compose(id));
|
|
99
144
|
found.set('.dockerignore', withheld());
|
|
100
145
|
}
|
|
@@ -187,8 +232,11 @@ function run() {
|
|
|
187
232
|
}
|
|
188
233
|
for (const [at, text] of files)
|
|
189
234
|
writeFileSync(join(TREE.at, at), text, 'utf8');
|
|
190
|
-
stdout.write('release: wrote ' + [...files.keys()].join(', ') + ' —
|
|
191
|
-
|
|
235
|
+
stdout.write('release: wrote ' + [...files.keys()].join(', ') + ' — ' +
|
|
236
|
+
(declaresUi(TREE)
|
|
237
|
+
? 'built by the build this app\'s own manifest declares, and started by `node server.js` from what that build compiled'
|
|
238
|
+
: 'started by `node ' + SERVER_OUT + '`, the server `thread build` compiled into this tree, beside only the packages installed for production') +
|
|
239
|
+
', listening on ' + String(LISTENS) + '.\n' +
|
|
192
240
|
'release: no secret is named there. A secret is read from the sealed store at the moment it is used, and ' +
|
|
193
241
|
'naming it here would be telling whoever deploys this to put a credential in the environment instead.\n');
|
|
194
242
|
return 0;
|
|
@@ -4,7 +4,7 @@ export const WRITES = {
|
|
|
4
4
|
docker: [
|
|
5
5
|
{
|
|
6
6
|
at: 'Dockerfile',
|
|
7
|
-
describes: 'How this app becomes an image and what starts it inside one
|
|
7
|
+
describes: 'How this app becomes an image, and what starts it inside one. An app that declares `ui` is built inside the image by the build command its own manifest declares, with the package manager that manifest names, and started with `node server.js` from what Next compiled. One that does not builds nothing there: it is started with Node on the server `thread build` compiled into this tree, beside only the packages its manifest installs for production. Either way the image keeps the files its routes read from disk, the receipt of the gate that passed, and the compiled thread command when this app runs one. Nothing of the framework\'s source goes in either way.',
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
at: 'compose.yaml',
|
|
@@ -12,13 +12,13 @@ export const WRITES = {
|
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
at: '.dockerignore',
|
|
15
|
-
describes: 'What a build of this image must never carry out of this directory: the sealed store and the key that opens it, the installed packages, and the history. Without it the build copies everything beside it, and every sealed value goes into the image and into whoever pulls it.',
|
|
15
|
+
describes: 'What a build of this image must never carry out of this directory: the sealed store and the key that opens it, an earlier build, the installed packages, and the history. From the sealed store\'s folder it lets through only the receipt of the gate that passed and, when this app runs the thread command, the compiled copy of that command. Neither holds a secret. Without it the build copies everything beside it, and every sealed value goes into the image and into whoever pulls it.',
|
|
16
16
|
},
|
|
17
17
|
],
|
|
18
18
|
};
|
|
19
19
|
export const LISTENS = 3000;
|
|
20
20
|
export const SAID = {
|
|
21
|
-
docker: 'A container image built from this app and the compose file that runs it. It
|
|
21
|
+
docker: 'A container image built from this app and the compose file that runs it. It needs no account and no network to be proven, so an app can be taken end to end with it before anybody has chosen where it really lives. What it holds is derived: the server, on the port it listens on, with the files its routes read from disk, the receipt of the gate that passed, and the compiled thread command when this app runs one. An app that declares `ui` is served by what Next compiles, started with `node server.js`; one that does not is served by the server `thread build` compiled into this tree, started with Node beside only the packages its manifest installs for production. It names no secret, because a value is typed by a person and read from the sealed store where it is used.',
|
|
22
22
|
};
|
|
23
23
|
export const kindOf = (id) => KINDS.find((one) => id.startsWith(PREFIXES[one]));
|
|
24
24
|
export const WRITTEN_BY = 'thread release';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LOCKFILES, type
|
|
1
|
+
import { LOCKFILES, type Ran, type Running } from '../integrations/target.ts';
|
|
2
2
|
export interface Starting {
|
|
3
3
|
readonly at?: string;
|
|
4
4
|
readonly shows?: boolean | 'errors' | 'tee';
|
|
@@ -19,20 +19,14 @@ export interface Held {
|
|
|
19
19
|
}
|
|
20
20
|
export declare function releases(): void;
|
|
21
21
|
export declare function claims(root: string, runs: string): Held;
|
|
22
|
-
export declare function programFor(name: string, at: string): string;
|
|
22
|
+
export declare function programFor(name: string, at: string): readonly string[];
|
|
23
23
|
export declare const SIGNING = "THREAD_RECEIPTS_KEY";
|
|
24
24
|
export declare function signing(): string;
|
|
25
25
|
export declare const PROGRAM: string;
|
|
26
26
|
export declare const COPIES: string;
|
|
27
27
|
export declare const COPY_AT: string;
|
|
28
28
|
export declare function running(name: string, subcommands: Readonly<Record<string, string>>, at: string): Running;
|
|
29
|
-
export
|
|
30
|
-
readonly program: string;
|
|
31
|
-
readonly target: Target;
|
|
32
|
-
readonly calls: readonly Call[];
|
|
33
|
-
}
|
|
34
|
-
export declare function expansions(at: string): readonly Expansion[];
|
|
35
|
-
export declare function calls(target: string, at: string): Running;
|
|
29
|
+
export { calls, expansions, type Expansion } from '../integrations/declared.ts';
|
|
36
30
|
export { LOCKFILES };
|
|
37
31
|
export declare const manager: (into: string) => string;
|
|
38
32
|
export declare function adding(into: string): readonly [string, string];
|