alepha 0.19.4 → 0.19.5
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/api/audits/index.d.ts +8 -8
- package/dist/api/issues/index.d.ts +810 -0
- package/dist/api/issues/index.d.ts.map +1 -0
- package/dist/api/issues/index.js +447 -0
- package/dist/api/issues/index.js.map +1 -0
- package/dist/api/keys/index.d.ts +5 -5
- package/dist/api/users/index.d.ts +6 -0
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +10 -3
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/workflows/index.d.ts +3 -3
- package/dist/captcha/index.d.ts +142 -0
- package/dist/captcha/index.d.ts.map +1 -0
- package/dist/captcha/index.js +177 -0
- package/dist/captcha/index.js.map +1 -0
- package/dist/cli/core/index.d.ts +82 -2
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +90 -6
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +84 -10
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +92 -4
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/vendor/index.d.ts +30 -3
- package/dist/cli/vendor/index.d.ts.map +1 -1
- package/dist/cli/vendor/index.js +98 -21
- package/dist/cli/vendor/index.js.map +1 -1
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +2 -3
- package/dist/command/index.js.map +1 -1
- package/dist/orm/core/index.bun.js +6 -6
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +6 -6
- package/dist/orm/core/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +1 -0
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/i18n/index.js +8 -4
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +145 -2
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +364 -63
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js.map +1 -1
- package/package.json +11 -1
- package/src/api/issues/__tests__/IssueService.spec.ts +263 -0
- package/src/api/issues/controllers/AdminIssueController.ts +149 -0
- package/src/api/issues/controllers/IssueController.ts +44 -0
- package/src/api/issues/entities/issues.ts +49 -0
- package/src/api/issues/index.ts +53 -0
- package/src/api/issues/schemas/createIssueSchema.ts +13 -0
- package/src/api/issues/schemas/issueConfigAtom.ts +13 -0
- package/src/api/issues/schemas/issueQuerySchema.ts +18 -0
- package/src/api/issues/schemas/issueResourceSchema.ts +6 -0
- package/src/api/issues/schemas/myIssueQuerySchema.ts +10 -0
- package/src/api/issues/schemas/updateIssueSchema.ts +13 -0
- package/src/api/issues/services/IssueService.ts +264 -0
- package/src/api/users/primitives/$realm.ts +24 -0
- package/src/api/users/services/CredentialService.ts +6 -3
- package/src/api/users/services/RegistrationService.ts +15 -5
- package/src/api/users/services/SessionService.ts +2 -0
- package/src/captcha/__tests__/MemoryCaptchaProvider.spec.ts +74 -0
- package/src/captcha/index.ts +33 -0
- package/src/captcha/providers/CaptchaProvider.ts +17 -0
- package/src/captcha/providers/MemoryCaptchaProvider.ts +65 -0
- package/src/captcha/providers/TurnstileCaptchaProvider.ts +125 -0
- package/src/cli/core/atoms/buildOptions.ts +57 -0
- package/src/cli/core/commands/build.ts +2 -0
- package/src/cli/core/providers/ViteDevServerProvider.ts +1 -1
- package/src/cli/core/services/ViteUtils.ts +5 -2
- package/src/cli/core/tasks/BuildClientTask.ts +3 -1
- package/src/cli/core/tasks/BuildCloudflareTask.ts +4 -0
- package/src/cli/core/tasks/BuildPwaTask.ts +81 -0
- package/src/cli/platform/adapters/CloudflareAdapter.ts +24 -0
- package/src/cli/platform/atoms/platformOptions.ts +19 -3
- package/src/cli/platform/hooks/PlatformHook.ts +51 -0
- package/src/cli/platform/index.ts +1 -0
- package/src/cli/platform/services/CloudflareApi.ts +22 -1
- package/src/cli/platform/services/PlatformOrchestrator.ts +67 -2
- package/src/cli/vendor/__tests__/VendorService.spec.ts +40 -1
- package/src/cli/vendor/commands/VendorCommand.ts +41 -38
- package/src/cli/vendor/services/VendorService.ts +108 -4
- package/src/command/__tests__/CliProvider.spec.ts +45 -0
- package/src/command/providers/CliProvider.ts +3 -4
- package/src/orm/core/services/Repository.ts +20 -6
- package/src/react/i18n/__tests__/I18nProvider.spec.ts +83 -0
- package/src/react/i18n/providers/I18nProvider.ts +12 -10
- package/src/security/primitives/$issuer.ts +3 -1
- package/src/server/auth/index.ts +7 -0
- package/src/server/auth/primitives/$auth.ts +37 -3
- package/src/server/auth/primitives/$authApple.ts +114 -4
- package/src/server/auth/primitives/$authFacebook.ts +98 -0
- package/src/server/auth/primitives/$authFranceConnect.ts +105 -0
- package/src/server/auth/primitives/$authGithub.ts +22 -16
- package/src/server/auth/primitives/$authMicrosoft.ts +88 -0
- package/src/server/auth/providers/ServerAuthProvider.ts +197 -72
- package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -0
- package/src/server/core/__tests__/ServerRouterProvider-errorHandler.spec.ts +1 -1
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +3 -1
|
@@ -657,7 +657,7 @@ declare class WorkflowProvider {
|
|
|
657
657
|
protected readonly stepLogs: _$alepha_orm0.Repository<_$alepha.TObject<{
|
|
658
658
|
id: _$alepha_orm0.PgAttr<_$alepha_orm0.PgAttr<_$alepha.TString, typeof _$alepha_orm0.PG_PRIMARY_KEY>, typeof _$alepha_orm0.PG_DEFAULT>;
|
|
659
659
|
logs: _$alepha.TArray<_$alepha.TObject<{
|
|
660
|
-
level: _$alepha.TUnsafe<"
|
|
660
|
+
level: _$alepha.TUnsafe<"SILENT" | "TRACE" | "DEBUG" | "INFO" | "WARN" | "ERROR">;
|
|
661
661
|
message: _$alepha.TString;
|
|
662
662
|
service: _$alepha.TString;
|
|
663
663
|
module: _$alepha.TString;
|
|
@@ -675,7 +675,7 @@ declare class WorkflowProvider {
|
|
|
675
675
|
data?: any;
|
|
676
676
|
context?: string | undefined;
|
|
677
677
|
app?: string | undefined;
|
|
678
|
-
level: "
|
|
678
|
+
level: "SILENT" | "TRACE" | "DEBUG" | "INFO" | "WARN" | "ERROR";
|
|
679
679
|
message: string;
|
|
680
680
|
service: string;
|
|
681
681
|
module: string;
|
|
@@ -1412,7 +1412,7 @@ declare class AdminWorkflowController {
|
|
|
1412
1412
|
declare const workflowStepLogs: _$alepha_orm0.EntityPrimitive<_$alepha.TObject<{
|
|
1413
1413
|
id: _$alepha_orm0.PgAttr<_$alepha_orm0.PgAttr<_$alepha.TString, typeof _$alepha_orm0.PG_PRIMARY_KEY>, typeof _$alepha_orm0.PG_DEFAULT>;
|
|
1414
1414
|
logs: _$alepha.TArray<_$alepha.TObject<{
|
|
1415
|
-
level: _$alepha.TUnsafe<"
|
|
1415
|
+
level: _$alepha.TUnsafe<"SILENT" | "TRACE" | "DEBUG" | "INFO" | "WARN" | "ERROR">;
|
|
1416
1416
|
message: _$alepha.TString;
|
|
1417
1417
|
service: _$alepha.TString;
|
|
1418
1418
|
module: _$alepha.TString;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as _$alepha from "alepha";
|
|
2
|
+
import * as _$alepha_logger0 from "alepha/logger";
|
|
3
|
+
|
|
4
|
+
//#region ../../src/captcha/providers/CaptchaProvider.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Captcha verification provider interface.
|
|
7
|
+
*
|
|
8
|
+
* Verifies that a user-submitted captcha token is valid. Implementations
|
|
9
|
+
* call the relevant captcha service (Turnstile, reCAPTCHA, hCaptcha, etc.)
|
|
10
|
+
* to validate the token server-side.
|
|
11
|
+
*/
|
|
12
|
+
declare abstract class CaptchaProvider {
|
|
13
|
+
/**
|
|
14
|
+
* Verify a captcha token.
|
|
15
|
+
*
|
|
16
|
+
* @param token - The captcha response token submitted by the client.
|
|
17
|
+
* @param ip - Optional client IP address for additional validation.
|
|
18
|
+
* @returns Whether the token is valid.
|
|
19
|
+
*/
|
|
20
|
+
abstract verify(token: string, ip?: string): Promise<boolean>;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region ../../src/captcha/providers/MemoryCaptchaProvider.d.ts
|
|
24
|
+
interface CaptchaRecord {
|
|
25
|
+
token: string;
|
|
26
|
+
ip?: string;
|
|
27
|
+
verifiedAt: Date;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* In-memory captcha provider for testing.
|
|
31
|
+
*
|
|
32
|
+
* Accepts all tokens by default. Use `reject()` to make verification fail,
|
|
33
|
+
* and `accept()` to restore. All verification attempts are recorded for assertions.
|
|
34
|
+
*/
|
|
35
|
+
declare class MemoryCaptchaProvider implements CaptchaProvider {
|
|
36
|
+
protected readonly log: _$alepha_logger0.Logger;
|
|
37
|
+
/**
|
|
38
|
+
* All verification attempts.
|
|
39
|
+
*/
|
|
40
|
+
records: CaptchaRecord[];
|
|
41
|
+
protected shouldAccept: boolean;
|
|
42
|
+
verify(token: string, ip?: string): Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Make all subsequent verifications fail.
|
|
45
|
+
*/
|
|
46
|
+
reject(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Make all subsequent verifications pass (default behavior).
|
|
49
|
+
*/
|
|
50
|
+
accept(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Whether a token was verified.
|
|
53
|
+
*/
|
|
54
|
+
wasVerified(token: string): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Get the last verification attempt.
|
|
57
|
+
*/
|
|
58
|
+
get last(): CaptchaRecord | undefined;
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region ../../src/captcha/providers/TurnstileCaptchaProvider.d.ts
|
|
62
|
+
/**
|
|
63
|
+
* Cloudflare Turnstile captcha verification provider.
|
|
64
|
+
*
|
|
65
|
+
* Validates captcha tokens against the Cloudflare Turnstile siteverify API.
|
|
66
|
+
* Free, privacy-friendly, and supports invisible mode.
|
|
67
|
+
*
|
|
68
|
+
* ## Setup
|
|
69
|
+
*
|
|
70
|
+
* 1. Create a Turnstile widget at https://dash.cloudflare.com/?to=/:account/turnstile
|
|
71
|
+
* 2. Copy the **Site Key** (public, for the client) and **Secret Key** (private, for the server)
|
|
72
|
+
* 3. Set `TURNSTILE_SECRET_KEY` in your environment
|
|
73
|
+
*
|
|
74
|
+
* ## Client-side integration
|
|
75
|
+
*
|
|
76
|
+
* Add the Turnstile script and widget to your form:
|
|
77
|
+
*
|
|
78
|
+
* ```html
|
|
79
|
+
* <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
|
80
|
+
* <form>
|
|
81
|
+
* <div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
|
|
82
|
+
* <button type="submit">Submit</button>
|
|
83
|
+
* </form>
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* The widget injects a hidden `cf-turnstile-response` input into the form.
|
|
87
|
+
* Send this value as the `captchaToken` in your registration request.
|
|
88
|
+
*
|
|
89
|
+
* For explicit rendering (React, SPA):
|
|
90
|
+
*
|
|
91
|
+
* ```ts
|
|
92
|
+
* turnstile.render("#container", {
|
|
93
|
+
* sitekey: "YOUR_SITE_KEY",
|
|
94
|
+
* callback: (token) => setCaptchaToken(token),
|
|
95
|
+
* });
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* ## Server-side usage
|
|
99
|
+
*
|
|
100
|
+
* Register the provider in your app:
|
|
101
|
+
*
|
|
102
|
+
* ```ts
|
|
103
|
+
* import { CaptchaProvider } from "alepha/captcha";
|
|
104
|
+
* import { TurnstileCaptchaProvider } from "alepha/captcha";
|
|
105
|
+
*
|
|
106
|
+
* alepha.with({ provide: CaptchaProvider, use: TurnstileCaptchaProvider });
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* ## Test keys (for development)
|
|
110
|
+
*
|
|
111
|
+
* - Always passes: site `1x00000000000000000000AA`, secret `1x0000000000000000000000000000000AA`
|
|
112
|
+
* - Always blocks: site `2x00000000000000000000AB`, secret `2x0000000000000000000000000000000AB`
|
|
113
|
+
* - Forces interactive: site `3x00000000000000000000FF`
|
|
114
|
+
*
|
|
115
|
+
* ## Environment Variables
|
|
116
|
+
*
|
|
117
|
+
* - `TURNSTILE_SECRET_KEY`: The secret key from the Cloudflare Turnstile dashboard.
|
|
118
|
+
*
|
|
119
|
+
* @see https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
|
|
120
|
+
*/
|
|
121
|
+
declare class TurnstileCaptchaProvider implements CaptchaProvider {
|
|
122
|
+
protected readonly log: _$alepha_logger0.Logger;
|
|
123
|
+
protected readonly secretKey: string;
|
|
124
|
+
constructor();
|
|
125
|
+
verify(token: string, ip?: string): Promise<boolean>;
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region ../../src/captcha/index.d.ts
|
|
129
|
+
/**
|
|
130
|
+
* Captcha verification for bot protection.
|
|
131
|
+
*
|
|
132
|
+
* **Features:**
|
|
133
|
+
* - Provider abstraction for captcha services
|
|
134
|
+
* - Cloudflare Turnstile support (free, privacy-friendly)
|
|
135
|
+
* - In-memory provider for testing
|
|
136
|
+
*
|
|
137
|
+
* @module alepha.captcha
|
|
138
|
+
*/
|
|
139
|
+
declare const AlephaCaptcha: _$alepha.Service<_$alepha.Module>;
|
|
140
|
+
//#endregion
|
|
141
|
+
export { AlephaCaptcha, CaptchaProvider, CaptchaRecord, MemoryCaptchaProvider, TurnstileCaptchaProvider };
|
|
142
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/captcha/providers/CaptchaProvider.ts","../../src/captcha/providers/MemoryCaptchaProvider.ts","../../src/captcha/providers/TurnstileCaptchaProvider.ts","../../src/captcha/index.ts"],"mappings":";;;;;;;;AAOA;;;uBAAsB,eAAA;EAQJ;;;;;;;EAAA,SAAA,MAAA,CAAO,KAAA,UAAe,EAAA,YAAc,OAAA;AAAA;;;UCZrC,aAAA;EACf,KAAA;EACA,EAAA;EACA,UAAA,EAAY,IAAA;AAAA;;;;;;;cASD,qBAAA,YAAiC,eAAA;EAAA,mBACzB,GAAA,EADc,gBAAA,CACX,MAAA;;;;EAKf,OAAA,EAAS,aAAA;EAAA,UAEN,YAAA;EAEG,MAAA,CAAO,KAAA,UAAe,EAAA,YAAc,OAAA;EAnBjC;;;EAkCT,MAAA,CAAA;EAlCK;;;EAyCL,MAAA,CAAA;EAhC0B;;;EAuC1B,WAAA,CAAY,KAAA;EA7B8B;;;EAAA,IAoCtC,IAAA,CAAA,GAAQ,aAAA;AAAA;;;;;;ADtDrB;;;;;;;;;;;;;ACJA;;;;;;;;;;AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACgDA;;cAAa,wBAAA,YAAoC,eAAA;EAAA,mBAC5B,GAAA,EADiB,gBAAA,CACd,MAAA;EAAA,mBACH,SAAA;;EAiBN,MAAA,CAAO,KAAA,UAAe,EAAA,YAAc,OAAA;AAAA;;;;AF3EnD;;;;;;;;;cGgBa,aAAA,EAAa,QAAA,CAAA,OAAA,CASxB,QAAA,CATwB,MAAA"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { $context, $module, AlephaError, t } from "alepha";
|
|
2
|
+
import { $logger } from "alepha/logger";
|
|
3
|
+
//#region ../../src/captcha/providers/CaptchaProvider.ts
|
|
4
|
+
/**
|
|
5
|
+
* Captcha verification provider interface.
|
|
6
|
+
*
|
|
7
|
+
* Verifies that a user-submitted captcha token is valid. Implementations
|
|
8
|
+
* call the relevant captcha service (Turnstile, reCAPTCHA, hCaptcha, etc.)
|
|
9
|
+
* to validate the token server-side.
|
|
10
|
+
*/
|
|
11
|
+
var CaptchaProvider = class {};
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region ../../src/captcha/providers/MemoryCaptchaProvider.ts
|
|
14
|
+
/**
|
|
15
|
+
* In-memory captcha provider for testing.
|
|
16
|
+
*
|
|
17
|
+
* Accepts all tokens by default. Use `reject()` to make verification fail,
|
|
18
|
+
* and `accept()` to restore. All verification attempts are recorded for assertions.
|
|
19
|
+
*/
|
|
20
|
+
var MemoryCaptchaProvider = class {
|
|
21
|
+
log = $logger();
|
|
22
|
+
/**
|
|
23
|
+
* All verification attempts.
|
|
24
|
+
*/
|
|
25
|
+
records = [];
|
|
26
|
+
shouldAccept = true;
|
|
27
|
+
async verify(token, ip) {
|
|
28
|
+
this.log.debug("Verifying captcha in memory store", {
|
|
29
|
+
token,
|
|
30
|
+
ip
|
|
31
|
+
});
|
|
32
|
+
this.records.push({
|
|
33
|
+
token,
|
|
34
|
+
ip,
|
|
35
|
+
verifiedAt: /* @__PURE__ */ new Date()
|
|
36
|
+
});
|
|
37
|
+
return this.shouldAccept;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Make all subsequent verifications fail.
|
|
41
|
+
*/
|
|
42
|
+
reject() {
|
|
43
|
+
this.shouldAccept = false;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Make all subsequent verifications pass (default behavior).
|
|
47
|
+
*/
|
|
48
|
+
accept() {
|
|
49
|
+
this.shouldAccept = true;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Whether a token was verified.
|
|
53
|
+
*/
|
|
54
|
+
wasVerified(token) {
|
|
55
|
+
return this.records.some((r) => r.token === token);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get the last verification attempt.
|
|
59
|
+
*/
|
|
60
|
+
get last() {
|
|
61
|
+
return this.records[this.records.length - 1];
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region ../../src/captcha/providers/TurnstileCaptchaProvider.ts
|
|
66
|
+
/**
|
|
67
|
+
* Cloudflare Turnstile captcha verification provider.
|
|
68
|
+
*
|
|
69
|
+
* Validates captcha tokens against the Cloudflare Turnstile siteverify API.
|
|
70
|
+
* Free, privacy-friendly, and supports invisible mode.
|
|
71
|
+
*
|
|
72
|
+
* ## Setup
|
|
73
|
+
*
|
|
74
|
+
* 1. Create a Turnstile widget at https://dash.cloudflare.com/?to=/:account/turnstile
|
|
75
|
+
* 2. Copy the **Site Key** (public, for the client) and **Secret Key** (private, for the server)
|
|
76
|
+
* 3. Set `TURNSTILE_SECRET_KEY` in your environment
|
|
77
|
+
*
|
|
78
|
+
* ## Client-side integration
|
|
79
|
+
*
|
|
80
|
+
* Add the Turnstile script and widget to your form:
|
|
81
|
+
*
|
|
82
|
+
* ```html
|
|
83
|
+
* <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer><\/script>
|
|
84
|
+
* <form>
|
|
85
|
+
* <div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
|
|
86
|
+
* <button type="submit">Submit</button>
|
|
87
|
+
* </form>
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* The widget injects a hidden `cf-turnstile-response` input into the form.
|
|
91
|
+
* Send this value as the `captchaToken` in your registration request.
|
|
92
|
+
*
|
|
93
|
+
* For explicit rendering (React, SPA):
|
|
94
|
+
*
|
|
95
|
+
* ```ts
|
|
96
|
+
* turnstile.render("#container", {
|
|
97
|
+
* sitekey: "YOUR_SITE_KEY",
|
|
98
|
+
* callback: (token) => setCaptchaToken(token),
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* ## Server-side usage
|
|
103
|
+
*
|
|
104
|
+
* Register the provider in your app:
|
|
105
|
+
*
|
|
106
|
+
* ```ts
|
|
107
|
+
* import { CaptchaProvider } from "alepha/captcha";
|
|
108
|
+
* import { TurnstileCaptchaProvider } from "alepha/captcha";
|
|
109
|
+
*
|
|
110
|
+
* alepha.with({ provide: CaptchaProvider, use: TurnstileCaptchaProvider });
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* ## Test keys (for development)
|
|
114
|
+
*
|
|
115
|
+
* - Always passes: site `1x00000000000000000000AA`, secret `1x0000000000000000000000000000000AA`
|
|
116
|
+
* - Always blocks: site `2x00000000000000000000AB`, secret `2x0000000000000000000000000000000AB`
|
|
117
|
+
* - Forces interactive: site `3x00000000000000000000FF`
|
|
118
|
+
*
|
|
119
|
+
* ## Environment Variables
|
|
120
|
+
*
|
|
121
|
+
* - `TURNSTILE_SECRET_KEY`: The secret key from the Cloudflare Turnstile dashboard.
|
|
122
|
+
*
|
|
123
|
+
* @see https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
|
|
124
|
+
*/
|
|
125
|
+
var TurnstileCaptchaProvider = class {
|
|
126
|
+
log = $logger();
|
|
127
|
+
secretKey;
|
|
128
|
+
constructor() {
|
|
129
|
+
const { alepha } = $context();
|
|
130
|
+
this.secretKey = alepha.parseEnv(t.object({ TURNSTILE_SECRET_KEY: t.text({ description: "The secret key from the Cloudflare Turnstile dashboard." }) })).TURNSTILE_SECRET_KEY;
|
|
131
|
+
}
|
|
132
|
+
async verify(token, ip) {
|
|
133
|
+
const body = new URLSearchParams();
|
|
134
|
+
body.set("secret", this.secretKey);
|
|
135
|
+
body.set("response", token);
|
|
136
|
+
if (ip) body.set("remoteip", ip);
|
|
137
|
+
try {
|
|
138
|
+
const data = await (await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
|
139
|
+
method: "POST",
|
|
140
|
+
body
|
|
141
|
+
})).json();
|
|
142
|
+
if (!data.success) this.log.debug("Turnstile verification failed", { errorCodes: data["error-codes"] });
|
|
143
|
+
return data.success;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
throw new AlephaError("Failed to verify Turnstile captcha token", { cause: error });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region ../../src/captcha/index.ts
|
|
151
|
+
/**
|
|
152
|
+
* Captcha verification for bot protection.
|
|
153
|
+
*
|
|
154
|
+
* **Features:**
|
|
155
|
+
* - Provider abstraction for captcha services
|
|
156
|
+
* - Cloudflare Turnstile support (free, privacy-friendly)
|
|
157
|
+
* - In-memory provider for testing
|
|
158
|
+
*
|
|
159
|
+
* @module alepha.captcha
|
|
160
|
+
*/
|
|
161
|
+
const AlephaCaptcha = $module({
|
|
162
|
+
name: "alepha.captcha",
|
|
163
|
+
services: [
|
|
164
|
+
CaptchaProvider,
|
|
165
|
+
MemoryCaptchaProvider,
|
|
166
|
+
TurnstileCaptchaProvider
|
|
167
|
+
],
|
|
168
|
+
register: (alepha) => alepha.with({
|
|
169
|
+
optional: true,
|
|
170
|
+
provide: CaptchaProvider,
|
|
171
|
+
use: MemoryCaptchaProvider
|
|
172
|
+
})
|
|
173
|
+
});
|
|
174
|
+
//#endregion
|
|
175
|
+
export { AlephaCaptcha, CaptchaProvider, MemoryCaptchaProvider, TurnstileCaptchaProvider };
|
|
176
|
+
|
|
177
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/captcha/providers/CaptchaProvider.ts","../../src/captcha/providers/MemoryCaptchaProvider.ts","../../src/captcha/providers/TurnstileCaptchaProvider.ts","../../src/captcha/index.ts"],"sourcesContent":["/**\n * Captcha verification provider interface.\n *\n * Verifies that a user-submitted captcha token is valid. Implementations\n * call the relevant captcha service (Turnstile, reCAPTCHA, hCaptcha, etc.)\n * to validate the token server-side.\n */\nexport abstract class CaptchaProvider {\n /**\n * Verify a captcha token.\n *\n * @param token - The captcha response token submitted by the client.\n * @param ip - Optional client IP address for additional validation.\n * @returns Whether the token is valid.\n */\n public abstract verify(token: string, ip?: string): Promise<boolean>;\n}\n","import { $logger } from \"alepha/logger\";\nimport type { CaptchaProvider } from \"./CaptchaProvider.ts\";\n\nexport interface CaptchaRecord {\n token: string;\n ip?: string;\n verifiedAt: Date;\n}\n\n/**\n * In-memory captcha provider for testing.\n *\n * Accepts all tokens by default. Use `reject()` to make verification fail,\n * and `accept()` to restore. All verification attempts are recorded for assertions.\n */\nexport class MemoryCaptchaProvider implements CaptchaProvider {\n protected readonly log = $logger();\n\n /**\n * All verification attempts.\n */\n public records: CaptchaRecord[] = [];\n\n protected shouldAccept = true;\n\n public async verify(token: string, ip?: string): Promise<boolean> {\n this.log.debug(\"Verifying captcha in memory store\", { token, ip });\n\n this.records.push({\n token,\n ip,\n verifiedAt: new Date(),\n });\n\n return this.shouldAccept;\n }\n\n /**\n * Make all subsequent verifications fail.\n */\n public reject(): void {\n this.shouldAccept = false;\n }\n\n /**\n * Make all subsequent verifications pass (default behavior).\n */\n public accept(): void {\n this.shouldAccept = true;\n }\n\n /**\n * Whether a token was verified.\n */\n public wasVerified(token: string): boolean {\n return this.records.some((r) => r.token === token);\n }\n\n /**\n * Get the last verification attempt.\n */\n public get last(): CaptchaRecord | undefined {\n return this.records[this.records.length - 1];\n }\n}\n","import { $context, AlephaError, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { CaptchaProvider } from \"./CaptchaProvider.ts\";\n\n/**\n * Cloudflare Turnstile captcha verification provider.\n *\n * Validates captcha tokens against the Cloudflare Turnstile siteverify API.\n * Free, privacy-friendly, and supports invisible mode.\n *\n * ## Setup\n *\n * 1. Create a Turnstile widget at https://dash.cloudflare.com/?to=/:account/turnstile\n * 2. Copy the **Site Key** (public, for the client) and **Secret Key** (private, for the server)\n * 3. Set `TURNSTILE_SECRET_KEY` in your environment\n *\n * ## Client-side integration\n *\n * Add the Turnstile script and widget to your form:\n *\n * ```html\n * <script src=\"https://challenges.cloudflare.com/turnstile/v0/api.js\" async defer></script>\n * <form>\n * <div class=\"cf-turnstile\" data-sitekey=\"YOUR_SITE_KEY\"></div>\n * <button type=\"submit\">Submit</button>\n * </form>\n * ```\n *\n * The widget injects a hidden `cf-turnstile-response` input into the form.\n * Send this value as the `captchaToken` in your registration request.\n *\n * For explicit rendering (React, SPA):\n *\n * ```ts\n * turnstile.render(\"#container\", {\n * sitekey: \"YOUR_SITE_KEY\",\n * callback: (token) => setCaptchaToken(token),\n * });\n * ```\n *\n * ## Server-side usage\n *\n * Register the provider in your app:\n *\n * ```ts\n * import { CaptchaProvider } from \"alepha/captcha\";\n * import { TurnstileCaptchaProvider } from \"alepha/captcha\";\n *\n * alepha.with({ provide: CaptchaProvider, use: TurnstileCaptchaProvider });\n * ```\n *\n * ## Test keys (for development)\n *\n * - Always passes: site `1x00000000000000000000AA`, secret `1x0000000000000000000000000000000AA`\n * - Always blocks: site `2x00000000000000000000AB`, secret `2x0000000000000000000000000000000AB`\n * - Forces interactive: site `3x00000000000000000000FF`\n *\n * ## Environment Variables\n *\n * - `TURNSTILE_SECRET_KEY`: The secret key from the Cloudflare Turnstile dashboard.\n *\n * @see https://developers.cloudflare.com/turnstile/get-started/server-side-validation/\n */\nexport class TurnstileCaptchaProvider implements CaptchaProvider {\n protected readonly log = $logger();\n protected readonly secretKey: string;\n\n constructor() {\n const { alepha } = $context();\n\n const env = alepha.parseEnv(\n t.object({\n TURNSTILE_SECRET_KEY: t.text({\n description:\n \"The secret key from the Cloudflare Turnstile dashboard.\",\n }),\n }),\n );\n\n this.secretKey = env.TURNSTILE_SECRET_KEY;\n }\n\n public async verify(token: string, ip?: string): Promise<boolean> {\n const body = new URLSearchParams();\n body.set(\"secret\", this.secretKey);\n body.set(\"response\", token);\n\n if (ip) {\n body.set(\"remoteip\", ip);\n }\n\n try {\n const res = await fetch(\n \"https://challenges.cloudflare.com/turnstile/v0/siteverify\",\n {\n method: \"POST\",\n body,\n },\n );\n\n const data = (await res.json()) as TurnstileResponse;\n\n if (!data.success) {\n this.log.debug(\"Turnstile verification failed\", {\n errorCodes: data[\"error-codes\"],\n });\n }\n\n return data.success;\n } catch (error) {\n throw new AlephaError(\"Failed to verify Turnstile captcha token\", {\n cause: error,\n });\n }\n }\n}\n\ninterface TurnstileResponse {\n success: boolean;\n \"error-codes\"?: string[];\n challenge_ts?: string;\n hostname?: string;\n action?: string;\n cdata?: string;\n}\n","import { $module } from \"alepha\";\nimport { CaptchaProvider } from \"./providers/CaptchaProvider.ts\";\nimport { MemoryCaptchaProvider } from \"./providers/MemoryCaptchaProvider.ts\";\nimport { TurnstileCaptchaProvider } from \"./providers/TurnstileCaptchaProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/CaptchaProvider.ts\";\nexport * from \"./providers/MemoryCaptchaProvider.ts\";\nexport * from \"./providers/TurnstileCaptchaProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Captcha verification for bot protection.\n *\n * **Features:**\n * - Provider abstraction for captcha services\n * - Cloudflare Turnstile support (free, privacy-friendly)\n * - In-memory provider for testing\n *\n * @module alepha.captcha\n */\nexport const AlephaCaptcha = $module({\n name: \"alepha.captcha\",\n services: [CaptchaProvider, MemoryCaptchaProvider, TurnstileCaptchaProvider],\n register: (alepha) =>\n alepha.with({\n optional: true,\n provide: CaptchaProvider,\n use: MemoryCaptchaProvider,\n }),\n});\n"],"mappings":";;;;;;;;;;AAOA,IAAsB,kBAAtB,MAAsC;;;;;;;;;ACQtC,IAAa,wBAAb,MAA8D;CAC5D,MAAyB,SAAS;;;;CAKlC,UAAkC,EAAE;CAEpC,eAAyB;CAEzB,MAAa,OAAO,OAAe,IAA+B;AAChE,OAAK,IAAI,MAAM,qCAAqC;GAAE;GAAO;GAAI,CAAC;AAElE,OAAK,QAAQ,KAAK;GAChB;GACA;GACA,4BAAY,IAAI,MAAM;GACvB,CAAC;AAEF,SAAO,KAAK;;;;;CAMd,SAAsB;AACpB,OAAK,eAAe;;;;;CAMtB,SAAsB;AACpB,OAAK,eAAe;;;;;CAMtB,YAAmB,OAAwB;AACzC,SAAO,KAAK,QAAQ,MAAM,MAAM,EAAE,UAAU,MAAM;;;;;CAMpD,IAAW,OAAkC;AAC3C,SAAO,KAAK,QAAQ,KAAK,QAAQ,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACC9C,IAAa,2BAAb,MAAiE;CAC/D,MAAyB,SAAS;CAClC;CAEA,cAAc;EACZ,MAAM,EAAE,WAAW,UAAU;AAW7B,OAAK,YATO,OAAO,SACjB,EAAE,OAAO,EACP,sBAAsB,EAAE,KAAK,EAC3B,aACE,2DACH,CAAC,EACH,CAAC,CACH,CAEoB;;CAGvB,MAAa,OAAO,OAAe,IAA+B;EAChE,MAAM,OAAO,IAAI,iBAAiB;AAClC,OAAK,IAAI,UAAU,KAAK,UAAU;AAClC,OAAK,IAAI,YAAY,MAAM;AAE3B,MAAI,GACF,MAAK,IAAI,YAAY,GAAG;AAG1B,MAAI;GASF,MAAM,OAAQ,OARF,MAAM,MAChB,6DACA;IACE,QAAQ;IACR;IACD,CACF,EAEuB,MAAM;AAE9B,OAAI,CAAC,KAAK,QACR,MAAK,IAAI,MAAM,iCAAiC,EAC9C,YAAY,KAAK,gBAClB,CAAC;AAGJ,UAAO,KAAK;WACL,OAAO;AACd,SAAM,IAAI,YAAY,4CAA4C,EAChE,OAAO,OACR,CAAC;;;;;;;;;;;;;;;;ACzFR,MAAa,gBAAgB,QAAQ;CACnC,MAAM;CACN,UAAU;EAAC;EAAiB;EAAuB;EAAyB;CAC5E,WAAW,WACT,OAAO,KAAK;EACV,UAAU;EACV,SAAS;EACT,KAAK;EACN,CAAC;CACL,CAAC"}
|
package/dist/cli/core/index.d.ts
CHANGED
|
@@ -202,6 +202,53 @@ declare const buildOptions: _$alepha.Atom<_$alepha.TObject<{
|
|
|
202
202
|
*/
|
|
203
203
|
domain: _$alepha.TOptional<_$alepha.TString>;
|
|
204
204
|
}>>;
|
|
205
|
+
/**
|
|
206
|
+
* PWA (Progressive Web App) configuration.
|
|
207
|
+
*
|
|
208
|
+
* Generates a web app manifest and enables installability.
|
|
209
|
+
* Requires a client-side bundle (React).
|
|
210
|
+
*/
|
|
211
|
+
pwa: _$alepha.TOptional<_$alepha.TObject<{
|
|
212
|
+
/**
|
|
213
|
+
* Full application name displayed on the splash screen
|
|
214
|
+
* and in the OS app switcher.
|
|
215
|
+
*/
|
|
216
|
+
name: _$alepha.TString;
|
|
217
|
+
/**
|
|
218
|
+
* Short name displayed on the home screen icon.
|
|
219
|
+
* Falls back to `name` if omitted.
|
|
220
|
+
*/
|
|
221
|
+
shortName: _$alepha.TOptional<_$alepha.TString>;
|
|
222
|
+
/**
|
|
223
|
+
* Theme color used for the browser toolbar and OS chrome.
|
|
224
|
+
*
|
|
225
|
+
* @default "#ffffff"
|
|
226
|
+
*/
|
|
227
|
+
themeColor: _$alepha.TOptional<_$alepha.TString>;
|
|
228
|
+
/**
|
|
229
|
+
* Background color for the splash screen.
|
|
230
|
+
*
|
|
231
|
+
* @default "#ffffff"
|
|
232
|
+
*/
|
|
233
|
+
backgroundColor: _$alepha.TOptional<_$alepha.TString>;
|
|
234
|
+
/**
|
|
235
|
+
* Display mode for the installed PWA.
|
|
236
|
+
*
|
|
237
|
+
* - `standalone` - Looks like a native app (default)
|
|
238
|
+
* - `fullscreen` - Uses entire screen (games, immersive)
|
|
239
|
+
* - `minimal-ui` - Like standalone with minimal browser UI
|
|
240
|
+
* - `browser` - Standard browser tab
|
|
241
|
+
*
|
|
242
|
+
* @default "standalone"
|
|
243
|
+
*/
|
|
244
|
+
display: _$alepha.TOptional<_$alepha.TUnsafe<"browser" | "standalone" | "fullscreen" | "minimal-ui">>;
|
|
245
|
+
/**
|
|
246
|
+
* Enable offline support via service worker.
|
|
247
|
+
*
|
|
248
|
+
* TODO: Not yet implemented.
|
|
249
|
+
*/
|
|
250
|
+
offline: _$alepha.TOptional<_$alepha.TBoolean>;
|
|
251
|
+
}>>;
|
|
205
252
|
/**
|
|
206
253
|
* Sitemap generation configuration.
|
|
207
254
|
*/
|
|
@@ -11802,7 +11849,9 @@ declare class ViteUtils {
|
|
|
11802
11849
|
* and injects only the short key into $page definitions.
|
|
11803
11850
|
*/
|
|
11804
11851
|
createSsrPreloadPlugin(): Plugin;
|
|
11805
|
-
generateIndexHtml(entry: AppEntry
|
|
11852
|
+
generateIndexHtml(entry: AppEntry, opts?: {
|
|
11853
|
+
pwa?: boolean;
|
|
11854
|
+
}): string;
|
|
11806
11855
|
/**
|
|
11807
11856
|
* We need to close the Vite dev server after build is done.
|
|
11808
11857
|
*/
|
|
@@ -12352,6 +12401,29 @@ declare class BuildPrerenderTask extends BuildTask {
|
|
|
12352
12401
|
protected renderFile(page: any, options: any, dist: string): Promise<void>;
|
|
12353
12402
|
}
|
|
12354
12403
|
//#endregion
|
|
12404
|
+
//#region ../../src/cli/core/tasks/BuildPwaTask.d.ts
|
|
12405
|
+
/**
|
|
12406
|
+
* Generate PWA web app manifest.
|
|
12407
|
+
*
|
|
12408
|
+
* Produces a `manifest.webmanifest` in the public output directory
|
|
12409
|
+
* from the `pwa` section of build options. Detects icons from `public/`.
|
|
12410
|
+
*/
|
|
12411
|
+
declare class BuildPwaTask extends BuildTask {
|
|
12412
|
+
protected readonly fs: FileSystemProvider;
|
|
12413
|
+
run(ctx: BuildTaskContext): Promise<void>;
|
|
12414
|
+
/**
|
|
12415
|
+
* Detect icon files in the public output directory.
|
|
12416
|
+
*
|
|
12417
|
+
* Looks for common icon filenames and generates
|
|
12418
|
+
* manifest icon entries with appropriate sizes and types.
|
|
12419
|
+
*/
|
|
12420
|
+
protected detectIcons(publicDir: string): Promise<Array<{
|
|
12421
|
+
src: string;
|
|
12422
|
+
sizes: string;
|
|
12423
|
+
type: string;
|
|
12424
|
+
}>>;
|
|
12425
|
+
}
|
|
12426
|
+
//#endregion
|
|
12355
12427
|
//#region ../../src/cli/core/tasks/BuildServerTask.d.ts
|
|
12356
12428
|
/**
|
|
12357
12429
|
* Build server-side SSR bundle with Vite.
|
|
@@ -12510,6 +12582,14 @@ declare class BuildCommand {
|
|
|
12510
12582
|
dist?: string | undefined;
|
|
12511
12583
|
public?: string | undefined;
|
|
12512
12584
|
} | undefined;
|
|
12585
|
+
pwa?: {
|
|
12586
|
+
shortName?: string | undefined;
|
|
12587
|
+
themeColor?: string | undefined;
|
|
12588
|
+
backgroundColor?: string | undefined;
|
|
12589
|
+
display?: "browser" | "standalone" | "fullscreen" | "minimal-ui" | undefined;
|
|
12590
|
+
offline?: boolean | undefined;
|
|
12591
|
+
name: string;
|
|
12592
|
+
} | undefined;
|
|
12513
12593
|
sitemap?: {
|
|
12514
12594
|
hostname: string;
|
|
12515
12595
|
} | undefined;
|
|
@@ -12519,7 +12599,7 @@ declare class BuildCommand {
|
|
|
12519
12599
|
* Each task self-guards (checks target, hasClient, etc.).
|
|
12520
12600
|
* Order matters — compress must be last.
|
|
12521
12601
|
*/
|
|
12522
|
-
protected readonly pipeline: (BuildAssetsTask | BuildClientTask | BuildCloudflareTask | BuildCompressTask | BuildDockerTask | BuildPrerenderTask | BuildServerTask | BuildSitemapTask | BuildStaticTask | BuildVercelTask)[];
|
|
12602
|
+
protected readonly pipeline: (BuildAssetsTask | BuildClientTask | BuildCloudflareTask | BuildCompressTask | BuildDockerTask | BuildPrerenderTask | BuildServerTask | BuildSitemapTask | BuildStaticTask | BuildVercelTask | BuildPwaTask)[];
|
|
12523
12603
|
/**
|
|
12524
12604
|
* Resolve the effective runtime based on target and explicit runtime flag.
|
|
12525
12605
|
*
|