@ismaelsoilet/synapsejs 1.8.0 → 1.11.1
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/README.md +98 -6
- package/bin/synapse.ts +291 -241
- package/package.json +2 -2
- package/src/client/components.tsx +459 -1
- package/src/client/context.tsx +23 -3
- package/src/client/error-boundary.tsx +59 -0
- package/src/client/hooks.ts +100 -1
- package/src/client/toast.tsx +219 -0
- package/src/client/upload.ts +153 -0
- package/src/client.ts +37 -2
- package/src/compiler/client-bundler.ts +94 -2
- package/src/compiler/context-bench.ts +9 -1
- package/src/compiler/contract.ts +31 -4
- package/src/compiler/db-schema-generator.ts +39 -8
- package/src/compiler/fields-parser.ts +28 -1
- package/src/compiler/impact-analyzer.ts +31 -1
- package/src/compiler/migration-runner.ts +10 -2
- package/src/compiler/optional-coverage.ts +137 -0
- package/src/compiler/oracle-runner.ts +18 -4
- package/src/compiler/scaffolder.ts +227 -9
- package/src/compiler/slice-discovery.ts +8 -1
- package/src/compiler/slice-splitter.ts +68 -6
- package/src/compiler/slice-templates.ts +234 -10
- package/src/compiler/typescript-api.ts +33 -0
- package/src/core/action-context.ts +101 -15
- package/src/core/config.ts +88 -10
- package/src/core/cron-parser.ts +167 -0
- package/src/core/database-client.ts +103 -6
- package/src/core/index.ts +5 -0
- package/src/core/jobs.ts +37 -0
- package/src/core/oauth-client.ts +322 -0
- package/src/core/path-guard.ts +105 -0
- package/src/core/postgres-client.ts +37 -0
- package/src/core/postgres-queue.ts +76 -9
- package/src/core/query-builder.ts +166 -34
- package/src/core/session-context.ts +45 -4
- package/src/core/session-token.ts +124 -8
- package/src/core/sqlite-client.ts +20 -0
- package/src/core/storage.ts +60 -19
- package/src/core/topics.ts +84 -0
- package/src/core/webhook-signature.ts +151 -0
- package/src/index.ts +2 -1
- package/src/mcp/server.ts +317 -218
- package/src/mcp/tools.ts +329 -0
- package/src/project-status.ts +433 -0
- package/src/runtime/client-entry.ts +11 -5
- package/src/runtime/network-guard.ts +3 -1
- package/src/runtime/queue-engine.ts +167 -5
- package/src/runtime/rate-limiter.ts +14 -4
- package/src/runtime/security-headers.ts +35 -0
- package/src/runtime/server.ts +1174 -236
- package/src/runtime/uploads.ts +199 -19
- package/src/version.ts +9 -2
- package/templates/starter/package.json +2 -2
- package/templates/starter/tsconfig.json +1 -0
package/bin/synapse.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* synapse split - Partition slices into shared/server/client modules (gated)
|
|
16
16
|
* synapse test - Run all property-based testing (PBT) oracles
|
|
17
17
|
* synapse new-slice - Scaffold a new atomic vertical slice
|
|
18
|
+
* synapse new-domain - Scaffold a domain layout, list slice and shared module
|
|
18
19
|
* synapse info - Machine metadata and framework metrics
|
|
19
20
|
*/
|
|
20
21
|
|
|
@@ -22,25 +23,116 @@ import * as fs from 'fs';
|
|
|
22
23
|
import * as path from 'path';
|
|
23
24
|
import { runMachineVerifications } from '../src/compiler/agent-diagnostic-json';
|
|
24
25
|
import { compressRepositoryAST } from '../src/compiler/ast-daemon-compressor';
|
|
25
|
-
import { buildAllClientBundles } from '../src/compiler/client-bundler';
|
|
26
|
+
import { buildAllClientBundles, buildCssBundle } from '../src/compiler/client-bundler';
|
|
26
27
|
import { renderContractJson, renderContractMarkdown } from '../src/compiler/contract';
|
|
27
28
|
import { generateDatabaseSchemaCatalog } from '../src/compiler/db-schema-generator';
|
|
28
29
|
import { analyzeImpact } from '../src/compiler/impact-analyzer';
|
|
29
30
|
import { rollbackSliceMigrations, runSliceMigrations } from '../src/compiler/migration-runner';
|
|
31
|
+
import { analyzeOptionalItemCoverage, unionCoverage } from '../src/compiler/optional-coverage';
|
|
30
32
|
import { runSliceOracles } from '../src/compiler/oracle-runner';
|
|
31
|
-
import { scaffoldCrud, scaffoldShared, scaffoldSlice } from '../src/compiler/scaffolder';
|
|
33
|
+
import { scaffoldCrud, scaffoldDomain, scaffoldShared, scaffoldSlice } from '../src/compiler/scaffolder';
|
|
32
34
|
import { checkSchemaDrift } from '../src/compiler/schema-drift';
|
|
33
35
|
import { findSliceFiles, resolveSlicesDir } from '../src/compiler/slice-discovery';
|
|
34
36
|
import { artifactDirectory, splitSlice, verifySplit, writeSplitArtifacts } from '../src/compiler/slice-splitter';
|
|
35
37
|
import { SLICE_TEMPLATES, type SliceTemplate } from '../src/compiler/slice-templates';
|
|
36
38
|
import { buildStandalone } from '../src/compiler/standalone-builder';
|
|
39
|
+
import { assertCompilerApi } from '../src/compiler/typescript-api';
|
|
40
|
+
import { isInsideBase } from '../src/core/path-guard';
|
|
37
41
|
import { SynapseMcpServer } from '../src/mcp/server';
|
|
42
|
+
import { projectStatus } from '../src/project-status';
|
|
38
43
|
import { SynapseServer } from '../src/runtime/server';
|
|
44
|
+
import { SYNAPSE_VERSION } from '../src/version';
|
|
39
45
|
|
|
40
46
|
const command = process.argv[2] || 'check';
|
|
41
47
|
const root = process.cwd();
|
|
42
48
|
|
|
49
|
+
/**
|
|
50
|
+
* The flags each subcommand accepts. An unrecognised flag is refused on every
|
|
51
|
+
* command, with the same machine-readable failure code — a machine consumer must
|
|
52
|
+
* not be told "PASS" for a flag the command silently ignored.
|
|
53
|
+
*/
|
|
54
|
+
const KNOWN_FLAGS: Record<string, string[]> = {
|
|
55
|
+
dev: ['--watch'],
|
|
56
|
+
start: ['--acknowledge-insecure'],
|
|
57
|
+
check: [],
|
|
58
|
+
migrate: [],
|
|
59
|
+
rollback: ['--steps'],
|
|
60
|
+
mcp: [],
|
|
61
|
+
skeleton: [],
|
|
62
|
+
'db-schema': [],
|
|
63
|
+
'db-drift': [],
|
|
64
|
+
impact: [],
|
|
65
|
+
split: [],
|
|
66
|
+
'new-slice': ['--template', '--fields'],
|
|
67
|
+
'new-shared': [],
|
|
68
|
+
'new-domain': ['--fields'],
|
|
69
|
+
build: ['--standalone'],
|
|
70
|
+
contract: ['--markdown'],
|
|
71
|
+
test: ['--gate'],
|
|
72
|
+
new: [],
|
|
73
|
+
create: [],
|
|
74
|
+
init: [],
|
|
75
|
+
worker: [],
|
|
76
|
+
info: [],
|
|
77
|
+
coverage: []
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** Returns the first unrecognised flag, or null when every flag is declared. */
|
|
81
|
+
function unknownFlag(argv: string[]): string | null {
|
|
82
|
+
const known = KNOWN_FLAGS[command] ?? [];
|
|
83
|
+
|
|
84
|
+
for (const argument of argv.slice(3)) {
|
|
85
|
+
if (!argument.startsWith('--')) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const name = argument.split('=')[0];
|
|
90
|
+
|
|
91
|
+
if (!known.includes(name)) {
|
|
92
|
+
return argument;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
43
99
|
async function main() {
|
|
100
|
+
const offendingFlag = unknownFlag(process.argv);
|
|
101
|
+
|
|
102
|
+
if (offendingFlag) {
|
|
103
|
+
process.stdout.write(
|
|
104
|
+
`${JSON.stringify(
|
|
105
|
+
{
|
|
106
|
+
status: 'FAIL',
|
|
107
|
+
code: 'UNKNOWN_FLAG',
|
|
108
|
+
operation: 'FLAG_VALIDATION',
|
|
109
|
+
message: `Flag desconhecida: ${offendingFlag}. Flags aceitas por '${command}': ${(KNOWN_FLAGS[command] ?? []).join(', ') || 'nenhuma'}.`
|
|
110
|
+
},
|
|
111
|
+
null,
|
|
112
|
+
2
|
|
113
|
+
)}\n`
|
|
114
|
+
);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const compilerCommands = new Set(['check', 'split', 'skeleton', 'db-schema', 'impact', 'test', 'build']);
|
|
119
|
+
|
|
120
|
+
if (compilerCommands.has(command)) {
|
|
121
|
+
try {
|
|
122
|
+
assertCompilerApi();
|
|
123
|
+
} catch (error) {
|
|
124
|
+
process.stdout.write(
|
|
125
|
+
`${JSON.stringify({
|
|
126
|
+
status: 'FAIL',
|
|
127
|
+
operation: 'COMPILER_API_CHECK',
|
|
128
|
+
code: 'COMPILER_API_UNAVAILABLE',
|
|
129
|
+
message: error instanceof Error ? error.message : String(error)
|
|
130
|
+
})}\n`
|
|
131
|
+
);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
44
136
|
switch (command) {
|
|
45
137
|
case 'dev': {
|
|
46
138
|
const watch = process.argv.includes('--watch');
|
|
@@ -54,6 +146,7 @@ async function main() {
|
|
|
54
146
|
}
|
|
55
147
|
|
|
56
148
|
const port = parseInt(process.argv[3] || process.env.PORT || '3000', 10);
|
|
149
|
+
await buildCssBundle(root);
|
|
57
150
|
const server = new SynapseServer(root, port);
|
|
58
151
|
await server.discoverSlices();
|
|
59
152
|
await server.start();
|
|
@@ -62,15 +155,36 @@ async function main() {
|
|
|
62
155
|
|
|
63
156
|
case 'start': {
|
|
64
157
|
const port = parseInt(process.argv[3] || process.env.PORT || '3000', 10);
|
|
65
|
-
const
|
|
158
|
+
const { isProductionLikeMode } = await import('../src/runtime/server');
|
|
159
|
+
const acknowledged =
|
|
160
|
+
process.argv.includes('--acknowledge-insecure') ||
|
|
161
|
+
process.env.SYNAPSE_ACKNOWLEDGE_INSECURE === 'true' ||
|
|
162
|
+
process.env.SYNAPSE_DEV_HEADERS === 'true';
|
|
66
163
|
|
|
67
|
-
if (
|
|
164
|
+
if (isProductionLikeMode() && !process.env.SYNAPSE_SESSION_SECRET && !acknowledged) {
|
|
165
|
+
process.stdout.write(
|
|
166
|
+
`${JSON.stringify(
|
|
167
|
+
{
|
|
168
|
+
status: 'FAIL',
|
|
169
|
+
operation: 'START',
|
|
170
|
+
code: 'MISSING_SESSION_SECRET',
|
|
171
|
+
message:
|
|
172
|
+
'Modo de produção sem SYNAPSE_SESSION_SECRET: toda requisição resolveria para anônimo. Configure o segredo ou passe --acknowledge-insecure para assumir o risco explicitamente.'
|
|
173
|
+
},
|
|
174
|
+
null,
|
|
175
|
+
2
|
|
176
|
+
)}\n`
|
|
177
|
+
);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!process.env.SYNAPSE_SESSION_SECRET && acknowledged) {
|
|
68
182
|
console.warn(
|
|
69
|
-
'⚠️ [AVISO DE SEGURANÇA]
|
|
70
|
-
' Sessões de usuário permanecerão anônimas por padrão.'
|
|
183
|
+
'⚠️ [AVISO DE SEGURANÇA] Iniciando sem SYNAPSE_SESSION_SECRET: identidade e papéis vindos de headers do chamador (x-user-id, x-user-roles) NÃO serão confiados; requisições permanecem anônimas.'
|
|
71
184
|
);
|
|
72
185
|
}
|
|
73
186
|
|
|
187
|
+
await buildCssBundle(root);
|
|
74
188
|
const server = new SynapseServer(root, port);
|
|
75
189
|
await server.discoverSlices();
|
|
76
190
|
await server.start();
|
|
@@ -79,13 +193,24 @@ async function main() {
|
|
|
79
193
|
const gracefulShutdown = async (signal: string) => {
|
|
80
194
|
if (shuttingDown) return;
|
|
81
195
|
shuttingDown = true;
|
|
82
|
-
console.log(`\n🛑 [SynapseJS] Recebido sinal ${signal}, iniciando encerramento gracioso...`);
|
|
83
196
|
try {
|
|
84
|
-
await server.stop();
|
|
85
|
-
|
|
197
|
+
const summary = await server.stop();
|
|
198
|
+
process.stdout.write(
|
|
199
|
+
`${JSON.stringify({
|
|
200
|
+
status: 'PASS',
|
|
201
|
+
operation: 'GRACEFUL_SHUTDOWN',
|
|
202
|
+
signal,
|
|
203
|
+
drainedRequests: summary.drained,
|
|
204
|
+
abortedRequests: summary.aborted,
|
|
205
|
+
jobsRequeued: summary.jobsRequeued,
|
|
206
|
+
jobsFailed: summary.jobsFailed
|
|
207
|
+
})}\n`
|
|
208
|
+
);
|
|
86
209
|
process.exit(0);
|
|
87
210
|
} catch (err) {
|
|
88
|
-
|
|
211
|
+
process.stderr.write(
|
|
212
|
+
`${JSON.stringify({ status: 'ERROR', operation: 'GRACEFUL_SHUTDOWN', error: String(err) })}\n`
|
|
213
|
+
);
|
|
89
214
|
process.exit(1);
|
|
90
215
|
}
|
|
91
216
|
};
|
|
@@ -257,7 +382,9 @@ async function main() {
|
|
|
257
382
|
|
|
258
383
|
const report = analyzeImpact(target, root);
|
|
259
384
|
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
260
|
-
|
|
385
|
+
// A target that does not exist is a failure; a valid target with no
|
|
386
|
+
// dependents is not.
|
|
387
|
+
process.exit(report.status === 'PASS' ? 0 : 1);
|
|
261
388
|
break;
|
|
262
389
|
}
|
|
263
390
|
|
|
@@ -341,7 +468,7 @@ async function main() {
|
|
|
341
468
|
const name = process.argv[4];
|
|
342
469
|
|
|
343
470
|
if (!domain || !name) {
|
|
344
|
-
process.
|
|
471
|
+
process.stdout.write(
|
|
345
472
|
`${JSON.stringify({
|
|
346
473
|
status: 'ERROR',
|
|
347
474
|
message: 'Parâmetros obrigatórios ausentes. Uso: synapse new-slice <domain> <name>'
|
|
@@ -409,7 +536,7 @@ async function main() {
|
|
|
409
536
|
const created = scaffoldSlice(domain, name, root, template, fieldsArg);
|
|
410
537
|
|
|
411
538
|
if (!created.ok) {
|
|
412
|
-
process.
|
|
539
|
+
process.stdout.write(
|
|
413
540
|
`${JSON.stringify(
|
|
414
541
|
{
|
|
415
542
|
status: 'ERROR',
|
|
@@ -445,7 +572,7 @@ async function main() {
|
|
|
445
572
|
case 'new-shared': {
|
|
446
573
|
const name = process.argv[3];
|
|
447
574
|
if (!name) {
|
|
448
|
-
process.
|
|
575
|
+
process.stdout.write(
|
|
449
576
|
`${JSON.stringify({
|
|
450
577
|
status: 'ERROR',
|
|
451
578
|
message: 'Nome do módulo compartilhado ausente. Uso: synapse new-shared <nome>'
|
|
@@ -458,7 +585,7 @@ async function main() {
|
|
|
458
585
|
const created = scaffoldShared(name, root);
|
|
459
586
|
|
|
460
587
|
if (!created.ok) {
|
|
461
|
-
process.
|
|
588
|
+
process.stdout.write(
|
|
462
589
|
`${JSON.stringify(
|
|
463
590
|
{
|
|
464
591
|
status: 'ERROR',
|
|
@@ -489,7 +616,56 @@ async function main() {
|
|
|
489
616
|
break;
|
|
490
617
|
}
|
|
491
618
|
|
|
619
|
+
case 'new-domain': {
|
|
620
|
+
const domain = process.argv[3];
|
|
621
|
+
if (!domain) {
|
|
622
|
+
process.stdout.write(
|
|
623
|
+
`${JSON.stringify({
|
|
624
|
+
status: 'ERROR',
|
|
625
|
+
message: 'Nome do domínio ausente. Uso: synapse new-domain <dominio> [--fields="..."]'
|
|
626
|
+
})}\n`
|
|
627
|
+
);
|
|
628
|
+
process.exit(1);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const root = process.env.SYNAPSE_ROOT || process.cwd();
|
|
632
|
+
const fields = process.argv.find((arg) => arg.startsWith('--fields='))?.slice('--fields='.length);
|
|
633
|
+
const created = scaffoldDomain(domain, root, fields);
|
|
634
|
+
|
|
635
|
+
if (!created.ok) {
|
|
636
|
+
process.stdout.write(
|
|
637
|
+
`${JSON.stringify(
|
|
638
|
+
{
|
|
639
|
+
status: 'ERROR',
|
|
640
|
+
operation: 'SCAFFOLD_DOMAIN',
|
|
641
|
+
code: created.error.code,
|
|
642
|
+
message: created.error.message
|
|
643
|
+
},
|
|
644
|
+
null,
|
|
645
|
+
2
|
|
646
|
+
)}\n`
|
|
647
|
+
);
|
|
648
|
+
process.exit(1);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
process.stdout.write(
|
|
652
|
+
`${JSON.stringify(
|
|
653
|
+
{
|
|
654
|
+
status: 'PASS',
|
|
655
|
+
operation: 'SCAFFOLD_DOMAIN',
|
|
656
|
+
domain,
|
|
657
|
+
createdFiles: created.value.map((f) => path.relative(root, f))
|
|
658
|
+
},
|
|
659
|
+
null,
|
|
660
|
+
2
|
|
661
|
+
)}\n`
|
|
662
|
+
);
|
|
663
|
+
process.exit(0);
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
666
|
+
|
|
492
667
|
case 'build': {
|
|
668
|
+
await buildCssBundle(root);
|
|
493
669
|
const isStandalone = process.argv.includes('--standalone');
|
|
494
670
|
|
|
495
671
|
if (isStandalone) {
|
|
@@ -572,6 +748,52 @@ async function main() {
|
|
|
572
748
|
break;
|
|
573
749
|
}
|
|
574
750
|
|
|
751
|
+
case 'coverage': {
|
|
752
|
+
// Which optional contract items the committed slices exercise — across every
|
|
753
|
+
// shipped application, so the report covers the union.
|
|
754
|
+
const targets = [
|
|
755
|
+
root,
|
|
756
|
+
...(fs.existsSync(path.join(root, 'examples'))
|
|
757
|
+
? fs
|
|
758
|
+
.readdirSync(path.join(root, 'examples'), { withFileTypes: true })
|
|
759
|
+
.filter((entry) => entry.isDirectory())
|
|
760
|
+
.map((entry) => path.join(root, 'examples', entry.name))
|
|
761
|
+
: [])
|
|
762
|
+
];
|
|
763
|
+
|
|
764
|
+
const reports = targets.map((target) => analyzeOptionalItemCoverage(target));
|
|
765
|
+
const resolvable = reports.filter((report) => report.status !== 'FAIL' || report.totalSlices > 0);
|
|
766
|
+
|
|
767
|
+
if (resolvable.length === 0) {
|
|
768
|
+
process.stdout.write(
|
|
769
|
+
`${JSON.stringify(reports[0] ?? { status: 'FAIL', operation: 'OPTIONAL_ITEM_COVERAGE' }, null, 2)}\n`
|
|
770
|
+
);
|
|
771
|
+
process.exit(1);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
const union = unionCoverage(resolvable);
|
|
775
|
+
|
|
776
|
+
process.stdout.write(
|
|
777
|
+
`${JSON.stringify(
|
|
778
|
+
{
|
|
779
|
+
status: union.unexercised.length === 0 ? 'PASS' : 'FAIL',
|
|
780
|
+
operation: 'OPTIONAL_ITEM_COVERAGE',
|
|
781
|
+
applications: resolvable.map((report) => ({
|
|
782
|
+
appDir: report.appDir,
|
|
783
|
+
totalSlices: report.totalSlices,
|
|
784
|
+
items: report.items
|
|
785
|
+
})),
|
|
786
|
+
covered: union.covered,
|
|
787
|
+
unexercised: union.unexercised
|
|
788
|
+
},
|
|
789
|
+
null,
|
|
790
|
+
2
|
|
791
|
+
)}\n`
|
|
792
|
+
);
|
|
793
|
+
process.exit(union.unexercised.length === 0 ? 0 : 1);
|
|
794
|
+
break;
|
|
795
|
+
}
|
|
796
|
+
|
|
575
797
|
case 'contract': {
|
|
576
798
|
// O framework se descreve numa chamada: e o que o agente le em vez de adivinhar.
|
|
577
799
|
const asMarkdown = process.argv.includes('--markdown');
|
|
@@ -597,7 +819,7 @@ async function main() {
|
|
|
597
819
|
case 'init': {
|
|
598
820
|
const rawTarget = process.argv[3];
|
|
599
821
|
if (!rawTarget) {
|
|
600
|
-
process.
|
|
822
|
+
process.stdout.write(
|
|
601
823
|
`${JSON.stringify({
|
|
602
824
|
status: 'ERROR',
|
|
603
825
|
message: 'Nome do projeto obrigatório. Uso: synapse new <project-name>'
|
|
@@ -607,15 +829,34 @@ async function main() {
|
|
|
607
829
|
}
|
|
608
830
|
|
|
609
831
|
const targetDir = path.resolve(root, rawTarget);
|
|
832
|
+
|
|
833
|
+
if (!isInsideBase(root, targetDir)) {
|
|
834
|
+
process.stdout.write(
|
|
835
|
+
`${JSON.stringify(
|
|
836
|
+
{
|
|
837
|
+
status: 'ERROR',
|
|
838
|
+
operation: 'CREATE_PROJECT',
|
|
839
|
+
code: 'INVALID_PATH_SEGMENT',
|
|
840
|
+
message: `O caminho '${rawTarget}' resolve para fora do diretório atual (${root}). Um projeto novo é criado dentro dele; use um nome simples ou um subdiretório.`
|
|
841
|
+
},
|
|
842
|
+
null,
|
|
843
|
+
2
|
|
844
|
+
)}\n`
|
|
845
|
+
);
|
|
846
|
+
process.exit(1);
|
|
847
|
+
}
|
|
848
|
+
|
|
610
849
|
const cleanProjectName = path
|
|
611
850
|
.basename(targetDir)
|
|
612
851
|
.toLowerCase()
|
|
613
852
|
.replace(/[^a-z0-9-_]/g, '-');
|
|
614
853
|
|
|
615
854
|
if (fs.existsSync(targetDir)) {
|
|
616
|
-
process.
|
|
855
|
+
process.stdout.write(
|
|
617
856
|
`${JSON.stringify({
|
|
618
857
|
status: 'ERROR',
|
|
858
|
+
operation: 'CREATE_PROJECT',
|
|
859
|
+
code: 'TARGET_EXISTS',
|
|
619
860
|
message: `O diretório '${rawTarget}' já existe em ${targetDir}.`
|
|
620
861
|
})}\n`
|
|
621
862
|
);
|
|
@@ -633,9 +874,11 @@ async function main() {
|
|
|
633
874
|
const templateDir = candidates.find((dir) => fs.existsSync(dir));
|
|
634
875
|
|
|
635
876
|
if (!templateDir) {
|
|
636
|
-
process.
|
|
877
|
+
process.stdout.write(
|
|
637
878
|
`${JSON.stringify({
|
|
638
879
|
status: 'ERROR',
|
|
880
|
+
operation: 'CREATE_PROJECT',
|
|
881
|
+
code: 'TEMPLATE_NOT_FOUND',
|
|
639
882
|
message: 'Template oficial starter não encontrado nos diretórios candidatos.'
|
|
640
883
|
})}\n`
|
|
641
884
|
);
|
|
@@ -660,9 +903,11 @@ async function main() {
|
|
|
660
903
|
let content = fs.readFileSync(s, 'utf-8');
|
|
661
904
|
if (entry.name === 'package.json') {
|
|
662
905
|
content = content.replace('"starter-app"', `"${cleanProjectName}"`);
|
|
906
|
+
// The scaffold depends on the version this CLI ships, never on a frozen
|
|
907
|
+
// older pin: a new project must receive every fix since then.
|
|
663
908
|
content = content.replace(
|
|
664
909
|
/"(@ismaelsoilet\/)?synapsejs":\s*"[^"]+"/g,
|
|
665
|
-
|
|
910
|
+
`"@ismaelsoilet/synapsejs": "^${SYNAPSE_VERSION}"`
|
|
666
911
|
);
|
|
667
912
|
}
|
|
668
913
|
fs.writeFileSync(d, content, 'utf-8');
|
|
@@ -712,6 +957,7 @@ async function main() {
|
|
|
712
957
|
typeof exportVal === 'object' &&
|
|
713
958
|
'name' in (exportVal as Record<string, unknown>)
|
|
714
959
|
) {
|
|
960
|
+
// biome-ignore lint/suspicious/noExplicitAny: slice export boundary, validated by the shape check above
|
|
715
961
|
queue.registerJob(exportVal as any);
|
|
716
962
|
}
|
|
717
963
|
}
|
|
@@ -724,9 +970,21 @@ async function main() {
|
|
|
724
970
|
console.log('⚡ [SynapseJS Worker] Fila de background jobs ativa e processando...');
|
|
725
971
|
let running = true;
|
|
726
972
|
const shutdown = () => {
|
|
973
|
+
if (!running) return;
|
|
727
974
|
running = false;
|
|
728
|
-
|
|
975
|
+
// Stop claiming first, then return whatever was running to the queue with its
|
|
976
|
+
// attempt counter already advanced by the interrupted claim.
|
|
977
|
+
queue.stopClaiming();
|
|
978
|
+
const interrupted = queue.interruptRunningJobs();
|
|
729
979
|
queue.close();
|
|
980
|
+
process.stdout.write(
|
|
981
|
+
`${JSON.stringify({
|
|
982
|
+
status: 'PASS',
|
|
983
|
+
operation: 'WORKER_SHUTDOWN',
|
|
984
|
+
jobsRequeued: interrupted.requeued,
|
|
985
|
+
jobsFailed: interrupted.failed
|
|
986
|
+
})}\n`
|
|
987
|
+
);
|
|
730
988
|
process.exit(0);
|
|
731
989
|
};
|
|
732
990
|
process.on('SIGINT', shutdown);
|
|
@@ -750,235 +1008,27 @@ async function main() {
|
|
|
750
1008
|
}
|
|
751
1009
|
|
|
752
1010
|
case 'info': {
|
|
1011
|
+
// One machine-readable source: version, adoption, tool inventory and feature
|
|
1012
|
+
// status all come from the same module the docs gate reads.
|
|
1013
|
+
process.stdout.write(`${JSON.stringify(projectStatus(), null, 2)}\n`);
|
|
1014
|
+
process.exit(0);
|
|
1015
|
+
break;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
default: {
|
|
753
1019
|
process.stdout.write(
|
|
754
1020
|
`${JSON.stringify(
|
|
755
1021
|
{
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
features: [
|
|
762
|
-
{
|
|
763
|
-
feature: 'Vertical slices (N = 1) with Locality of Behavior',
|
|
764
|
-
status: 'stable',
|
|
765
|
-
evidence: 'bun test packages/synapse/test/locality.test.ts'
|
|
766
|
-
},
|
|
767
|
-
{
|
|
768
|
-
feature: 'Result<T, E> control flow (no public throwing helper)',
|
|
769
|
-
status: 'stable',
|
|
770
|
-
evidence: 'bun test packages/synapse/test/machine-types.test.ts'
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
feature: 'TypeBox JIT input contracts',
|
|
774
|
-
status: 'stable',
|
|
775
|
-
evidence: 'bun test packages/synapse/test/slice-contract.test.ts'
|
|
776
|
-
},
|
|
777
|
-
{
|
|
778
|
-
feature: 'Declarative sliceSchema migrations, applied once per statement',
|
|
779
|
-
status: 'stable',
|
|
780
|
-
evidence: 'bun test packages/synapse/test/migration-runner.test.ts'
|
|
781
|
-
},
|
|
782
|
-
{
|
|
783
|
-
feature: 'Domain errors map to HTTP status (401/403/404/409/422/500)',
|
|
784
|
-
status: 'stable',
|
|
785
|
-
evidence: 'bun test packages/synapse/test/runtime-server.test.ts'
|
|
786
|
-
},
|
|
787
|
-
{
|
|
788
|
-
feature: 'React hydration from a client bundle the splitter produces',
|
|
789
|
-
status: 'stable',
|
|
790
|
-
evidence: 'bun test packages/synapse/test/runtime-server.test.ts'
|
|
791
|
-
},
|
|
792
|
-
{
|
|
793
|
-
feature: 'Signed sessions (SYNAPSE_SESSION_SECRET) and static files from public/',
|
|
794
|
-
status: 'stable',
|
|
795
|
-
evidence: 'bun test packages/synapse/test/runtime-server.test.ts'
|
|
796
|
-
},
|
|
797
|
-
{
|
|
798
|
-
feature: 'Login with Bun.password, signed token and cookie session (storeSession/clearSession)',
|
|
799
|
-
status: 'stable',
|
|
800
|
-
evidence:
|
|
801
|
-
'bun run --cwd apps/crm synapse test (login oracle: the server accepts the issued token) + bun test packages/synapse/test/session-cookie.test.ts'
|
|
802
|
-
},
|
|
803
|
-
{
|
|
804
|
-
feature: 'Cross-slice atomicity through src/shared modules (db.transaction, rollback proven on SQLite)',
|
|
805
|
-
status: 'stable',
|
|
806
|
-
evidence: 'bun test packages/synapse/test/shared-modules.test.ts'
|
|
807
|
-
},
|
|
808
|
-
{
|
|
809
|
-
feature: 'Slice boundary: a slice never imports another slice (SLICE_IMPORTS_SLICE)',
|
|
810
|
-
status: 'stable',
|
|
811
|
-
evidence: 'bun test packages/synapse/test/slice-splitter.test.ts'
|
|
812
|
-
},
|
|
813
|
-
{
|
|
814
|
-
feature: 'Browser RPC transport that never throws (RPC_MALFORMED/RPC_UNREACHABLE as values)',
|
|
815
|
-
status: 'stable',
|
|
816
|
-
evidence: 'bun test packages/synapse/test/rpc-client.test.ts'
|
|
817
|
-
},
|
|
818
|
-
{
|
|
819
|
-
feature: 'Pre-built client bundles with a manifest (synapse build)',
|
|
820
|
-
status: 'stable',
|
|
821
|
-
evidence: 'bun test packages/synapse/test/client-bundler.test.ts'
|
|
822
|
-
},
|
|
823
|
-
{
|
|
824
|
-
feature: 'Uploads with session, size limit and traversal guard (/_synapse/files)',
|
|
825
|
-
status: 'stable',
|
|
826
|
-
evidence: 'bun test packages/synapse/test/uploads.test.ts'
|
|
827
|
-
},
|
|
828
|
-
{
|
|
829
|
-
feature: 'Embedded SQLite engine (WAL, prepared-statement cache)',
|
|
830
|
-
status: 'stable',
|
|
831
|
-
evidence: 'bun test packages/synapse/test/sqlite-client.test.ts'
|
|
832
|
-
},
|
|
833
|
-
{
|
|
834
|
-
feature: 'Zero-wiring routing, SSR shell and RPC dispatcher',
|
|
835
|
-
status: 'stable',
|
|
836
|
-
evidence: 'bun --cwd examples/enterprise-crm test:e2e'
|
|
837
|
-
},
|
|
838
|
-
{
|
|
839
|
-
feature: 'Slice discovery resolution (never reports PASS with zero slices)',
|
|
840
|
-
status: 'stable',
|
|
841
|
-
evidence: 'bun test packages/synapse/test/slice-discovery.test.ts'
|
|
842
|
-
},
|
|
843
|
-
{
|
|
844
|
-
feature: 'MCP stdio server (11 tools)',
|
|
845
|
-
status: 'stable',
|
|
846
|
-
evidence: 'bun test packages/synapse/test/mcp-server.test.ts'
|
|
847
|
-
},
|
|
848
|
-
{
|
|
849
|
-
feature: 'AST skeletonizer to .codebase/repo-map.d.ts',
|
|
850
|
-
status: 'stable',
|
|
851
|
-
evidence: 'bun --cwd examples/enterprise-crm skeleton'
|
|
852
|
-
},
|
|
853
|
-
{
|
|
854
|
-
feature: 'Centralized Database Schema Catalog (.codebase/db-schema.d.ts)',
|
|
855
|
-
status: 'stable',
|
|
856
|
-
evidence: 'bun test packages/synapse/test/db-schema-generator.test.ts'
|
|
857
|
-
},
|
|
858
|
-
{
|
|
859
|
-
feature: 'Unified Object Storage (LocalStorage + AWS S3/R2 with SigV4)',
|
|
860
|
-
status: 'stable',
|
|
861
|
-
evidence: 'bun test packages/synapse/test/storage.test.ts'
|
|
862
|
-
},
|
|
863
|
-
{
|
|
864
|
-
feature: 'Distributed PostgreSQL Queue (SKIP LOCKED, Full Jitter, DLQ)',
|
|
865
|
-
status: 'stable',
|
|
866
|
-
evidence: 'bun test packages/synapse/test/postgres-queue.test.ts'
|
|
867
|
-
},
|
|
868
|
-
{
|
|
869
|
-
feature: 'Isomorphic slice splitter (shared/server/client modules)',
|
|
870
|
-
status: 'stable',
|
|
871
|
-
evidence: 'bun test packages/synapse/test/slice-splitter.test.ts'
|
|
872
|
-
},
|
|
873
|
-
{
|
|
874
|
-
feature: 'Slice invariants executed by bun:test (per-invariant reporting)',
|
|
875
|
-
status: 'stable',
|
|
876
|
-
evidence: 'bun test packages/synapse/test/oracle-runner.test.ts'
|
|
877
|
-
},
|
|
878
|
-
{
|
|
879
|
-
feature: 'Incremental diagnostics cache across processes',
|
|
880
|
-
status: 'roadmap',
|
|
881
|
-
evidence: 'removido na 0.4.0: medido mais lento que o check completo (2.5s vs 1.9s)'
|
|
882
|
-
},
|
|
883
|
-
{
|
|
884
|
-
feature: 'PostgreSQL parity (migrations, DDL, action round-trip)',
|
|
885
|
-
status: 'stable',
|
|
886
|
-
evidence: 'bun run test:postgres (CI roda contra um serviço postgres:16)'
|
|
887
|
-
},
|
|
888
|
-
{
|
|
889
|
-
feature: 'Context surface benchmark (files/tokens an agent must read)',
|
|
890
|
-
status: 'stable',
|
|
891
|
-
evidence: 'bun run bench'
|
|
892
|
-
},
|
|
893
|
-
{
|
|
894
|
-
feature: 'Isolated SQLite Queue & Background Jobs (defineJob, exponential backoff)',
|
|
895
|
-
status: 'stable',
|
|
896
|
-
evidence: 'bun test packages/synapse/test/jobs-queue.test.ts'
|
|
897
|
-
},
|
|
898
|
-
{
|
|
899
|
-
feature: 'Bidirectional DDL migrations with transactional rollback (synapse rollback)',
|
|
900
|
-
status: 'stable',
|
|
901
|
-
evidence: 'bun test packages/synapse/test/onda2.test.ts'
|
|
902
|
-
},
|
|
903
|
-
{
|
|
904
|
-
feature: 'Vendor code-splitting and dynamic metadata (sliceMeta)',
|
|
905
|
-
status: 'stable',
|
|
906
|
-
evidence: 'bun test packages/synapse/test/onda1.test.ts'
|
|
907
|
-
},
|
|
908
|
-
{
|
|
909
|
-
feature: 'Hierarchical domain layouts (_layout.tsx)',
|
|
910
|
-
status: 'stable',
|
|
911
|
-
evidence: 'bun test packages/synapse/test/onda1.test.ts'
|
|
912
|
-
},
|
|
913
|
-
{
|
|
914
|
-
feature: 'Sliding-window rate limiting & streaming upload guard',
|
|
915
|
-
status: 'stable',
|
|
916
|
-
evidence: 'bun test packages/synapse/test/onda2.test.ts'
|
|
917
|
-
},
|
|
918
|
-
{
|
|
919
|
-
feature: 'Distributed PostgreSQL Event Hub & Isomorphic i18n',
|
|
920
|
-
status: 'stable',
|
|
921
|
-
evidence: 'bun test packages/synapse/test/onda3.test.ts'
|
|
922
|
-
},
|
|
923
|
-
{
|
|
924
|
-
feature: 'Token revocation, TOTP 2FA, image optimization & plugin hooks',
|
|
925
|
-
status: 'stable',
|
|
926
|
-
evidence: 'bun test packages/synapse/test/onda4.test.ts'
|
|
927
|
-
},
|
|
928
|
-
{
|
|
929
|
-
feature: 'SSRF & Private Network Guard',
|
|
930
|
-
status: 'stable',
|
|
931
|
-
evidence: 'bun test packages/synapse/test/production-hardening.test.ts'
|
|
932
|
-
},
|
|
933
|
-
{
|
|
934
|
-
feature: 'Bounded LRU SSR Cache & Query Normalization',
|
|
935
|
-
status: 'stable',
|
|
936
|
-
evidence: 'bun test packages/synapse/test/production-hardening.test.ts'
|
|
937
|
-
},
|
|
938
|
-
{
|
|
939
|
-
feature: 'SQLite Queue Zombie Job Visibility Recovery',
|
|
940
|
-
status: 'stable',
|
|
941
|
-
evidence: 'bun test packages/synapse/test/production-hardening.test.ts'
|
|
942
|
-
},
|
|
943
|
-
{
|
|
944
|
-
feature: 'HttpOnly Cookie Authentication via ActionContext',
|
|
945
|
-
status: 'stable',
|
|
946
|
-
evidence: 'bun test packages/synapse/test/production-hardening.test.ts'
|
|
947
|
-
}
|
|
948
|
-
],
|
|
949
|
-
commands: [
|
|
950
|
-
'new',
|
|
951
|
-
'dev',
|
|
952
|
-
'start',
|
|
953
|
-
'check',
|
|
954
|
-
'migrate',
|
|
955
|
-
'rollback',
|
|
956
|
-
'db-drift',
|
|
957
|
-
'impact',
|
|
958
|
-
'mcp',
|
|
959
|
-
'skeleton',
|
|
960
|
-
'db-schema',
|
|
961
|
-
'split',
|
|
962
|
-
'build',
|
|
963
|
-
'test',
|
|
964
|
-
'worker',
|
|
965
|
-
'new-slice',
|
|
966
|
-
'contract',
|
|
967
|
-
'info'
|
|
968
|
-
]
|
|
1022
|
+
status: 'FAIL',
|
|
1023
|
+
code: 'UNKNOWN_COMMAND',
|
|
1024
|
+
operation: 'COMMAND_VALIDATION',
|
|
1025
|
+
message: `Comando desconhecido: ${command}.`,
|
|
1026
|
+
commands: Object.keys(KNOWN_FLAGS)
|
|
969
1027
|
},
|
|
970
1028
|
null,
|
|
971
1029
|
2
|
|
972
1030
|
)}\n`
|
|
973
1031
|
);
|
|
974
|
-
process.exit(0);
|
|
975
|
-
break;
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
default: {
|
|
979
|
-
process.stderr.write(
|
|
980
|
-
`Unknown command: ${command}\nAvailable: start, dev, check, migrate, rollback, mcp, skeleton, db-schema, db-drift, impact, split, build, test, worker, new, new-slice, contract, info\n`
|
|
981
|
-
);
|
|
982
1032
|
process.exit(1);
|
|
983
1033
|
}
|
|
984
1034
|
}
|