@byline/cli 5.0.0 → 5.1.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/dist/phases/wire/server-ts.d.ts.map +1 -1
- package/dist/phases/wire/server-ts.js +72 -17
- package/dist/phases/wire/server-ts.js.map +1 -1
- package/dist/phases/wire/server-ts.test.d.ts +2 -0
- package/dist/phases/wire/server-ts.test.d.ts.map +1 -0
- package/dist/phases/wire/server-ts.test.js +37 -0
- package/dist/phases/wire/server-ts.test.js.map +1 -0
- package/dist/phases/wire/start-ts.d.ts +7 -0
- package/dist/phases/wire/start-ts.d.ts.map +1 -1
- package/dist/phases/wire/start-ts.js +76 -10
- package/dist/phases/wire/start-ts.js.map +1 -1
- package/dist/phases/wire/start-ts.test.d.ts +9 -0
- package/dist/phases/wire/start-ts.test.d.ts.map +1 -0
- package/dist/phases/wire/start-ts.test.js +50 -0
- package/dist/phases/wire/start-ts.test.js.map +1 -0
- package/dist/templates/dialects/mysql/byline/server.config.ts +15 -1
- package/dist/templates/dialects/mysql/byline-examples/server.config.ts +15 -1
- package/dist/templates/dialects/postgres/byline/server.config.ts +15 -1
- package/dist/templates/dialects/postgres/byline-examples/server.config.ts +15 -1
- package/dist/templates/migrations/mysql/{0000_pale_bastion.sql → 0000_kind_exodus.sql} +24 -0
- package/dist/templates/migrations/mysql/meta/_journal.json +2 -2
- package/dist/templates/migrations/postgres/{0000_yielding_vulture.sql → 0000_lively_talisman.sql} +22 -0
- package/dist/templates/migrations/postgres/meta/_journal.json +2 -2
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-ts.d.ts","sourceRoot":"","sources":["../../../src/phases/wire/server-ts.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"server-ts.d.ts","sourceRoot":"","sources":["../../../src/phases/wire/server-ts.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,aAAa,CAAA;AAezD,eAAO,MAAM,YAAY,EAAE,OAS1B,CAAA"}
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { Project } from 'ts-morph';
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { Project, SyntaxKind } from 'ts-morph';
|
|
3
3
|
const REL = 'src/server.ts';
|
|
4
4
|
const IMPORT_SPECIFIER = '../byline/server.config';
|
|
5
5
|
const SNIPPET = `// Initialize Byline server config (DB adapter, etc.) before handling any requests.
|
|
6
6
|
import '${IMPORT_SPECIFIER}'
|
|
7
|
+
import { getBylineCore } from '@byline/core'
|
|
8
|
+
import { startBylineScheduler } from '@byline/core/scheduler'
|
|
9
|
+
declare global {
|
|
10
|
+
// biome-ignore lint: globalThis augmentation requires var rather than let
|
|
11
|
+
var __bylineSchedulerController__: ReturnType<typeof startBylineScheduler> | undefined
|
|
12
|
+
}
|
|
13
|
+
globalThis.__bylineSchedulerController__ ??= startBylineScheduler(getBylineCore())
|
|
7
14
|
`;
|
|
8
15
|
export const wireServerTs = {
|
|
9
16
|
key: 'server-ts',
|
|
10
|
-
title: `
|
|
17
|
+
title: `Initialize Byline and start its scheduler in ${REL}`,
|
|
11
18
|
async preview(ctx) {
|
|
12
19
|
return run(ctx, true);
|
|
13
20
|
},
|
|
@@ -21,29 +28,77 @@ async function run(ctx, dryRun) {
|
|
|
21
28
|
return { status: 'blocked', message: `${REL} not found — host phase should have caught this` };
|
|
22
29
|
}
|
|
23
30
|
const text = readFileSync(path, 'utf8');
|
|
24
|
-
|
|
25
|
-
return { status: 'skipped', message: `${REL}: side-effect import already present` };
|
|
26
|
-
}
|
|
27
|
-
if (dryRun) {
|
|
28
|
-
return { status: 'done', message: `${REL}: will inject \`import '${IMPORT_SPECIFIER}'\`` };
|
|
29
|
-
}
|
|
30
|
-
const project = new Project({ useInMemoryFileSystem: false, skipAddingFilesFromTsConfig: true });
|
|
31
|
+
const project = new Project({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
|
|
31
32
|
let source;
|
|
32
33
|
try {
|
|
33
|
-
source = project.
|
|
34
|
+
source = project.createSourceFile('server.ts', text);
|
|
34
35
|
}
|
|
35
36
|
catch (_e) {
|
|
36
37
|
return {
|
|
37
38
|
status: 'manual',
|
|
38
|
-
message: `${REL}: could not parse — please
|
|
39
|
+
message: `${REL}: could not parse — please wire initialization and scheduler manually`,
|
|
39
40
|
snippet: SNIPPET,
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
let changed = false;
|
|
44
|
+
if (!hasImport(text, IMPORT_SPECIFIER)) {
|
|
45
|
+
source.insertImportDeclaration(0, { moduleSpecifier: IMPORT_SPECIFIER });
|
|
46
|
+
changed = true;
|
|
47
|
+
}
|
|
48
|
+
const schedulerImport = source
|
|
49
|
+
.getImportDeclarations()
|
|
50
|
+
.find((declaration) => declaration.getModuleSpecifierValue() === '@byline/core/scheduler');
|
|
51
|
+
const schedulerName = schedulerImport
|
|
52
|
+
?.getNamedImports()
|
|
53
|
+
.find((item) => item.getName() === 'startBylineScheduler');
|
|
54
|
+
const localSchedulerName = schedulerName?.getAliasNode()?.getText() ?? schedulerName?.getName() ?? 'startBylineScheduler';
|
|
55
|
+
const alreadyStarted = schedulerName !== undefined &&
|
|
56
|
+
source
|
|
57
|
+
.getDescendantsOfKind(SyntaxKind.CallExpression)
|
|
58
|
+
.some((call) => call.getExpression().getText() === localSchedulerName);
|
|
59
|
+
if (!alreadyStarted) {
|
|
60
|
+
const coreGetter = ensureImport(source, '@byline/core', 'getBylineCore');
|
|
61
|
+
const scheduler = ensureImport(source, '@byline/core/scheduler', 'startBylineScheduler');
|
|
62
|
+
const declaration = source
|
|
63
|
+
.getDescendantsOfKind(SyntaxKind.VariableDeclaration)
|
|
64
|
+
.some((item) => item.getName() === '__bylineSchedulerController__');
|
|
65
|
+
source.addStatements(`
|
|
66
|
+
// Host-owned lifetime: config imports from seeds/migrations remain inert.
|
|
67
|
+
${declaration
|
|
68
|
+
? ''
|
|
69
|
+
: `declare global {
|
|
70
|
+
// biome-ignore lint: globalThis augmentation requires var rather than let
|
|
71
|
+
var __bylineSchedulerController__: ReturnType<typeof ${scheduler}> | undefined
|
|
72
|
+
}`}
|
|
73
|
+
globalThis.__bylineSchedulerController__ ??= ${scheduler}(${coreGetter}())
|
|
74
|
+
`);
|
|
75
|
+
changed = true;
|
|
76
|
+
}
|
|
77
|
+
if (!changed)
|
|
78
|
+
return {
|
|
79
|
+
status: 'skipped',
|
|
80
|
+
message: `${REL}: Byline initialization and scheduler already present`,
|
|
81
|
+
};
|
|
82
|
+
if (!dryRun) {
|
|
83
|
+
writeFileSync(path, source.getFullText());
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
status: 'done',
|
|
87
|
+
message: `${REL}: ${dryRun ? 'will wire' : 'wired'} Byline initialization and scheduler`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function ensureImport(source, moduleSpecifier, name) {
|
|
91
|
+
const declaration = source
|
|
92
|
+
.getImportDeclarations()
|
|
93
|
+
.find((item) => item.getModuleSpecifierValue() === moduleSpecifier && !item.isTypeOnly());
|
|
94
|
+
const existing = declaration?.getNamedImports().find((item) => item.getName() === name);
|
|
95
|
+
if (existing)
|
|
96
|
+
return existing.getAliasNode()?.getText() ?? existing.getName();
|
|
97
|
+
if (declaration)
|
|
98
|
+
declaration.addNamedImport(name);
|
|
99
|
+
else
|
|
100
|
+
source.addImportDeclaration({ moduleSpecifier, namedImports: [name] });
|
|
101
|
+
return name;
|
|
47
102
|
}
|
|
48
103
|
function hasImport(source, specifier) {
|
|
49
104
|
// Match either form: with or without `.ts` extension, single or double quote.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-ts.js","sourceRoot":"","sources":["../../../src/phases/wire/server-ts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"server-ts.js","sourceRoot":"","sources":["../../../src/phases/wire/server-ts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-ts.test.d.ts","sourceRoot":"","sources":["../../../src/phases/wire/server-ts.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
+
import { createTestContext } from '../../test-helpers.js';
|
|
4
|
+
import { wireServerTs } from './server-ts.js';
|
|
5
|
+
const contexts = [];
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
for (const ctx of contexts.splice(0))
|
|
8
|
+
rmSync(ctx.cwd, { recursive: true, force: true });
|
|
9
|
+
});
|
|
10
|
+
function fixture(text) {
|
|
11
|
+
const ctx = createTestContext();
|
|
12
|
+
contexts.push(ctx);
|
|
13
|
+
mkdirSync(ctx.resolve('src'), { recursive: true });
|
|
14
|
+
writeFileSync(ctx.resolve('src/server.ts'), text);
|
|
15
|
+
return ctx;
|
|
16
|
+
}
|
|
17
|
+
describe('host scheduler wiring', () => {
|
|
18
|
+
it('upgrades an existing config import, preserves the handler, and is idempotent', async () => {
|
|
19
|
+
const ctx = fixture("import '../byline/server.config.ts'\nexport default handler");
|
|
20
|
+
const original = readFileSync(ctx.resolve('src/server.ts'), 'utf8');
|
|
21
|
+
expect((await wireServerTs.preview(ctx)).status).toBe('done');
|
|
22
|
+
expect(readFileSync(ctx.resolve('src/server.ts'), 'utf8')).toBe(original);
|
|
23
|
+
expect((await wireServerTs.apply(ctx)).status).toBe('done');
|
|
24
|
+
const text = readFileSync(ctx.resolve('src/server.ts'), 'utf8');
|
|
25
|
+
expect(text).toContain('globalThis.__bylineSchedulerController__ ??= startBylineScheduler(getBylineCore())');
|
|
26
|
+
expect(text).toContain('export default handler');
|
|
27
|
+
expect((await wireServerTs.apply(ctx)).status).toBe('skipped');
|
|
28
|
+
expect(readFileSync(ctx.resolve('src/server.ts'), 'utf8')).toBe(text);
|
|
29
|
+
});
|
|
30
|
+
it('preserves an existing aliased scheduler startup', async () => {
|
|
31
|
+
const text = "import '../byline/server.config'\nimport { startBylineScheduler as start } from '@byline/core/scheduler'\nstart(core, { concurrency: 2 })";
|
|
32
|
+
const ctx = fixture(text);
|
|
33
|
+
expect((await wireServerTs.apply(ctx)).status).toBe('skipped');
|
|
34
|
+
expect(readFileSync(ctx.resolve('src/server.ts'), 'utf8')).toBe(text);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=server-ts.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-ts.test.js","sourceRoot":"","sources":["../../../src/phases/wire/server-ts.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
1
8
|
import type { SubEdit } from './shared.js';
|
|
2
9
|
export declare const wireStartTs: SubEdit;
|
|
3
10
|
//# sourceMappingURL=start-ts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start-ts.d.ts","sourceRoot":"","sources":["../../../src/phases/wire/start-ts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"start-ts.d.ts","sourceRoot":"","sources":["../../../src/phases/wire/start-ts.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,aAAa,CAAA;AAiBzD,eAAO,MAAM,WAAW,EAAE,OASzB,CAAA"}
|
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
2
9
|
import { Node, Project, SyntaxKind, } from 'ts-morph';
|
|
3
10
|
const REL = 'src/start.ts';
|
|
4
11
|
const ADAPTER_NAME = 'bylineCodedErrorAdapter';
|
|
5
12
|
const ADAPTER_MODULE = '@byline/host-tanstack-start/integrations/start-errors';
|
|
6
|
-
const SNIPPET = `import { createStart } from '@tanstack/react-start'
|
|
13
|
+
const SNIPPET = `import { createCsrfMiddleware, createStart } from '@tanstack/react-start'
|
|
7
14
|
|
|
8
15
|
import { ${ADAPTER_NAME} } from '${ADAPTER_MODULE}'
|
|
16
|
+
import { passwordSignInMiddleware } from '@byline/host-tanstack-start/integrations/sign-in-middleware'
|
|
17
|
+
import { sessionRequestMiddleware } from '@byline/host-tanstack-start/integrations/session-request-middleware'
|
|
9
18
|
|
|
10
19
|
export const startInstance = createStart(() => ({
|
|
11
20
|
serializationAdapters: [${ADAPTER_NAME}],
|
|
21
|
+
requestMiddleware: [createCsrfMiddleware({ filter: (ctx) => ctx.handlerType === 'serverFn' }), sessionRequestMiddleware, passwordSignInMiddleware],
|
|
12
22
|
}))
|
|
13
23
|
`;
|
|
14
24
|
export const wireStartTs = {
|
|
@@ -26,13 +36,6 @@ async function run(ctx, dryRun) {
|
|
|
26
36
|
if (!existsSync(path)) {
|
|
27
37
|
return { status: 'blocked', message: `${REL} not found — host phase should have caught this` };
|
|
28
38
|
}
|
|
29
|
-
const text = readFileSync(path, 'utf8');
|
|
30
|
-
// Cheap pre-check: if the adapter identifier is already referenced anywhere,
|
|
31
|
-
// assume the wiring is in place. Avoids a full AST round-trip on the common
|
|
32
|
-
// case where wire has already been run.
|
|
33
|
-
if (text.includes(ADAPTER_NAME)) {
|
|
34
|
-
return { status: 'skipped', message: `${REL}: ${ADAPTER_NAME} already registered` };
|
|
35
|
-
}
|
|
36
39
|
const project = new Project({ useInMemoryFileSystem: false, skipAddingFilesFromTsConfig: true });
|
|
37
40
|
let source;
|
|
38
41
|
try {
|
|
@@ -49,13 +52,76 @@ async function run(ctx, dryRun) {
|
|
|
49
52
|
if (!optionsLiteral) {
|
|
50
53
|
return manualBail(`${REL}: \`createStart\` factory does not return an inline object literal — cannot safely auto-edit`);
|
|
51
54
|
}
|
|
55
|
+
const middlewareProp = optionsLiteral.getProperty('requestMiddleware');
|
|
56
|
+
if (middlewareProp &&
|
|
57
|
+
(!Node.isPropertyAssignment(middlewareProp) ||
|
|
58
|
+
!Node.isArrayLiteralExpression(middlewareProp.getInitializer()))) {
|
|
59
|
+
return manualBail(`${REL}: requestMiddleware must be an inline array for safe editing`);
|
|
60
|
+
}
|
|
61
|
+
const arr = middlewareProp && Node.isPropertyAssignment(middlewareProp)
|
|
62
|
+
? middlewareProp.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression)
|
|
63
|
+
: undefined;
|
|
64
|
+
const hasCsrf = arr?.getElements().some((el) => {
|
|
65
|
+
const expression = Node.isIdentifier(el)
|
|
66
|
+
? source.getVariableDeclaration(el.getText())?.getInitializer()
|
|
67
|
+
: el;
|
|
68
|
+
return (expression &&
|
|
69
|
+
Node.isCallExpression(expression) &&
|
|
70
|
+
expression.getExpression().getText() === 'createCsrfMiddleware');
|
|
71
|
+
}) ?? false;
|
|
72
|
+
const hasSessionGuard = arr?.getElements().some((el) => el.getText() === 'sessionRequestMiddleware') ?? false;
|
|
73
|
+
const hasBodyGuard = arr?.getElements().some((el) => el.getText() === 'passwordSignInMiddleware') ?? false;
|
|
74
|
+
const adapterProp = optionsLiteral.getProperty('serializationAdapters');
|
|
75
|
+
const hasAdapter = adapterProp &&
|
|
76
|
+
Node.isPropertyAssignment(adapterProp) &&
|
|
77
|
+
adapterProp
|
|
78
|
+
.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression)
|
|
79
|
+
?.getElements()
|
|
80
|
+
.some((el) => el.getText() === ADAPTER_NAME);
|
|
81
|
+
if (hasCsrf && hasBodyGuard && hasSessionGuard && hasAdapter) {
|
|
82
|
+
return {
|
|
83
|
+
status: 'skipped',
|
|
84
|
+
message: `${REL}: sign-in protection and error serialization already registered`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
52
87
|
if (dryRun) {
|
|
53
88
|
return {
|
|
54
89
|
status: 'done',
|
|
55
|
-
message: `${REL}: will
|
|
90
|
+
message: `${REL}: will register sign-in protection and error serialization`,
|
|
56
91
|
};
|
|
57
92
|
}
|
|
58
93
|
ensureImport(source);
|
|
94
|
+
for (const [moduleSpecifier, name] of [
|
|
95
|
+
['@byline/host-tanstack-start/integrations/sign-in-middleware', 'passwordSignInMiddleware'],
|
|
96
|
+
['@tanstack/react-start', 'createCsrfMiddleware'],
|
|
97
|
+
[
|
|
98
|
+
'@byline/host-tanstack-start/integrations/session-request-middleware',
|
|
99
|
+
'sessionRequestMiddleware',
|
|
100
|
+
],
|
|
101
|
+
]) {
|
|
102
|
+
const declaration = source
|
|
103
|
+
.getImportDeclarations()
|
|
104
|
+
.find((d) => d.getModuleSpecifierValue() === moduleSpecifier);
|
|
105
|
+
if (!declaration)
|
|
106
|
+
source.addImportDeclaration({ moduleSpecifier: moduleSpecifier, namedImports: [name] });
|
|
107
|
+
else if (!declaration.getNamedImports().some((n) => n.getText() === name))
|
|
108
|
+
declaration.addNamedImport(name);
|
|
109
|
+
}
|
|
110
|
+
const csrf = "createCsrfMiddleware({ filter: (ctx) => ctx.handlerType === 'serverFn' })";
|
|
111
|
+
if (!arr) {
|
|
112
|
+
optionsLiteral.addPropertyAssignment({
|
|
113
|
+
name: 'requestMiddleware',
|
|
114
|
+
initializer: `[${csrf}, sessionRequestMiddleware, passwordSignInMiddleware]`,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
if (!hasCsrf)
|
|
119
|
+
arr.insertElement(0, csrf);
|
|
120
|
+
if (!hasSessionGuard)
|
|
121
|
+
arr.addElement('sessionRequestMiddleware');
|
|
122
|
+
if (!hasBodyGuard)
|
|
123
|
+
arr.addElement('passwordSignInMiddleware');
|
|
124
|
+
}
|
|
59
125
|
ensureAdapterInOptions(optionsLiteral);
|
|
60
126
|
source.saveSync();
|
|
61
127
|
return { status: 'done', message: `${REL}: registered ${ADAPTER_NAME}` };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start-ts.js","sourceRoot":"","sources":["../../../src/phases/wire/start-ts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"start-ts.js","sourceRoot":"","sources":["../../../src/phases/wire/start-ts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=start-ts.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-ts.test.d.ts","sourceRoot":"","sources":["../../../src/phases/wire/start-ts.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
10
|
+
import { createTestContext } from '../../test-helpers.js';
|
|
11
|
+
import { wireStartTs } from './start-ts.js';
|
|
12
|
+
const contexts = [];
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
for (const ctx of contexts.splice(0))
|
|
15
|
+
rmSync(ctx.cwd, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
function fixture(options) {
|
|
18
|
+
const ctx = createTestContext();
|
|
19
|
+
contexts.push(ctx);
|
|
20
|
+
mkdirSync(ctx.resolve('src'), { recursive: true });
|
|
21
|
+
writeFileSync(ctx.resolve('src/start.ts'), `import { createStart, createCsrfMiddleware } from '@tanstack/react-start'
|
|
22
|
+
import { bylineCodedErrorAdapter } from '@byline/host-tanstack-start/integrations/start-errors'
|
|
23
|
+
const csrf = createCsrfMiddleware({filter: ctx => ctx.handlerType === 'serverFn'})
|
|
24
|
+
export const startInstance = createStart(() => (${options}))`);
|
|
25
|
+
return ctx;
|
|
26
|
+
}
|
|
27
|
+
describe('Start sign-in middleware wiring', () => {
|
|
28
|
+
it('upgrades existing serialization-only wiring and is idempotent', async () => {
|
|
29
|
+
const ctx = fixture('{serializationAdapters:[bylineCodedErrorAdapter]}');
|
|
30
|
+
expect((await wireStartTs.apply(ctx)).status).toBe('done');
|
|
31
|
+
const text = readFileSync(ctx.resolve('src/start.ts'), 'utf8');
|
|
32
|
+
expect(text).toContain('requestMiddleware: [createCsrfMiddleware(');
|
|
33
|
+
expect(text).toContain('passwordSignInMiddleware]');
|
|
34
|
+
expect((await wireStartTs.apply(ctx)).status).toBe('skipped');
|
|
35
|
+
expect(readFileSync(ctx.resolve('src/start.ts'), 'utf8')).toBe(text);
|
|
36
|
+
});
|
|
37
|
+
it('preserves existing CSRF and unrelated middleware', async () => {
|
|
38
|
+
const ctx = fixture('{serializationAdapters:[bylineCodedErrorAdapter],requestMiddleware:[csrf, custom]}');
|
|
39
|
+
expect((await wireStartTs.apply(ctx)).status).toBe('done');
|
|
40
|
+
expect(readFileSync(ctx.resolve('src/start.ts'), 'utf8')).toContain('[csrf, custom, sessionRequestMiddleware, passwordSignInMiddleware]');
|
|
41
|
+
});
|
|
42
|
+
it('leaves computed middleware configuration for manual wiring', async () => {
|
|
43
|
+
const ctx = fixture('{requestMiddleware: buildMiddleware()}');
|
|
44
|
+
const original = readFileSync(ctx.resolve('src/start.ts'), 'utf8');
|
|
45
|
+
expect((await wireStartTs.preview(ctx)).status).toBe('manual');
|
|
46
|
+
expect((await wireStartTs.apply(ctx)).status).toBe('manual');
|
|
47
|
+
expect(readFileSync(ctx.resolve('src/start.ts'), 'utf8')).toBe(original);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=start-ts.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-ts.test.js","sourceRoot":"","sources":["../../../src/phases/wire/start-ts.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { type AdminStore, registerAdminAbilities } from '@byline/admin'
|
|
17
|
-
import { JwtSessionProvider } from '@byline/admin/auth'
|
|
17
|
+
import { createPasswordSignInLimiter, JwtSessionProvider } from '@byline/admin/auth'
|
|
18
|
+
import { getRequestIP } from '@tanstack/react-start/server'
|
|
19
|
+
import { createClientIpResolver } from '@byline/host-tanstack-start/integrations/client-ip'
|
|
18
20
|
import { getAdminBylineClient } from '@byline/client/server'
|
|
19
21
|
import { type BylineCore, initBylineCore } from '@byline/core'
|
|
20
22
|
import { mysqlAdapter } from '@byline/db-mysql'
|
|
@@ -67,6 +69,8 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
67
69
|
)
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
const signInLimiter = createPasswordSignInLimiter(adminStore.signInRateLimits, signingSecret)
|
|
73
|
+
|
|
70
74
|
const sessionProvider = new JwtSessionProvider({
|
|
71
75
|
store: adminStore,
|
|
72
76
|
signingSecret,
|
|
@@ -86,6 +90,16 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
86
90
|
baseUrl: '/uploads',
|
|
87
91
|
}),
|
|
88
92
|
sessionProvider,
|
|
93
|
+
recurringTasks: [signInLimiter.cleanupTask],
|
|
94
|
+
passwordSignIn: {
|
|
95
|
+
limiter: signInLimiter,
|
|
96
|
+
resolveClientIp: createClientIpResolver({
|
|
97
|
+
// Opt in only when the proxy overwrites this header and direct access is blocked.
|
|
98
|
+
trustedProxyHeader: process.env.BYLINE_TRUSTED_CLIENT_IP_HEADER || undefined,
|
|
99
|
+
resolvePeerIp: () => getRequestIP() ??
|
|
100
|
+
(process.env.NODE_ENV === 'development' ? '127.0.0.1' : null),
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
89
103
|
fields: {
|
|
90
104
|
richText: {
|
|
91
105
|
embed: lexicalEditorEmbedServer({ getClient: getAdminBylineClient }),
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { type AdminStore, registerAdminAbilities } from '@byline/admin'
|
|
14
|
-
import { JwtSessionProvider } from '@byline/admin/auth'
|
|
14
|
+
import { createPasswordSignInLimiter, JwtSessionProvider } from '@byline/admin/auth'
|
|
15
|
+
import { getRequestIP } from '@tanstack/react-start/server'
|
|
16
|
+
import { createClientIpResolver } from '@byline/host-tanstack-start/integrations/client-ip'
|
|
15
17
|
import { getAdminBylineClient } from '@byline/client/server'
|
|
16
18
|
import { type BylineCore, initBylineCore } from '@byline/core'
|
|
17
19
|
import { mysqlAdapter } from '@byline/db-mysql'
|
|
@@ -75,6 +77,8 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
75
77
|
)
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
const signInLimiter = createPasswordSignInLimiter(adminStore.signInRateLimits, signingSecret)
|
|
81
|
+
|
|
78
82
|
const sessionProvider = new JwtSessionProvider({
|
|
79
83
|
store: adminStore,
|
|
80
84
|
signingSecret,
|
|
@@ -94,6 +98,16 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
94
98
|
baseUrl: '/uploads',
|
|
95
99
|
}),
|
|
96
100
|
sessionProvider,
|
|
101
|
+
recurringTasks: [signInLimiter.cleanupTask],
|
|
102
|
+
passwordSignIn: {
|
|
103
|
+
limiter: signInLimiter,
|
|
104
|
+
resolveClientIp: createClientIpResolver({
|
|
105
|
+
// Opt in only when the proxy overwrites this header and direct access is blocked.
|
|
106
|
+
trustedProxyHeader: process.env.BYLINE_TRUSTED_CLIENT_IP_HEADER || undefined,
|
|
107
|
+
resolvePeerIp: () => getRequestIP() ??
|
|
108
|
+
(process.env.NODE_ENV === 'development' ? '127.0.0.1' : null),
|
|
109
|
+
}),
|
|
110
|
+
},
|
|
97
111
|
fields: {
|
|
98
112
|
richText: {
|
|
99
113
|
embed: lexicalEditorEmbedServer({ getClient: getAdminBylineClient }),
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { type AdminStore, registerAdminAbilities } from '@byline/admin'
|
|
21
|
-
import { JwtSessionProvider } from '@byline/admin/auth'
|
|
21
|
+
import { createPasswordSignInLimiter, JwtSessionProvider } from '@byline/admin/auth'
|
|
22
|
+
import { getRequestIP } from '@tanstack/react-start/server'
|
|
23
|
+
import { createClientIpResolver } from '@byline/host-tanstack-start/integrations/client-ip'
|
|
22
24
|
import { type BylineCore, initBylineCore } from '@byline/core'
|
|
23
25
|
import { pgAdapter } from '@byline/db-postgres'
|
|
24
26
|
import { createAdminStore } from '@byline/db-postgres/admin'
|
|
@@ -82,6 +84,8 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
82
84
|
)
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
const signInLimiter = createPasswordSignInLimiter(adminStore.signInRateLimits, signingSecret)
|
|
88
|
+
|
|
85
89
|
const sessionProvider = new JwtSessionProvider({
|
|
86
90
|
store: adminStore,
|
|
87
91
|
signingSecret,
|
|
@@ -136,6 +140,16 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
136
140
|
// cacheControl: 'public, max-age=31536000, immutable',
|
|
137
141
|
// }),
|
|
138
142
|
sessionProvider,
|
|
143
|
+
recurringTasks: [signInLimiter.cleanupTask],
|
|
144
|
+
passwordSignIn: {
|
|
145
|
+
limiter: signInLimiter,
|
|
146
|
+
resolveClientIp: createClientIpResolver({
|
|
147
|
+
// Opt in only when the proxy overwrites this header and direct access is blocked.
|
|
148
|
+
trustedProxyHeader: process.env.BYLINE_TRUSTED_CLIENT_IP_HEADER || undefined,
|
|
149
|
+
resolvePeerIp: () => getRequestIP() ??
|
|
150
|
+
(process.env.NODE_ENV === 'development' ? '127.0.0.1' : null),
|
|
151
|
+
}),
|
|
152
|
+
},
|
|
139
153
|
fields: {
|
|
140
154
|
richText: {
|
|
141
155
|
embed: lexicalEditorEmbedServer({ getClient: getAdminBylineClient }),
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { type AdminStore, registerAdminAbilities } from '@byline/admin'
|
|
18
|
-
import { JwtSessionProvider } from '@byline/admin/auth'
|
|
18
|
+
import { createPasswordSignInLimiter, JwtSessionProvider } from '@byline/admin/auth'
|
|
19
|
+
import { getRequestIP } from '@tanstack/react-start/server'
|
|
20
|
+
import { createClientIpResolver } from '@byline/host-tanstack-start/integrations/client-ip'
|
|
19
21
|
import { type BylineCore, initBylineCore } from '@byline/core'
|
|
20
22
|
import { pgAdapter } from '@byline/db-postgres'
|
|
21
23
|
import { createAdminStore } from '@byline/db-postgres/admin'
|
|
@@ -130,6 +132,8 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
130
132
|
)
|
|
131
133
|
}
|
|
132
134
|
|
|
135
|
+
const signInLimiter = createPasswordSignInLimiter(adminStore.signInRateLimits, signingSecret)
|
|
136
|
+
|
|
133
137
|
const sessionProvider = new JwtSessionProvider({
|
|
134
138
|
store: adminStore,
|
|
135
139
|
signingSecret,
|
|
@@ -190,6 +194,16 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
190
194
|
// cacheControl: 'public, max-age=31536000, immutable',
|
|
191
195
|
// }),
|
|
192
196
|
sessionProvider,
|
|
197
|
+
recurringTasks: [signInLimiter.cleanupTask],
|
|
198
|
+
passwordSignIn: {
|
|
199
|
+
limiter: signInLimiter,
|
|
200
|
+
resolveClientIp: createClientIpResolver({
|
|
201
|
+
// Opt in only when the proxy overwrites this header and direct access is blocked.
|
|
202
|
+
trustedProxyHeader: process.env.BYLINE_TRUSTED_CLIENT_IP_HEADER || undefined,
|
|
203
|
+
resolvePeerIp: () => getRequestIP() ??
|
|
204
|
+
(process.env.NODE_ENV === 'development' ? '127.0.0.1' : null),
|
|
205
|
+
}),
|
|
206
|
+
},
|
|
193
207
|
fields: {
|
|
194
208
|
// Server-side richtext adapter — refreshes embedded relation
|
|
195
209
|
// envelopes (link `{ title, path }`, inline-image `{ title, altText,
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
CREATE TABLE `byline_admin_login_sessions` (
|
|
2
|
+
`id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
3
|
+
`admin_user_id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
4
|
+
`session_version` int NOT NULL,
|
|
5
|
+
`expires_at` datetime(6) NOT NULL,
|
|
6
|
+
`revoked_at` datetime(6),
|
|
7
|
+
CONSTRAINT `byline_admin_login_sessions_id` PRIMARY KEY(`id`)
|
|
8
|
+
);
|
|
9
|
+
--> statement-breakpoint
|
|
1
10
|
CREATE TABLE `byline_admin_permissions` (
|
|
2
11
|
`id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
3
12
|
`vid` int NOT NULL DEFAULT 1,
|
|
@@ -12,7 +21,9 @@ CREATE TABLE `byline_admin_permissions` (
|
|
|
12
21
|
CREATE TABLE `byline_admin_refresh_tokens` (
|
|
13
22
|
`id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
14
23
|
`admin_user_id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
24
|
+
`sid` char(36) CHARACTER SET ascii COLLATE ascii_bin,
|
|
15
25
|
`token_hash` varchar(64) NOT NULL,
|
|
26
|
+
`session_version` int NOT NULL DEFAULT -1,
|
|
16
27
|
`issued_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
17
28
|
`expires_at` datetime(6) NOT NULL,
|
|
18
29
|
`revoked_at` datetime(6),
|
|
@@ -46,6 +57,13 @@ CREATE TABLE `byline_admin_roles` (
|
|
|
46
57
|
CONSTRAINT `byline_admin_roles_machine_name_unique` UNIQUE(`machine_name`)
|
|
47
58
|
);
|
|
48
59
|
--> statement-breakpoint
|
|
60
|
+
CREATE TABLE `byline_admin_sign_in_rate_limits` (
|
|
61
|
+
`key` varchar(64) NOT NULL,
|
|
62
|
+
`attempts` int NOT NULL,
|
|
63
|
+
`expires_at` datetime(3) NOT NULL,
|
|
64
|
+
CONSTRAINT `byline_admin_sign_in_rate_limits_key` PRIMARY KEY(`key`)
|
|
65
|
+
);
|
|
66
|
+
--> statement-breakpoint
|
|
49
67
|
CREATE TABLE `byline_admin_user_preferences` (
|
|
50
68
|
`user_id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
51
69
|
`scope` varchar(255) NOT NULL,
|
|
@@ -57,6 +75,7 @@ CREATE TABLE `byline_admin_user_preferences` (
|
|
|
57
75
|
--> statement-breakpoint
|
|
58
76
|
CREATE TABLE `byline_admin_users` (
|
|
59
77
|
`id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
78
|
+
`session_version` int NOT NULL DEFAULT 0,
|
|
60
79
|
`vid` int NOT NULL DEFAULT 1,
|
|
61
80
|
`given_name` varchar(100),
|
|
62
81
|
`family_name` varchar(100),
|
|
@@ -377,7 +396,9 @@ CREATE TABLE `byline_store_text` (
|
|
|
377
396
|
CONSTRAINT `unique_text_field` UNIQUE(`document_version_id`,`field_path`,`locale`)
|
|
378
397
|
);
|
|
379
398
|
--> statement-breakpoint
|
|
399
|
+
ALTER TABLE `byline_admin_login_sessions` ADD CONSTRAINT `fk_admin_login_sessions_user` FOREIGN KEY (`admin_user_id`) REFERENCES `byline_admin_users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
380
400
|
ALTER TABLE `byline_admin_permissions` ADD CONSTRAINT `fk_admin_permissions_admin_role_id` FOREIGN KEY (`admin_role_id`) REFERENCES `byline_admin_roles`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
401
|
+
ALTER TABLE `byline_admin_refresh_tokens` ADD CONSTRAINT `fk_admin_refresh_tokens_sid` FOREIGN KEY (`sid`) REFERENCES `byline_admin_login_sessions`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
381
402
|
ALTER TABLE `byline_admin_refresh_tokens` ADD CONSTRAINT `fk_admin_refresh_tokens_admin_user_id` FOREIGN KEY (`admin_user_id`) REFERENCES `byline_admin_users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
382
403
|
ALTER TABLE `byline_admin_role_admin_user` ADD CONSTRAINT `fk_admin_role_admin_user_admin_role_id` FOREIGN KEY (`admin_role_id`) REFERENCES `byline_admin_roles`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
383
404
|
ALTER TABLE `byline_admin_role_admin_user` ADD CONSTRAINT `fk_admin_role_admin_user_admin_user_id` FOREIGN KEY (`admin_user_id`) REFERENCES `byline_admin_users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
@@ -414,11 +435,14 @@ ALTER TABLE `byline_store_relation` ADD CONSTRAINT `fk_store_relation_target_col
|
|
|
414
435
|
ALTER TABLE `byline_singleton_documents` ADD CONSTRAINT `fk_singleton_documents_document` FOREIGN KEY (`collection_id`,`document_id`) REFERENCES `byline_documents`(`collection_id`,`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
415
436
|
ALTER TABLE `byline_store_text` ADD CONSTRAINT `fk_store_text_document_version_id` FOREIGN KEY (`document_version_id`) REFERENCES `byline_document_versions`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
416
437
|
ALTER TABLE `byline_store_text` ADD CONSTRAINT `fk_store_text_collection_id` FOREIGN KEY (`collection_id`) REFERENCES `byline_collections`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
438
|
+
CREATE INDEX `idx_admin_login_sessions_user` ON `byline_admin_login_sessions` (`admin_user_id`);--> statement-breakpoint
|
|
417
439
|
CREATE INDEX `idx_byline_admin_permissions_role` ON `byline_admin_permissions` (`admin_role_id`);--> statement-breakpoint
|
|
440
|
+
CREATE INDEX `idx_admin_refresh_tokens_sid` ON `byline_admin_refresh_tokens` (`sid`);--> statement-breakpoint
|
|
418
441
|
CREATE INDEX `idx_byline_admin_refresh_tokens_user` ON `byline_admin_refresh_tokens` (`admin_user_id`);--> statement-breakpoint
|
|
419
442
|
CREATE INDEX `idx_byline_admin_refresh_tokens_token_hash` ON `byline_admin_refresh_tokens` (`token_hash`);--> statement-breakpoint
|
|
420
443
|
CREATE INDEX `idx_byline_admin_role_admin_user_user` ON `byline_admin_role_admin_user` (`admin_user_id`);--> statement-breakpoint
|
|
421
444
|
CREATE INDEX `idx_byline_admin_roles_machine_name` ON `byline_admin_roles` (`machine_name`);--> statement-breakpoint
|
|
445
|
+
CREATE INDEX `idx_admin_sign_in_rate_limits_expiry` ON `byline_admin_sign_in_rate_limits` (`expires_at`);--> statement-breakpoint
|
|
422
446
|
CREATE INDEX `idx_byline_admin_users_email` ON `byline_admin_users` (`email`);--> statement-breakpoint
|
|
423
447
|
CREATE INDEX `idx_audit_log_document_id` ON `byline_audit_log` (`document_id`,`id`);--> statement-breakpoint
|
|
424
448
|
CREATE INDEX `idx_audit_log_actor_id` ON `byline_audit_log` (`actor_id`,`id`);--> statement-breakpoint
|
package/dist/templates/migrations/postgres/{0000_yielding_vulture.sql → 0000_lively_talisman.sql}
RENAMED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
CREATE TABLE "byline_admin_login_sessions" (
|
|
2
|
+
"id" uuid PRIMARY KEY NOT NULL,
|
|
3
|
+
"admin_user_id" uuid NOT NULL,
|
|
4
|
+
"session_version" integer NOT NULL,
|
|
5
|
+
"expires_at" timestamp (6) with time zone NOT NULL,
|
|
6
|
+
"revoked_at" timestamp (6) with time zone
|
|
7
|
+
);
|
|
8
|
+
--> statement-breakpoint
|
|
1
9
|
CREATE TABLE "byline_admin_permissions" (
|
|
2
10
|
"id" uuid PRIMARY KEY NOT NULL,
|
|
3
11
|
"vid" integer DEFAULT 1 NOT NULL,
|
|
@@ -11,7 +19,9 @@ CREATE TABLE "byline_admin_permissions" (
|
|
|
11
19
|
CREATE TABLE "byline_admin_refresh_tokens" (
|
|
12
20
|
"id" uuid PRIMARY KEY NOT NULL,
|
|
13
21
|
"admin_user_id" uuid NOT NULL,
|
|
22
|
+
"sid" uuid,
|
|
14
23
|
"token_hash" varchar(64) NOT NULL,
|
|
24
|
+
"session_version" integer DEFAULT -1 NOT NULL,
|
|
15
25
|
"issued_at" timestamp (6) with time zone DEFAULT now() NOT NULL,
|
|
16
26
|
"expires_at" timestamp (6) with time zone NOT NULL,
|
|
17
27
|
"revoked_at" timestamp (6) with time zone,
|
|
@@ -43,6 +53,12 @@ CREATE TABLE "byline_admin_roles" (
|
|
|
43
53
|
CONSTRAINT "byline_admin_roles_machine_name_unique" UNIQUE("machine_name")
|
|
44
54
|
);
|
|
45
55
|
--> statement-breakpoint
|
|
56
|
+
CREATE TABLE "byline_admin_sign_in_rate_limits" (
|
|
57
|
+
"key" varchar(64) PRIMARY KEY NOT NULL,
|
|
58
|
+
"attempts" integer NOT NULL,
|
|
59
|
+
"expires_at" timestamp (3) with time zone NOT NULL
|
|
60
|
+
);
|
|
61
|
+
--> statement-breakpoint
|
|
46
62
|
CREATE TABLE "byline_admin_user_preferences" (
|
|
47
63
|
"user_id" uuid NOT NULL,
|
|
48
64
|
"scope" varchar(255) NOT NULL,
|
|
@@ -54,6 +70,7 @@ CREATE TABLE "byline_admin_user_preferences" (
|
|
|
54
70
|
--> statement-breakpoint
|
|
55
71
|
CREATE TABLE "byline_admin_users" (
|
|
56
72
|
"id" uuid PRIMARY KEY NOT NULL,
|
|
73
|
+
"session_version" integer DEFAULT 0 NOT NULL,
|
|
57
74
|
"vid" integer DEFAULT 1 NOT NULL,
|
|
58
75
|
"given_name" varchar(100),
|
|
59
76
|
"family_name" varchar(100),
|
|
@@ -356,8 +373,10 @@ CREATE TABLE "byline_store_text" (
|
|
|
356
373
|
CONSTRAINT "unique_text_field" UNIQUE("document_version_id","field_path","locale")
|
|
357
374
|
);
|
|
358
375
|
--> statement-breakpoint
|
|
376
|
+
ALTER TABLE "byline_admin_login_sessions" ADD CONSTRAINT "byline_admin_login_sessions_admin_user_id_byline_admin_users_id_fk" FOREIGN KEY ("admin_user_id") REFERENCES "public"."byline_admin_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
359
377
|
ALTER TABLE "byline_admin_permissions" ADD CONSTRAINT "byline_admin_permissions_admin_role_id_byline_admin_roles_id_fk" FOREIGN KEY ("admin_role_id") REFERENCES "public"."byline_admin_roles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
360
378
|
ALTER TABLE "byline_admin_refresh_tokens" ADD CONSTRAINT "byline_admin_refresh_tokens_admin_user_id_byline_admin_users_id_fk" FOREIGN KEY ("admin_user_id") REFERENCES "public"."byline_admin_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
379
|
+
ALTER TABLE "byline_admin_refresh_tokens" ADD CONSTRAINT "byline_admin_refresh_tokens_sid_byline_admin_login_sessions_id_fk" FOREIGN KEY ("sid") REFERENCES "public"."byline_admin_login_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
361
380
|
ALTER TABLE "byline_admin_role_admin_user" ADD CONSTRAINT "byline_admin_role_admin_user_admin_role_id_byline_admin_roles_id_fk" FOREIGN KEY ("admin_role_id") REFERENCES "public"."byline_admin_roles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
362
381
|
ALTER TABLE "byline_admin_role_admin_user" ADD CONSTRAINT "byline_admin_role_admin_user_admin_user_id_byline_admin_users_id_fk" FOREIGN KEY ("admin_user_id") REFERENCES "public"."byline_admin_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
363
382
|
ALTER TABLE "byline_admin_user_preferences" ADD CONSTRAINT "byline_admin_user_preferences_user_id_byline_admin_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."byline_admin_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
@@ -393,11 +412,14 @@ ALTER TABLE "byline_store_relation" ADD CONSTRAINT "byline_store_relation_target
|
|
|
393
412
|
ALTER TABLE "byline_singleton_documents" ADD CONSTRAINT "fk_singleton_documents_document" FOREIGN KEY ("collection_id","document_id") REFERENCES "public"."byline_documents"("collection_id","id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
394
413
|
ALTER TABLE "byline_store_text" ADD CONSTRAINT "byline_store_text_document_version_id_byline_document_versions_id_fk" FOREIGN KEY ("document_version_id") REFERENCES "public"."byline_document_versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
395
414
|
ALTER TABLE "byline_store_text" ADD CONSTRAINT "byline_store_text_collection_id_byline_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."byline_collections"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
415
|
+
CREATE INDEX "idx_admin_login_sessions_user" ON "byline_admin_login_sessions" USING btree ("admin_user_id");--> statement-breakpoint
|
|
396
416
|
CREATE INDEX "idx_byline_admin_permissions_role" ON "byline_admin_permissions" USING btree ("admin_role_id");--> statement-breakpoint
|
|
417
|
+
CREATE INDEX "idx_admin_refresh_tokens_sid" ON "byline_admin_refresh_tokens" USING btree ("sid");--> statement-breakpoint
|
|
397
418
|
CREATE INDEX "idx_byline_admin_refresh_tokens_user" ON "byline_admin_refresh_tokens" USING btree ("admin_user_id");--> statement-breakpoint
|
|
398
419
|
CREATE INDEX "idx_byline_admin_refresh_tokens_token_hash" ON "byline_admin_refresh_tokens" USING btree ("token_hash");--> statement-breakpoint
|
|
399
420
|
CREATE INDEX "idx_byline_admin_role_admin_user_user" ON "byline_admin_role_admin_user" USING btree ("admin_user_id");--> statement-breakpoint
|
|
400
421
|
CREATE INDEX "idx_byline_admin_roles_machine_name" ON "byline_admin_roles" USING btree ("machine_name");--> statement-breakpoint
|
|
422
|
+
CREATE INDEX "idx_admin_sign_in_rate_limits_expiry" ON "byline_admin_sign_in_rate_limits" USING btree ("expires_at");--> statement-breakpoint
|
|
401
423
|
CREATE INDEX "idx_byline_admin_users_email" ON "byline_admin_users" USING btree ("email");--> statement-breakpoint
|
|
402
424
|
CREATE INDEX "idx_audit_log_document_id" ON "byline_audit_log" USING btree ("document_id","id");--> statement-breakpoint
|
|
403
425
|
CREATE INDEX "idx_audit_log_actor_id" ON "byline_audit_log" USING btree ("actor_id","id");--> statement-breakpoint
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@byline/cli",
|
|
3
3
|
"private": false,
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
|
-
"version": "5.
|
|
5
|
+
"version": "5.1.1",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.9.0"
|
|
8
8
|
},
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
"dist"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@clack/prompts": "^1.
|
|
36
|
+
"@clack/prompts": "^1.8.0",
|
|
37
37
|
"commander": "^15.0.0",
|
|
38
38
|
"diff": "^9.0.0",
|
|
39
39
|
"drizzle-orm": "^0.45.2",
|
|
40
40
|
"execa": "^10.0.1",
|
|
41
41
|
"jsonc-parser": "^3.3.1",
|
|
42
42
|
"minimatch": "^10.2.6",
|
|
43
|
-
"mysql2": "^3.24.
|
|
43
|
+
"mysql2": "^3.24.4",
|
|
44
44
|
"pg": "^8.23.0",
|
|
45
45
|
"picocolors": "^1.1.1",
|
|
46
46
|
"semver": "^7.8.5",
|
|
@@ -48,20 +48,20 @@
|
|
|
48
48
|
"yaml": "^2.9.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@biomejs/biome": "2.5.
|
|
51
|
+
"@biomejs/biome": "2.5.12",
|
|
52
52
|
"@types/mdast": "^4.0.4",
|
|
53
|
-
"@types/node": "^26.
|
|
53
|
+
"@types/node": "^26.5.0",
|
|
54
54
|
"@types/pg": "^8.23.1",
|
|
55
55
|
"@types/semver": "^7.8.0",
|
|
56
56
|
"dotenv": "^17.4.2",
|
|
57
57
|
"mdast-util-to-string": "^4.0.0",
|
|
58
58
|
"remark-gfm": "^4.0.1",
|
|
59
59
|
"remark-parse": "^11.0.0",
|
|
60
|
-
"tsc-alias": "^1.9.
|
|
60
|
+
"tsc-alias": "^1.9.4",
|
|
61
61
|
"typescript": "^7.0.2",
|
|
62
62
|
"unified": "^11.0.5",
|
|
63
|
-
"vitest": "^
|
|
64
|
-
"@byline/core": "5.
|
|
63
|
+
"vitest": "^5.0.0",
|
|
64
|
+
"@byline/core": "5.1.1"
|
|
65
65
|
},
|
|
66
66
|
"publishConfig": {
|
|
67
67
|
"access": "public",
|