@zintrust/core 0.7.0 → 0.7.2
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/package.json +7 -5
- package/src/auth/LoginFlow.d.ts +65 -0
- package/src/auth/LoginFlow.d.ts.map +1 -0
- package/src/auth/LoginFlow.js +352 -0
- package/src/cache/drivers/KVRemoteDriver.d.ts.map +1 -1
- package/src/cache/drivers/KVRemoteDriver.js +9 -3
- package/src/cli/commands/ScheduleListCommand.d.ts.map +1 -1
- package/src/cli/commands/ScheduleListCommand.js +3 -0
- package/src/cli/commands/ScheduleRunCommand.d.ts.map +1 -1
- package/src/cli/commands/ScheduleRunCommand.js +3 -0
- package/src/cli/commands/ScheduleStartCommand.d.ts.map +1 -1
- package/src/cli/commands/ScheduleStartCommand.js +3 -0
- package/src/cli/commands/schedule/ScheduleCliSupport.d.ts +1 -0
- package/src/cli/commands/schedule/ScheduleCliSupport.d.ts.map +1 -1
- package/src/cli/commands/schedule/ScheduleCliSupport.js +116 -6
- package/src/cli/scaffolding/GovernanceScaffolder.d.ts.map +1 -1
- package/src/cli/scaffolding/GovernanceScaffolder.js +22 -3
- package/src/cli/scaffolding/MigrationGenerator.d.ts.map +1 -1
- package/src/cli/scaffolding/MigrationGenerator.js +2 -1
- package/src/cli/scaffolding/ProjectScaffolder.d.ts.map +1 -1
- package/src/cli/scaffolding/ProjectScaffolder.js +79 -8
- package/src/cli/scaffolding/ScaffoldingVersionUtils.js +1 -1
- package/src/common/ContextLoader.d.ts +27 -0
- package/src/common/ContextLoader.d.ts.map +1 -0
- package/src/common/ContextLoader.js +187 -0
- package/src/config/logger.d.ts +2 -0
- package/src/config/logger.d.ts.map +1 -1
- package/src/config/logger.js +12 -0
- package/src/index.d.ts +7 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +7 -3
- package/src/orm/Model.d.ts.map +1 -1
- package/src/orm/Model.js +1 -1
- package/src/orm/adapters/D1Adapter.d.ts.map +1 -1
- package/src/orm/adapters/D1Adapter.js +1 -1
- package/src/orm/adapters/MySQLAdapter.d.ts.map +1 -1
- package/src/orm/adapters/MySQLAdapter.js +1 -1
- package/src/orm/adapters/MySQLProxyAdapter.d.ts.map +1 -1
- package/src/orm/adapters/MySQLProxyAdapter.js +11 -9
- package/src/orm/adapters/PostgreSQLAdapter.js +1 -1
- package/src/orm/adapters/SQLServerAdapter.d.ts.map +1 -1
- package/src/orm/adapters/SQLServerAdapter.js +1 -1
- package/src/orm/adapters/SQLiteAdapter.js +1 -1
- package/src/proxy/ProxyServerUtils.d.ts.map +1 -1
- package/src/proxy/ProxyServerUtils.js +15 -11
- package/src/security/SecurePayload.d.ts +38 -0
- package/src/security/SecurePayload.d.ts.map +1 -0
- package/src/security/SecurePayload.js +214 -0
- package/src/templates/project/basic/app/Controllers/AuthController.ts.tpl +132 -46
- package/src/templates/project/basic/package.json.tpl +5 -0
- package/src/tools/notification/Composer.d.ts +40 -0
- package/src/tools/notification/Composer.d.ts.map +1 -0
- package/src/tools/notification/Composer.js +140 -0
- package/src/tools/notification/Notification.d.ts +6 -0
- package/src/tools/notification/Notification.d.ts.map +1 -1
- package/src/tools/notification/Notification.js +7 -0
- package/src/tools/queue/AdvancedQueue.js +15 -0
- package/src/tools/queue/Queue.d.ts +1 -0
- package/src/tools/queue/Queue.d.ts.map +1 -1
- package/src/types/Queue.d.ts +1 -0
- package/src/types/Queue.d.ts.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/core",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Production-grade TypeScript backend framework for JavaScript",
|
|
5
5
|
"homepage": "https://zintrust.com",
|
|
6
6
|
"repository": {
|
|
@@ -57,19 +57,21 @@
|
|
|
57
57
|
"./package.json": "./package.json"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@cloudflare/containers": "^0.3.
|
|
60
|
+
"@cloudflare/containers": "^0.3.2",
|
|
61
61
|
"bcryptjs": "^3.0.3",
|
|
62
|
-
"bullmq": "^5.
|
|
62
|
+
"bullmq": "^5.74.1",
|
|
63
63
|
"chalk": "^5.6.2",
|
|
64
64
|
"commander": "^14.0.3",
|
|
65
65
|
"inquirer": "^13.4.1",
|
|
66
66
|
"jsonwebtoken": "^9.0.3",
|
|
67
|
-
"mysql2": "^3.22.
|
|
68
|
-
"pg": "^8.20.0"
|
|
67
|
+
"mysql2": "^3.22.1",
|
|
68
|
+
"pg": "^8.20.0",
|
|
69
|
+
"tsx": "^4.21.0"
|
|
69
70
|
},
|
|
70
71
|
"overrides": {
|
|
71
72
|
"ajv": "^8.18.0",
|
|
72
73
|
"axios": "^1.15.0",
|
|
74
|
+
"@eslint/plugin-kit": "^0.7.1",
|
|
73
75
|
"follow-redirects": "^1.16.0",
|
|
74
76
|
"@tootallnate/once": "^3.0.1",
|
|
75
77
|
"node-forge": "^1.4.0",
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type JwtPayload } from '../security/JwtManager';
|
|
2
|
+
export type LoginFlowStage = 'identify' | 'verify' | 'issue' | 'audit';
|
|
3
|
+
export type LoginFlowError = Error & {
|
|
4
|
+
stage: LoginFlowStage;
|
|
5
|
+
details?: unknown;
|
|
6
|
+
};
|
|
7
|
+
export type LoginFlowIdentity = Record<string, unknown> | null;
|
|
8
|
+
export type LoginFlowVerifiedRecord = {
|
|
9
|
+
user?: unknown;
|
|
10
|
+
subject?: string;
|
|
11
|
+
claims?: JwtPayload;
|
|
12
|
+
metadata?: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
export type LoginFlowResult = {
|
|
15
|
+
identity: LoginFlowIdentity;
|
|
16
|
+
verified: LoginFlowVerifiedRecord;
|
|
17
|
+
issued?: unknown;
|
|
18
|
+
};
|
|
19
|
+
export type LoginFlowProvider<TContext = unknown> = {
|
|
20
|
+
identify: (input: unknown, context: TContext) => Promise<LoginFlowIdentity>;
|
|
21
|
+
verify: (identity: LoginFlowIdentity, input: unknown, context: TContext) => Promise<LoginFlowVerifiedRecord>;
|
|
22
|
+
};
|
|
23
|
+
export type LoginFlowIssuerInput<TContext = unknown> = {
|
|
24
|
+
verified: LoginFlowVerifiedRecord;
|
|
25
|
+
context: TContext;
|
|
26
|
+
};
|
|
27
|
+
export type LoginFlowIssuer<TContext = unknown> = (input: LoginFlowIssuerInput<TContext>) => Promise<unknown>;
|
|
28
|
+
export type LoginFlowAuditEvent<TContext = unknown> = {
|
|
29
|
+
status: 'success' | 'failed';
|
|
30
|
+
stage?: LoginFlowStage;
|
|
31
|
+
provider: string;
|
|
32
|
+
issuer?: string;
|
|
33
|
+
identity?: LoginFlowIdentity;
|
|
34
|
+
verified?: LoginFlowVerifiedRecord;
|
|
35
|
+
issued?: unknown;
|
|
36
|
+
error?: unknown;
|
|
37
|
+
context: TContext;
|
|
38
|
+
};
|
|
39
|
+
export type LoginFlowAuditor<TContext = unknown> = (event: LoginFlowAuditEvent<TContext>) => Promise<void>;
|
|
40
|
+
export type LoginFlowCreateOptions<TContext = unknown> = {
|
|
41
|
+
provider: string | LoginFlowProvider<TContext>;
|
|
42
|
+
context: TContext;
|
|
43
|
+
};
|
|
44
|
+
export type LoginFlowBuilder<TContext = unknown> = {
|
|
45
|
+
identify: (input: unknown) => LoginFlowBuilder<TContext>;
|
|
46
|
+
verify: (input: unknown) => LoginFlowBuilder<TContext>;
|
|
47
|
+
issue: (issuer: string | LoginFlowIssuer<TContext>) => LoginFlowBuilder<TContext>;
|
|
48
|
+
audit: (auditor?: string | LoginFlowAuditor<TContext>) => LoginFlowBuilder<TContext>;
|
|
49
|
+
run: () => Promise<LoginFlowResult>;
|
|
50
|
+
};
|
|
51
|
+
export type LoginFlowNamespace = {
|
|
52
|
+
create: <TContext = unknown>(options: LoginFlowCreateOptions<TContext>) => LoginFlowBuilder<TContext>;
|
|
53
|
+
registerProvider: <TContext = unknown>(name: string, provider: LoginFlowProvider<TContext>) => void;
|
|
54
|
+
unregisterProvider: (name: string) => void;
|
|
55
|
+
hasProvider: (name: string) => boolean;
|
|
56
|
+
registerIssuer: <TContext = unknown>(name: string, issuer: LoginFlowIssuer<TContext>) => void;
|
|
57
|
+
unregisterIssuer: (name: string) => void;
|
|
58
|
+
hasIssuer: (name: string) => boolean;
|
|
59
|
+
registerAuditor: <TContext = unknown>(name: string, auditor: LoginFlowAuditor<TContext>) => void;
|
|
60
|
+
unregisterAuditor: (name: string) => void;
|
|
61
|
+
hasAuditor: (name: string) => boolean;
|
|
62
|
+
clearRegistrations: () => void;
|
|
63
|
+
};
|
|
64
|
+
export declare const LoginFlow: LoginFlowNamespace;
|
|
65
|
+
//# sourceMappingURL=LoginFlow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoginFlow.d.ts","sourceRoot":"","sources":["../../../src/auth/LoginFlow.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEnE,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;AAEvE,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG;IACnC,KAAK,EAAE,cAAc,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;AAE/D,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,QAAQ,EAAE,uBAAuB,CAAC;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,QAAQ,GAAG,OAAO,IAAI;IAClD,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC5E,MAAM,EAAE,CACN,QAAQ,EAAE,iBAAiB,EAC3B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,QAAQ,KACd,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,oBAAoB,CAAC,QAAQ,GAAG,OAAO,IAAI;IACrD,QAAQ,EAAE,uBAAuB,CAAC;IAClC,OAAO,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,QAAQ,GAAG,OAAO,IAAI,CAChD,KAAK,EAAE,oBAAoB,CAAC,QAAQ,CAAC,KAClC,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,MAAM,MAAM,mBAAmB,CAAC,QAAQ,GAAG,OAAO,IAAI;IACpD,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC7B,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,CAAC,EAAE,uBAAuB,CAAC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,QAAQ,GAAG,OAAO,IAAI,CACjD,KAAK,EAAE,mBAAmB,CAAC,QAAQ,CAAC,KACjC,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,MAAM,sBAAsB,CAAC,QAAQ,GAAG,OAAO,IAAI;IACvD,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,OAAO,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,QAAQ,GAAG,OAAO,IAAI;IACjD,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACzD,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACvD,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,KAAK,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAClF,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,KAAK,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACrF,GAAG,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,CAAC,QAAQ,GAAG,OAAO,EACzB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,KACtC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAChC,gBAAgB,EAAE,CAAC,QAAQ,GAAG,OAAO,EACnC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,iBAAiB,CAAC,QAAQ,CAAC,KAClC,IAAI,CAAC;IACV,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACvC,cAAc,EAAE,CAAC,QAAQ,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAC9F,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACrC,eAAe,EAAE,CAAC,QAAQ,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACjG,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,kBAAkB,EAAE,MAAM,IAAI,CAAC;CAChC,CAAC;AA8cF,eAAO,MAAM,SAAS,EAAE,kBAYtB,CAAC"}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { SystemTraceBridge } from '../trace/SystemTraceBridge.js';
|
|
2
|
+
import { ErrorFactory } from '../exceptions/ZintrustError.js';
|
|
3
|
+
import { isFunction, isNonEmptyString, isObject } from '../helper/index.js';
|
|
4
|
+
import { JwtManager } from '../security/JwtManager.js';
|
|
5
|
+
const providerRegistry = new Map();
|
|
6
|
+
const issuerRegistry = new Map();
|
|
7
|
+
const auditorRegistry = new Map();
|
|
8
|
+
const createLoginFlowError = (stage, message, details) => {
|
|
9
|
+
return Object.assign(ErrorFactory.createValidationError(message, details), {
|
|
10
|
+
stage,
|
|
11
|
+
details,
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
const isLoginFlowError = (value) => {
|
|
15
|
+
if (!isObject(value)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return isNonEmptyString(value['stage']);
|
|
19
|
+
};
|
|
20
|
+
const getNamedProvider = (name) => {
|
|
21
|
+
const provider = providerRegistry.get(name);
|
|
22
|
+
if (!provider) {
|
|
23
|
+
throw createLoginFlowError('identify', `LoginFlow provider "${name}" is not registered`, {
|
|
24
|
+
provider: name,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return provider;
|
|
28
|
+
};
|
|
29
|
+
const getNamedIssuer = (name) => {
|
|
30
|
+
const issuer = issuerRegistry.get(name);
|
|
31
|
+
if (!issuer) {
|
|
32
|
+
throw createLoginFlowError('issue', `LoginFlow issuer "${name}" is not registered`, {
|
|
33
|
+
issuer: name,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return issuer;
|
|
37
|
+
};
|
|
38
|
+
const getNamedAuditor = (name) => {
|
|
39
|
+
const auditor = auditorRegistry.get(name);
|
|
40
|
+
if (!auditor) {
|
|
41
|
+
throw createLoginFlowError('audit', `LoginFlow auditor "${name}" is not registered`, {
|
|
42
|
+
auditor: name,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return auditor;
|
|
46
|
+
};
|
|
47
|
+
const resolveProvider = (provider) => {
|
|
48
|
+
if (typeof provider === 'string') {
|
|
49
|
+
return getNamedProvider(provider);
|
|
50
|
+
}
|
|
51
|
+
return provider;
|
|
52
|
+
};
|
|
53
|
+
const resolveProviderName = (provider) => {
|
|
54
|
+
if (typeof provider === 'string') {
|
|
55
|
+
return provider;
|
|
56
|
+
}
|
|
57
|
+
return 'inline';
|
|
58
|
+
};
|
|
59
|
+
const resolveIssuer = (issuer) => {
|
|
60
|
+
if (typeof issuer === 'string') {
|
|
61
|
+
return getNamedIssuer(issuer);
|
|
62
|
+
}
|
|
63
|
+
return issuer;
|
|
64
|
+
};
|
|
65
|
+
const resolveIssuerName = (issuer) => {
|
|
66
|
+
if (typeof issuer === 'string') {
|
|
67
|
+
return issuer;
|
|
68
|
+
}
|
|
69
|
+
return 'inline';
|
|
70
|
+
};
|
|
71
|
+
const resolveAuditor = (auditor) => {
|
|
72
|
+
if (typeof auditor === 'string') {
|
|
73
|
+
return getNamedAuditor(auditor);
|
|
74
|
+
}
|
|
75
|
+
return auditor;
|
|
76
|
+
};
|
|
77
|
+
const normalizeVerifiedRecord = (value) => {
|
|
78
|
+
if (!isObject(value)) {
|
|
79
|
+
throw createLoginFlowError('verify', 'LoginFlow verify() must return an object', { value });
|
|
80
|
+
}
|
|
81
|
+
const record = value;
|
|
82
|
+
if (record.subject !== undefined && !isNonEmptyString(record.subject)) {
|
|
83
|
+
throw createLoginFlowError('verify', 'LoginFlow verify() returned an invalid subject', {
|
|
84
|
+
subject: record.subject,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (record.claims !== undefined && !isObject(record.claims)) {
|
|
88
|
+
throw createLoginFlowError('verify', 'LoginFlow verify() returned invalid claims', {
|
|
89
|
+
claims: record.claims,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return record;
|
|
93
|
+
};
|
|
94
|
+
const createJwtIssuer = async ({ verified, }) => {
|
|
95
|
+
const claims = isObject(verified.claims) ? { ...verified.claims } : {};
|
|
96
|
+
if (isNonEmptyString(verified.subject) && !isNonEmptyString(claims.sub)) {
|
|
97
|
+
claims.sub = verified.subject;
|
|
98
|
+
}
|
|
99
|
+
return JwtManager.signAccessToken(claims);
|
|
100
|
+
};
|
|
101
|
+
const createTraceAuditor = async (event) => {
|
|
102
|
+
const subject = typeof event.verified?.subject === 'string' && event.verified.subject.trim() !== ''
|
|
103
|
+
? event.verified.subject
|
|
104
|
+
: undefined;
|
|
105
|
+
SystemTraceBridge.emitAuth(event.status === 'success' ? 'login' : 'failed', subject);
|
|
106
|
+
return Promise.resolve();
|
|
107
|
+
};
|
|
108
|
+
const ensureNamedRegistration = (kind, name) => {
|
|
109
|
+
if (!isNonEmptyString(name)) {
|
|
110
|
+
throw createLoginFlowError('identify', `LoginFlow ${kind} name must be a non-empty string`, {
|
|
111
|
+
name,
|
|
112
|
+
kind,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const ensureProvider = (provider) => {
|
|
117
|
+
if (!isObject(provider) || !isFunction(provider['identify']) || !isFunction(provider['verify'])) {
|
|
118
|
+
throw createLoginFlowError('identify', 'LoginFlow provider must expose identify() and verify()', {
|
|
119
|
+
provider,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const ensureHandler = (stage, kind, handler) => {
|
|
124
|
+
if (!isFunction(handler)) {
|
|
125
|
+
throw createLoginFlowError(stage, `LoginFlow ${kind} must be a function`, {
|
|
126
|
+
handler,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const auditFailureIfNeeded = async (auditorTarget, event) => {
|
|
131
|
+
if (auditorTarget === undefined) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const auditor = resolveAuditor(auditorTarget);
|
|
135
|
+
await auditor(event);
|
|
136
|
+
};
|
|
137
|
+
const ensureRequiredInputs = (state) => {
|
|
138
|
+
if (!state.identifySet) {
|
|
139
|
+
throw createLoginFlowError('identify', 'LoginFlow identify() must be called before run()', {
|
|
140
|
+
provider: state.providerName,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (!state.verifySet) {
|
|
144
|
+
throw createLoginFlowError('verify', 'LoginFlow verify() must be called before run()', {
|
|
145
|
+
provider: state.providerName,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const resolveProviderWithAudit = async (state) => {
|
|
150
|
+
try {
|
|
151
|
+
return resolveProvider(state.options.provider);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
const wrapped = isLoginFlowError(error)
|
|
155
|
+
? error
|
|
156
|
+
: createLoginFlowError('identify', 'LoginFlow identify() failed', {
|
|
157
|
+
provider: state.providerName,
|
|
158
|
+
error,
|
|
159
|
+
});
|
|
160
|
+
await auditFailureIfNeeded(state.auditorTarget, {
|
|
161
|
+
status: 'failed',
|
|
162
|
+
stage: 'identify',
|
|
163
|
+
provider: state.providerName,
|
|
164
|
+
context: state.options.context,
|
|
165
|
+
error: wrapped,
|
|
166
|
+
});
|
|
167
|
+
throw wrapped;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const identifyWithAudit = async (state, provider) => {
|
|
171
|
+
try {
|
|
172
|
+
return await provider.identify(state.identifyInput, state.options.context);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
const wrapped = createLoginFlowError('identify', 'LoginFlow identify() failed', {
|
|
176
|
+
provider: state.providerName,
|
|
177
|
+
error,
|
|
178
|
+
});
|
|
179
|
+
await auditFailureIfNeeded(state.auditorTarget, {
|
|
180
|
+
status: 'failed',
|
|
181
|
+
stage: 'identify',
|
|
182
|
+
provider: state.providerName,
|
|
183
|
+
context: state.options.context,
|
|
184
|
+
error: wrapped,
|
|
185
|
+
});
|
|
186
|
+
throw wrapped;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const verifyWithAudit = async (state, provider, identity) => {
|
|
190
|
+
try {
|
|
191
|
+
return normalizeVerifiedRecord(await provider.verify(identity, state.verifyInput, state.options.context));
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
const wrapped = isLoginFlowError(error)
|
|
195
|
+
? error
|
|
196
|
+
: createLoginFlowError('verify', 'LoginFlow verify() failed', {
|
|
197
|
+
provider: state.providerName,
|
|
198
|
+
error,
|
|
199
|
+
});
|
|
200
|
+
await auditFailureIfNeeded(state.auditorTarget, {
|
|
201
|
+
status: 'failed',
|
|
202
|
+
stage: 'verify',
|
|
203
|
+
provider: state.providerName,
|
|
204
|
+
identity,
|
|
205
|
+
context: state.options.context,
|
|
206
|
+
error: wrapped,
|
|
207
|
+
});
|
|
208
|
+
throw wrapped;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const issueWithAudit = async (state, identity, verified) => {
|
|
212
|
+
if (state.issueTarget === undefined) {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
const issuer = resolveIssuer(state.issueTarget);
|
|
216
|
+
try {
|
|
217
|
+
return await issuer({ verified, context: state.options.context });
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
const wrapped = createLoginFlowError('issue', 'LoginFlow issue() failed', {
|
|
221
|
+
provider: state.providerName,
|
|
222
|
+
issuer: state.issueName,
|
|
223
|
+
error,
|
|
224
|
+
});
|
|
225
|
+
await auditFailureIfNeeded(state.auditorTarget, {
|
|
226
|
+
status: 'failed',
|
|
227
|
+
stage: 'issue',
|
|
228
|
+
provider: state.providerName,
|
|
229
|
+
issuer: state.issueName,
|
|
230
|
+
identity,
|
|
231
|
+
verified,
|
|
232
|
+
context: state.options.context,
|
|
233
|
+
error: wrapped,
|
|
234
|
+
});
|
|
235
|
+
throw wrapped;
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
const auditSuccess = async (state, identity, verified, issued) => {
|
|
239
|
+
if (state.auditorTarget === undefined) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const auditor = resolveAuditor(state.auditorTarget);
|
|
243
|
+
await auditor({
|
|
244
|
+
status: 'success',
|
|
245
|
+
provider: state.providerName,
|
|
246
|
+
issuer: state.issueName,
|
|
247
|
+
identity,
|
|
248
|
+
verified,
|
|
249
|
+
issued,
|
|
250
|
+
context: state.options.context,
|
|
251
|
+
});
|
|
252
|
+
};
|
|
253
|
+
const runLoginFlow = async (state) => {
|
|
254
|
+
const provider = await resolveProviderWithAudit(state);
|
|
255
|
+
ensureRequiredInputs(state);
|
|
256
|
+
const identity = await identifyWithAudit(state, provider);
|
|
257
|
+
const verified = await verifyWithAudit(state, provider, identity);
|
|
258
|
+
const issued = await issueWithAudit(state, identity, verified);
|
|
259
|
+
await auditSuccess(state, identity, verified, issued);
|
|
260
|
+
return {
|
|
261
|
+
identity,
|
|
262
|
+
verified,
|
|
263
|
+
...(issued === undefined ? {} : { issued }),
|
|
264
|
+
};
|
|
265
|
+
};
|
|
266
|
+
const createBuilder = (state) => {
|
|
267
|
+
return Object.freeze({
|
|
268
|
+
identify(input) {
|
|
269
|
+
state.identifyInput = input;
|
|
270
|
+
state.identifySet = true;
|
|
271
|
+
return this;
|
|
272
|
+
},
|
|
273
|
+
verify(input) {
|
|
274
|
+
state.verifyInput = input;
|
|
275
|
+
state.verifySet = true;
|
|
276
|
+
return this;
|
|
277
|
+
},
|
|
278
|
+
issue(issuer) {
|
|
279
|
+
state.issueTarget = issuer;
|
|
280
|
+
state.issueName = resolveIssuerName(issuer);
|
|
281
|
+
return this;
|
|
282
|
+
},
|
|
283
|
+
audit(auditor) {
|
|
284
|
+
state.auditorTarget = auditor ?? 'trace';
|
|
285
|
+
return this;
|
|
286
|
+
},
|
|
287
|
+
async run() {
|
|
288
|
+
return runLoginFlow(state);
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
const create = (options) => {
|
|
293
|
+
return createBuilder({
|
|
294
|
+
options,
|
|
295
|
+
providerName: resolveProviderName(options.provider),
|
|
296
|
+
identifyInput: undefined,
|
|
297
|
+
verifyInput: undefined,
|
|
298
|
+
issueTarget: undefined,
|
|
299
|
+
issueName: undefined,
|
|
300
|
+
auditorTarget: undefined,
|
|
301
|
+
identifySet: false,
|
|
302
|
+
verifySet: false,
|
|
303
|
+
});
|
|
304
|
+
};
|
|
305
|
+
const registerProvider = (name, provider) => {
|
|
306
|
+
ensureNamedRegistration('provider', name);
|
|
307
|
+
ensureProvider(provider);
|
|
308
|
+
providerRegistry.set(name, provider);
|
|
309
|
+
};
|
|
310
|
+
const unregisterProvider = (name) => {
|
|
311
|
+
providerRegistry.delete(name);
|
|
312
|
+
};
|
|
313
|
+
const hasProvider = (name) => providerRegistry.has(name);
|
|
314
|
+
const registerIssuer = (name, issuer) => {
|
|
315
|
+
ensureNamedRegistration('issuer', name);
|
|
316
|
+
ensureHandler('issue', 'issuer', issuer);
|
|
317
|
+
issuerRegistry.set(name, issuer);
|
|
318
|
+
};
|
|
319
|
+
const unregisterIssuer = (name) => {
|
|
320
|
+
issuerRegistry.delete(name);
|
|
321
|
+
};
|
|
322
|
+
const hasIssuer = (name) => issuerRegistry.has(name);
|
|
323
|
+
const registerAuditor = (name, auditor) => {
|
|
324
|
+
ensureNamedRegistration('auditor', name);
|
|
325
|
+
ensureHandler('audit', 'auditor', auditor);
|
|
326
|
+
auditorRegistry.set(name, auditor);
|
|
327
|
+
};
|
|
328
|
+
const unregisterAuditor = (name) => {
|
|
329
|
+
auditorRegistry.delete(name);
|
|
330
|
+
};
|
|
331
|
+
const hasAuditor = (name) => auditorRegistry.has(name);
|
|
332
|
+
const clearRegistrations = () => {
|
|
333
|
+
providerRegistry.clear();
|
|
334
|
+
issuerRegistry.clear();
|
|
335
|
+
auditorRegistry.clear();
|
|
336
|
+
issuerRegistry.set('jwt', createJwtIssuer);
|
|
337
|
+
auditorRegistry.set('trace', createTraceAuditor);
|
|
338
|
+
};
|
|
339
|
+
clearRegistrations();
|
|
340
|
+
export const LoginFlow = Object.freeze({
|
|
341
|
+
create,
|
|
342
|
+
registerProvider,
|
|
343
|
+
unregisterProvider,
|
|
344
|
+
hasProvider,
|
|
345
|
+
registerIssuer,
|
|
346
|
+
unregisterIssuer,
|
|
347
|
+
hasIssuer,
|
|
348
|
+
registerAuditor,
|
|
349
|
+
unregisterAuditor,
|
|
350
|
+
hasAuditor,
|
|
351
|
+
clearRegistrations,
|
|
352
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KVRemoteDriver.d.ts","sourceRoot":"","sources":["../../../../src/cache/drivers/KVRemoteDriver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"KVRemoteDriver.d.ts","sourceRoot":"","sources":["../../../../src/cache/drivers/KVRemoteDriver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AA2YtD,eAAO,MAAM,cAAc;kBA5FM,WAAW;EA8F1C,CAAC;AAEH,eAAe,cAAc,CAAC"}
|
|
@@ -227,7 +227,9 @@ const createKvRemoteDriver = () => {
|
|
|
227
227
|
if (!hasCloudflareApiCreds())
|
|
228
228
|
throw error;
|
|
229
229
|
Logger.warn('KV remote proxy GET failed; falling back to Cloudflare KV API', {
|
|
230
|
-
|
|
230
|
+
...Logger.withTraceSkipContext({
|
|
231
|
+
error: error instanceof Error ? error.message : String(error),
|
|
232
|
+
}),
|
|
231
233
|
});
|
|
232
234
|
return cf.getJson(key);
|
|
233
235
|
}
|
|
@@ -251,7 +253,9 @@ const createKvRemoteDriver = () => {
|
|
|
251
253
|
if (!hasCloudflareApiCreds())
|
|
252
254
|
throw error;
|
|
253
255
|
Logger.warn('KV remote proxy PUT failed; falling back to Cloudflare KV API', {
|
|
254
|
-
|
|
256
|
+
...Logger.withTraceSkipContext({
|
|
257
|
+
error: error instanceof Error ? error.message : String(error),
|
|
258
|
+
}),
|
|
255
259
|
});
|
|
256
260
|
await cf.putJson(key, value, ttl);
|
|
257
261
|
}
|
|
@@ -273,7 +277,9 @@ const createKvRemoteDriver = () => {
|
|
|
273
277
|
if (!hasCloudflareApiCreds())
|
|
274
278
|
throw error;
|
|
275
279
|
Logger.warn('KV remote proxy DELETE failed; falling back to Cloudflare KV API', {
|
|
276
|
-
|
|
280
|
+
...Logger.withTraceSkipContext({
|
|
281
|
+
error: error instanceof Error ? error.message : String(error),
|
|
282
|
+
}),
|
|
277
283
|
});
|
|
278
284
|
await cf.deleteKey(key);
|
|
279
285
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScheduleListCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/ScheduleListCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"ScheduleListCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/ScheduleListCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA8ErE,eAAO,MAAM,mBAAmB;cACpB,YAAY;EAUtB,CAAC;AAEH,eAAe,mBAAmB,CAAC"}
|
|
@@ -3,6 +3,9 @@ import { ScheduleCliSupport } from '../commands/schedule/ScheduleCliSupport.js';
|
|
|
3
3
|
import { Logger } from '../../config/logger.js';
|
|
4
4
|
import { SchedulerRuntime } from '../../scheduler/SchedulerRuntime.js';
|
|
5
5
|
const execute = async (options) => {
|
|
6
|
+
if (await ScheduleCliSupport.ensureProjectSourceContext()) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
6
9
|
try {
|
|
7
10
|
await ScheduleCliSupport.registerAll();
|
|
8
11
|
const toIso = (ms) => typeof ms === 'number' && Number.isFinite(ms) ? new Date(ms).toISOString() : undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScheduleRunCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/ScheduleRunCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"ScheduleRunCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/ScheduleRunCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA8BrE,eAAO,MAAM,kBAAkB;cACnB,YAAY;EAUtB,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
|
|
@@ -7,6 +7,9 @@ const execute = async (options) => {
|
|
|
7
7
|
const name = (options.name ?? '').trim();
|
|
8
8
|
if (name.length === 0)
|
|
9
9
|
throw ErrorFactory.createConfigError('--name is required');
|
|
10
|
+
if (await ScheduleCliSupport.ensureProjectSourceContext()) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
10
13
|
try {
|
|
11
14
|
await ScheduleCliSupport.registerAll();
|
|
12
15
|
Logger.info('Running schedule once', { name });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScheduleStartCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/ScheduleStartCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"ScheduleStartCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/ScheduleStartCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAiDrE,eAAO,MAAM,oBAAoB;cACrB,YAAY;EAOtB,CAAC;AAEH,eAAe,oBAAoB,CAAC"}
|
|
@@ -18,6 +18,9 @@ const execute = async (_options) => {
|
|
|
18
18
|
Logger.info('Schedules are disabled (SCHEDULES_ENABLED=false); exiting');
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
|
+
if (await ScheduleCliSupport.ensureProjectSourceContext()) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
21
24
|
await ScheduleCliSupport.registerAll();
|
|
22
25
|
const registeredCount = SchedulerRuntime.list().length;
|
|
23
26
|
Logger.info('Starting schedules daemon', { registeredCount });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScheduleCliSupport.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/schedule/ScheduleCliSupport.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ScheduleCliSupport.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/schedule/ScheduleCliSupport.ts"],"names":[],"mappings":"AAsMA,eAAO,MAAM,kBAAkB;sCA1Ec,OAAO,CAAC,OAAO,CAAC;mBA4EtC,OAAO,CAAC,IAAI,CAAC;gCA3BG,OAAO,CAAC,IAAI,CAAC;EAqClD,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
|