@zintrust/core 0.7.0 → 0.7.3
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 +24 -21
- 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 +50 -13
- 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.3",
|
|
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":"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"}
|
|
@@ -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;AA6YtD,eAAO,MAAM,cAAc;kBAvEM,WAAW;EAyE1C,CAAC;AAEH,eAAe,cAAc,CAAC"}
|
|
@@ -206,6 +206,25 @@ const createCloudflareKvApiClient = (resolveNamespaceId) => {
|
|
|
206
206
|
};
|
|
207
207
|
return { getJson, putJson, deleteKey };
|
|
208
208
|
};
|
|
209
|
+
const logKvRemoteProxyFallback = (operation, error) => {
|
|
210
|
+
Logger.warn(`KV remote proxy ${operation} failed; falling back to Cloudflare KV API`, Logger.withTraceSkipContext({
|
|
211
|
+
error: error instanceof Error ? error.message : String(error),
|
|
212
|
+
}));
|
|
213
|
+
};
|
|
214
|
+
const clearKvRemoteDriver = async () => {
|
|
215
|
+
Logger.warn('KV remote clear() is not implemented.');
|
|
216
|
+
await Promise.resolve();
|
|
217
|
+
};
|
|
218
|
+
const createKvRemoteHas = (getJson) => {
|
|
219
|
+
return async function has(key) {
|
|
220
|
+
if (!hasCloudflareApiCreds())
|
|
221
|
+
return (await this.get(key)) !== null;
|
|
222
|
+
const settings = getSettings();
|
|
223
|
+
if (!hasProxySigningCreds(settings))
|
|
224
|
+
return (await getJson(key)) !== null;
|
|
225
|
+
return (await this.get(key)) !== null;
|
|
226
|
+
};
|
|
227
|
+
};
|
|
209
228
|
const createKvRemoteDriver = () => {
|
|
210
229
|
const resolveNamespaceId = createCloudflareNamespaceIdResolver();
|
|
211
230
|
const cf = createCloudflareKvApiClient(resolveNamespaceId);
|
|
@@ -226,9 +245,7 @@ const createKvRemoteDriver = () => {
|
|
|
226
245
|
catch (error) {
|
|
227
246
|
if (!hasCloudflareApiCreds())
|
|
228
247
|
throw error;
|
|
229
|
-
|
|
230
|
-
error: error instanceof Error ? error.message : String(error),
|
|
231
|
-
});
|
|
248
|
+
logKvRemoteProxyFallback('GET', error);
|
|
232
249
|
return cf.getJson(key);
|
|
233
250
|
}
|
|
234
251
|
},
|
|
@@ -250,9 +267,7 @@ const createKvRemoteDriver = () => {
|
|
|
250
267
|
catch (error) {
|
|
251
268
|
if (!hasCloudflareApiCreds())
|
|
252
269
|
throw error;
|
|
253
|
-
|
|
254
|
-
error: error instanceof Error ? error.message : String(error),
|
|
255
|
-
});
|
|
270
|
+
logKvRemoteProxyFallback('PUT', error);
|
|
256
271
|
await cf.putJson(key, value, ttl);
|
|
257
272
|
}
|
|
258
273
|
},
|
|
@@ -272,24 +287,12 @@ const createKvRemoteDriver = () => {
|
|
|
272
287
|
catch (error) {
|
|
273
288
|
if (!hasCloudflareApiCreds())
|
|
274
289
|
throw error;
|
|
275
|
-
|
|
276
|
-
error: error instanceof Error ? error.message : String(error),
|
|
277
|
-
});
|
|
290
|
+
logKvRemoteProxyFallback('DELETE', error);
|
|
278
291
|
await cf.deleteKey(key);
|
|
279
292
|
}
|
|
280
293
|
},
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
await Promise.resolve();
|
|
284
|
-
},
|
|
285
|
-
async has(key) {
|
|
286
|
-
if (!hasCloudflareApiCreds())
|
|
287
|
-
return (await this.get(key)) !== null;
|
|
288
|
-
const settings = getSettings();
|
|
289
|
-
if (!hasProxySigningCreds(settings))
|
|
290
|
-
return (await cf.getJson(key)) !== null;
|
|
291
|
-
return (await this.get(key)) !== null;
|
|
292
|
-
},
|
|
294
|
+
clear: clearKvRemoteDriver,
|
|
295
|
+
has: createKvRemoteHas(cf.getJson),
|
|
293
296
|
};
|
|
294
297
|
};
|
|
295
298
|
export const KVRemoteDriver = Object.freeze({
|
|
@@ -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"}
|