cabloy 5.1.242 → 5.1.243
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/.cabloy-version +1 -1
- package/.github/workflows/vona-test-sqlite3.yml +1 -1
- package/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/repo-docs/backend/demonstration-mode-guide.md +5 -2
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.build.ts +11 -0
- package/vona/packages-cli/cli-set-api/test/binBuildFreshInputs.test.ts +152 -0
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-core/src/lib/core/application.ts +2 -2
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/src/suite/a-demo/modules/demo-demonstration/src/bean/guard.demonstration.ts +1 -1
- package/vona/src/suite/a-demo/modules/demo-demonstration/test/guardDemonstration.test.ts +4 -3
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.243
|
|
@@ -94,7 +94,7 @@ jobs:
|
|
|
94
94
|
run: npm run init
|
|
95
95
|
- name: run isolated test infrastructure gate
|
|
96
96
|
# Bound concurrent PowerShell/Add-Type cold starts on Windows runners.
|
|
97
|
-
run: node --test ${{ runner.os == 'Windows' && '--test-concurrency=2' || '' }} scripts/testClaimProcesses.test.ts scripts/runIsolatedTest.test.ts scripts/testClaimCleanup.test.ts scripts/testProcessGuardianWindows.test.ts vona/packages-vona/vona-core/src/lib/core/testClaim.test.ts vona/packages-vona/vona-core/src/lib/core/testClaimFilesystem.test.ts vona/packages-vona/vona-core/src/lib/framework/bootstrap.test.ts vona/packages-vona/vona-core/src/lib/framework/useApp.test.ts vona/packages-cli/cli-set-api/test/binBuildEntries.test.ts vona/packages-cli/cli-set-api/test/binTestPatterns.test.ts
|
|
97
|
+
run: node --test ${{ runner.os == 'Windows' && '--test-concurrency=2' || '' }} scripts/testClaimProcesses.test.ts scripts/runIsolatedTest.test.ts scripts/testClaimCleanup.test.ts scripts/testProcessGuardianWindows.test.ts vona/packages-vona/vona-core/src/lib/core/testClaim.test.ts vona/packages-vona/vona-core/src/lib/core/testClaimFilesystem.test.ts vona/packages-vona/vona-core/src/lib/framework/bootstrap.test.ts vona/packages-vona/vona-core/src/lib/framework/useApp.test.ts vona/packages-cli/cli-set-api/test/binBuildEntries.test.ts vona/packages-cli/cli-set-api/test/binBuildFreshInputs.test.ts vona/packages-cli/cli-set-api/test/binTestPatterns.test.ts
|
|
98
98
|
- name: run A-Pay focused gate
|
|
99
99
|
run: npm run test -- test-pay/test/paymentSession.test.ts test-pay/test/payMock.test.ts test-pay/test/paypalProvider.test.ts test-pay/test/paypalLifecycle.test.ts test-pay/test/stripeProvider.test.ts test-pay/test/stripeLifecycle.test.ts test-pay/test/outbox.test.ts test-pay/test/webhook.test.ts
|
|
100
100
|
working-directory: vona
|
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -80,10 +80,13 @@ The Guard depends on Passport-resolved account state, so it makes this decision
|
|
|
80
80
|
|
|
81
81
|
## Exempt authentication routes
|
|
82
82
|
|
|
83
|
-
Demonstration Mode deliberately does not apply to the standard authentication and OAuth routes below. This keeps sign-in,
|
|
83
|
+
Demonstration Mode deliberately does not apply to the standard authentication and OAuth routes below. This keeps sign-in, registration, account association and migration, OAuth callbacks, and token flows available while the write policy is active.
|
|
84
|
+
|
|
85
|
+
In Cabloy Basic, and Cabloy Start, `POST /home/user/passport/logout` is **not exempt**. While Demonstration Mode is enabled, only authenticated, active, non-anonymous whitelisted users may log out; other users receive `403` with `demo-demonstration:1001`.
|
|
86
|
+
|
|
87
|
+
This prevents ordinary visitors sharing a demo account from revoking other visitors' logins. Whitelisted users remain able to log out. The underlying logout semantics are unchanged: logout revokes the shared login state for the current account and authentication record, not just the current browser session. When Demonstration Mode is disabled, this Guard does not restrict logout.
|
|
84
88
|
|
|
85
89
|
```text
|
|
86
|
-
/home/user/passport/logout
|
|
87
90
|
/home/user/passport/register
|
|
88
91
|
/home/user/passport/login
|
|
89
92
|
/home/user/passport/login/:module/:providerName/:clientName?
|
|
@@ -203,6 +203,13 @@ export class CliBinBuild extends BeanCliBase {
|
|
|
203
203
|
|
|
204
204
|
const sourceMap = process.env.BUILD_SOURCEMAP === 'true';
|
|
205
205
|
|
|
206
|
+
// Explicit roots bypass the application's .vona exclusion and prevent stale emit-cache fallback.
|
|
207
|
+
const tsconfig = path.join(projectPath, '.vona/tsconfig.build.json');
|
|
208
|
+
await fse.writeJson(tsconfig, {
|
|
209
|
+
extends: '../tsconfig.json',
|
|
210
|
+
files: ['./bootstrap.ts', './app.ts', './env.ts', './config.ts', './modules-meta.ts'],
|
|
211
|
+
});
|
|
212
|
+
|
|
206
213
|
const replaceValues = generateConfigDefine(env, ['NODE_ENV', 'META_MODE', 'META_FLAVOR']);
|
|
207
214
|
for (const relativeName in modulesMeta.modules) {
|
|
208
215
|
const module = modulesMeta.modules[relativeName];
|
|
@@ -265,11 +272,15 @@ export class CliBinBuild extends BeanCliBase {
|
|
|
265
272
|
json(),
|
|
266
273
|
commonjs(),
|
|
267
274
|
typescript({
|
|
275
|
+
tsconfig,
|
|
268
276
|
module: 'nodenext',
|
|
269
277
|
compilerOptions: {
|
|
270
278
|
noCheck: true,
|
|
279
|
+
noEmit: false,
|
|
271
280
|
declaration: false,
|
|
281
|
+
declarationMap: false,
|
|
272
282
|
composite: false,
|
|
283
|
+
incremental: false,
|
|
273
284
|
sourceMap,
|
|
274
285
|
},
|
|
275
286
|
outputToFilesystem: false,
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { test } from 'node:test';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
const buildUrl = new URL('../src/lib/bean/cli.bin.build.ts', import.meta.url).href;
|
|
10
|
+
|
|
11
|
+
// Keep the actual bundler and transpilers; only isolate CLI initialization.
|
|
12
|
+
const source = `
|
|
13
|
+
import assert from 'node:assert/strict';
|
|
14
|
+
import { readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
15
|
+
import { registerHooks } from 'node:module';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { pathToFileURL } from 'node:url';
|
|
18
|
+
const buildUrl = ${JSON.stringify(buildUrl)};
|
|
19
|
+
const projectPath = process.argv[1];
|
|
20
|
+
registerHooks({
|
|
21
|
+
resolve(specifier, context, nextResolve) {
|
|
22
|
+
if (context.parentURL === buildUrl) {
|
|
23
|
+
const mock = specifier === '@cabloy/cli'
|
|
24
|
+
? 'export class BeanCliBase {}'
|
|
25
|
+
: specifier === './toolsBin/generateVonaMeta.ts'
|
|
26
|
+
? 'export function generateVonaMeta() { throw new Error("Unexpected generation"); }'
|
|
27
|
+
: undefined;
|
|
28
|
+
if (mock) return { url: 'data:text/javascript,' + encodeURIComponent(mock), shortCircuit: true };
|
|
29
|
+
}
|
|
30
|
+
return nextResolve(specifier, context);
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
const { CliBinBuild } = await import(buildUrl);
|
|
34
|
+
const cli = Object.create(CliBinBuild.prototype);
|
|
35
|
+
const cachePath = process.argv[2];
|
|
36
|
+
const poisoned = readFileSync(cachePath, 'utf8');
|
|
37
|
+
for (const [index, flavor] of ['normal', 'ci', 'normal'].entries()) {
|
|
38
|
+
const env = {
|
|
39
|
+
META_MODE: 'prod',
|
|
40
|
+
META_FLAVOR: flavor,
|
|
41
|
+
SERVER_LISTEN_DISABLE: flavor === 'ci' ? 'true' : '',
|
|
42
|
+
generation: index,
|
|
43
|
+
};
|
|
44
|
+
writeFileSync(join(projectPath, '.vona/env.ts'),
|
|
45
|
+
'export default ' + JSON.stringify(env) + ' as const;');
|
|
46
|
+
writeFileSync(join(projectPath, '.vona/config.ts'),
|
|
47
|
+
'export default { generation: ' + index + ' };');
|
|
48
|
+
const outDir = join(projectPath, 'dist/build-' + index);
|
|
49
|
+
await cli._rollup(projectPath, env, { modules: {} }, outDir);
|
|
50
|
+
const app = await import(pathToFileURL(join(outDir, 'app.js')).href);
|
|
51
|
+
assert.deepEqual(app.env, env, 'must compile current generated environment, not cached CI output');
|
|
52
|
+
assert.deepEqual(await app.config(), { generation: index });
|
|
53
|
+
assert.equal(app.view, 'fresh-view', 'must retain inherited JSX and legacy decorator compilation');
|
|
54
|
+
assert.equal(readFileSync(cachePath, 'utf8'), poisoned, 'legacy cache must remain in place');
|
|
55
|
+
if (process.env.BUILD_SOURCEMAP === 'true') {
|
|
56
|
+
const maps = readdirSync(outDir).filter(name => name.endsWith('.js.map'));
|
|
57
|
+
assert.ok(maps.length > 0);
|
|
58
|
+
assert.ok(maps.some(name =>
|
|
59
|
+
JSON.parse(readFileSync(join(outDir, name), 'utf8')).sources
|
|
60
|
+
.some(source => source.endsWith('/.vona/env.ts'))));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
for (const sourceMap of [false, true]) {
|
|
66
|
+
test(`production build uses fresh generated inputs despite CI cache (sourceMap=${sourceMap})`, () => {
|
|
67
|
+
const projectPath = realpathSync(mkdtempSync(join(tmpdir(), 'vona-bin-build-fresh-')));
|
|
68
|
+
try {
|
|
69
|
+
mkdirSync(join(projectPath, '.vona'));
|
|
70
|
+
mkdirSync(join(projectPath, 'src/backend'), { recursive: true });
|
|
71
|
+
symlinkSync(
|
|
72
|
+
fileURLToPath(new URL('../../../node_modules', import.meta.url)),
|
|
73
|
+
join(projectPath, 'node_modules'),
|
|
74
|
+
process.platform === 'win32' ? 'junction' : 'dir',
|
|
75
|
+
);
|
|
76
|
+
writeFileSync(join(projectPath, 'package.json'), JSON.stringify({ type: 'module' }));
|
|
77
|
+
writeFileSync(
|
|
78
|
+
join(projectPath, 'tsconfig.base.json'),
|
|
79
|
+
JSON.stringify({
|
|
80
|
+
compilerOptions: {
|
|
81
|
+
target: 'ES2020',
|
|
82
|
+
module: 'NodeNext',
|
|
83
|
+
moduleResolution: 'NodeNext',
|
|
84
|
+
incremental: true,
|
|
85
|
+
composite: true,
|
|
86
|
+
declaration: true,
|
|
87
|
+
declarationMap: false,
|
|
88
|
+
jsx: 'react',
|
|
89
|
+
jsxFactory: 'h',
|
|
90
|
+
experimentalDecorators: true,
|
|
91
|
+
emitDecoratorMetadata: true,
|
|
92
|
+
skipLibCheck: true,
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
writeFileSync(
|
|
97
|
+
join(projectPath, 'tsconfig.json'),
|
|
98
|
+
JSON.stringify({
|
|
99
|
+
extends: './tsconfig.base.json',
|
|
100
|
+
compilerOptions: { noEmit: true, composite: false, declaration: false },
|
|
101
|
+
exclude: ['.vona', 'dist', 'node_modules'],
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
104
|
+
writeFileSync(
|
|
105
|
+
join(projectPath, 'src/backend/view.tsx'),
|
|
106
|
+
`function h(tag: string) { return tag; }
|
|
107
|
+
function mark(target: any) { target.value = 'fresh-view'; }
|
|
108
|
+
@mark
|
|
109
|
+
class View { static value: string; }
|
|
110
|
+
export const view = <fixture /> === 'fixture' ? View.value : 'invalid';`,
|
|
111
|
+
);
|
|
112
|
+
writeFileSync(
|
|
113
|
+
join(projectPath, '.vona/app.ts'),
|
|
114
|
+
`export { default as env } from './env.ts';
|
|
115
|
+
export { view } from '../src/backend/view.tsx';
|
|
116
|
+
export async function config() { return (await import('./config.ts')).default; }`,
|
|
117
|
+
);
|
|
118
|
+
writeFileSync(join(projectPath, '.vona/bootstrap.ts'), "export { env } from './app.ts';");
|
|
119
|
+
writeFileSync(join(projectPath, '.vona/modules-meta.ts'), 'export default {};');
|
|
120
|
+
const cachePath = join(
|
|
121
|
+
projectPath,
|
|
122
|
+
'.rollup.cache',
|
|
123
|
+
join(projectPath, '.vona/env.js').replace(/^([a-z]+):/i, '$1'),
|
|
124
|
+
);
|
|
125
|
+
mkdirSync(dirname(cachePath), { recursive: true });
|
|
126
|
+
writeFileSync(
|
|
127
|
+
cachePath,
|
|
128
|
+
'export default { META_MODE: "prod", META_FLAVOR: "ci", SERVER_LISTEN_DISABLE: "true", generation: "poisoned" };',
|
|
129
|
+
);
|
|
130
|
+
const result = spawnSync(
|
|
131
|
+
process.execPath,
|
|
132
|
+
['--input-type=module', '--eval', source, projectPath, cachePath],
|
|
133
|
+
{
|
|
134
|
+
cwd: projectPath,
|
|
135
|
+
encoding: 'utf8',
|
|
136
|
+
timeout: 60_000,
|
|
137
|
+
env: {
|
|
138
|
+
...process.env,
|
|
139
|
+
BUILD_MINIFY: 'false',
|
|
140
|
+
BUILD_SOURCEMAP: String(sourceMap),
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
assert.ifError(result.error);
|
|
145
|
+
assert.equal(result.signal, null, result.stderr);
|
|
146
|
+
assert.equal(result.status, 0, result.stderr);
|
|
147
|
+
assert.doesNotMatch(result.stderr, /\[plugin typescript\]/i, result.stderr);
|
|
148
|
+
} finally {
|
|
149
|
+
rmSync(projectPath, { recursive: true, force: true });
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.122",
|
|
4
4
|
"gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
|
|
5
5
|
"description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
|
|
6
6
|
"keywords": [
|
|
@@ -30,7 +30,7 @@ export interface IGuardOptionsDemonstration extends IDecoratorGuardOptionsGlobal
|
|
|
30
30
|
methodsForWrite,
|
|
31
31
|
usernameWhitelist: parseUsernameWhitelist(app.meta.env.DEMONSTRATION_USERNAME_WHITELIST),
|
|
32
32
|
ignore: [
|
|
33
|
-
'/home/user/passport/logout',
|
|
33
|
+
// '/home/user/passport/logout',
|
|
34
34
|
'/home/user/passport/register',
|
|
35
35
|
'/home/user/passport/login',
|
|
36
36
|
'/home/user/passport/login/:module/:providerName/:clientName?',
|
|
@@ -29,7 +29,7 @@ function isGuardEnabledForRoute(routePathRaw: string, enable = true): boolean {
|
|
|
29
29
|
return app.bean.onion.checkOnionOptionsEnabled({ ...options, enable }, routePathRaw);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
describe('guardDemonstration.test.ts', () => {
|
|
32
|
+
describe('guardDemonstration.test.ts', { concurrency: false }, () => {
|
|
33
33
|
it('parses a trimmed, deduplicated username whitelist and exposes localized errors', async () => {
|
|
34
34
|
assert.deepEqual(parseUsernameWhitelist(' admin, demo, ,admin '), ['admin', 'demo']);
|
|
35
35
|
assert.deepEqual(parseUsernameWhitelist(undefined), []);
|
|
@@ -101,11 +101,10 @@ describe('guardDemonstration.test.ts', () => {
|
|
|
101
101
|
});
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
-
it('
|
|
104
|
+
it('exempts the configured authentication and OAuth routes but protects logout', () => {
|
|
105
105
|
for (const routePathRaw of [
|
|
106
106
|
'/home/user/passport/login',
|
|
107
107
|
'/home/user/passport/login/:module/:providerName/:clientName?',
|
|
108
|
-
'/home/user/passport/logout',
|
|
109
108
|
'/home/user/passport/register',
|
|
110
109
|
'/home/user/passport/associate/:module/:providerName/:clientName?',
|
|
111
110
|
'/home/user/passport/migrate/:module/:providerName/:clientName?',
|
|
@@ -116,6 +115,8 @@ describe('guardDemonstration.test.ts', () => {
|
|
|
116
115
|
]) {
|
|
117
116
|
assert.equal(isGuardEnabledForRoute(routePathRaw), false);
|
|
118
117
|
}
|
|
118
|
+
assert.equal(isGuardEnabledForRoute('/home/user/passport/logout'), true);
|
|
119
|
+
assert.equal(isGuardEnabledForRoute('/home/user/passport/logout', false), false);
|
|
119
120
|
assert.equal(isGuardEnabledForRoute('/test/business/write'), true);
|
|
120
121
|
});
|
|
121
122
|
});
|