vintasend-api 1.0.0-alpha5 → 1.0.0-alpha7
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/README.md +19 -3
- package/dist/domain/schemas.d.ts +37 -66
- package/dist/exports.d.ts +4 -3
- package/dist/exports.js +4 -3
- package/dist/exports.js.map +1 -1
- package/dist/index.js +18 -3
- package/dist/index.js.map +1 -1
- package/dist/middleware/authenticate.d.ts +8 -0
- package/dist/middleware/authenticate.js +11 -7
- package/dist/middleware/authenticate.js.map +1 -1
- package/dist/routes/validation.d.ts +3 -3
- package/dist/server.d.ts +9 -0
- package/dist/server.js +10 -0
- package/dist/server.js.map +1 -0
- package/package.json +19 -7
- package/dist/middleware/api-key-auth.d.ts +0 -9
- package/dist/middleware/api-key-auth.js +0 -34
- package/dist/middleware/api-key-auth.js.map +0 -1
package/README.md
CHANGED
|
@@ -38,6 +38,19 @@ npm install vintasend-api vintasend
|
|
|
38
38
|
Install the same release line for both: the API is released together with
|
|
39
39
|
`vintasend`, and its version matches.
|
|
40
40
|
|
|
41
|
+
The standalone server also needs `@hono/node-server`. It is an optional peer
|
|
42
|
+
dependency, so a host that mounts `createApp` in its own server does not install
|
|
43
|
+
a Node HTTP server it never starts:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install @hono/node-server # only to run the vintasend-api command
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`vintasend-api` holds `createApp`, the authenticators, `ApiError` and the wire
|
|
50
|
+
types, and loads in a browser. `vintasend-api/server` holds what the standalone
|
|
51
|
+
command is built from: `loadServerConfig` and the service-module loader. It is
|
|
52
|
+
Node only.
|
|
53
|
+
|
|
41
54
|
## Architecture
|
|
42
55
|
|
|
43
56
|
```
|
|
@@ -146,11 +159,14 @@ either package: both recognise an error by its name and code, not by its class.
|
|
|
146
159
|
Throw `ApiError.unauthorized` for a caller with no valid credential and
|
|
147
160
|
`ApiError.forbidden` for one you know and refuse: a 401 would tell a signed-in
|
|
148
161
|
user to sign in again. For one shared secret, pass
|
|
149
|
-
`authenticate: apiKeyAuthenticator(key)`, which compares in constant time.
|
|
162
|
+
`authenticate: apiKeyAuthenticator(key)`, which compares in constant time. To
|
|
163
|
+
check a token yourself, such as the caller's own identity-provider token, read it
|
|
164
|
+
with `bearerToken(c.req.header('authorization'))`, which is `null` when the
|
|
165
|
+
request carries none.
|
|
150
166
|
|
|
151
167
|
The app uses Web APIs only — no Node built-ins — so it runs wherever `fetch`
|
|
152
|
-
does. The standalone server and the module-path service loader
|
|
153
|
-
parts, and
|
|
168
|
+
does. The standalone server and the module-path service loader on `./server` are
|
|
169
|
+
the Node-only parts, and the package entry loads neither.
|
|
154
170
|
|
|
155
171
|
An unexpected error is reported to the client as a generic 500 with an
|
|
156
172
|
`X-Request-Id` header. By default it is logged as one line — the error's name,
|
package/dist/domain/schemas.d.ts
CHANGED
|
@@ -9,82 +9,53 @@ export declare const DEFAULT_PAGE_SIZE = 20;
|
|
|
9
9
|
export declare const MIN_PAGE_SIZE = 1;
|
|
10
10
|
export declare const MAX_PAGE_SIZE = 100;
|
|
11
11
|
export declare const paginationQuerySchema: z.ZodObject<{
|
|
12
|
-
page: z.ZodDefault<z.
|
|
13
|
-
pageSize: z.ZodDefault<z.
|
|
14
|
-
},
|
|
15
|
-
page: number;
|
|
16
|
-
pageSize: number;
|
|
17
|
-
}, {
|
|
18
|
-
page?: number | undefined;
|
|
19
|
-
pageSize?: number | undefined;
|
|
20
|
-
}>;
|
|
12
|
+
page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
13
|
+
pageSize: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
14
|
+
}, z.core.$strip>;
|
|
21
15
|
export declare const notificationListQuerySchema: z.ZodObject<{
|
|
22
|
-
page: z.ZodDefault<z.
|
|
23
|
-
pageSize: z.ZodDefault<z.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
17
|
+
pageSize: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
18
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
19
|
+
PENDING_SEND: "PENDING_SEND";
|
|
20
|
+
SENT: "SENT";
|
|
21
|
+
FAILED: "FAILED";
|
|
22
|
+
READ: "READ";
|
|
23
|
+
CANCELLED: "CANCELLED";
|
|
24
|
+
}>>;
|
|
25
|
+
notificationType: z.ZodOptional<z.ZodEnum<{
|
|
26
|
+
EMAIL: "EMAIL";
|
|
27
|
+
SMS: "SMS";
|
|
28
|
+
PUSH: "PUSH";
|
|
29
|
+
IN_APP: "IN_APP";
|
|
30
|
+
}>>;
|
|
27
31
|
adapterUsed: z.ZodOptional<z.ZodString>;
|
|
28
32
|
userId: z.ZodOptional<z.ZodString>;
|
|
29
33
|
bodyTemplate: z.ZodOptional<z.ZodString>;
|
|
30
34
|
subjectTemplate: z.ZodOptional<z.ZodString>;
|
|
31
35
|
contextName: z.ZodOptional<z.ZodString>;
|
|
32
36
|
tenant: z.ZodOptional<z.ZodString>;
|
|
33
|
-
requestedTemplateVersion: z.ZodOptional<z.
|
|
34
|
-
usedTemplateVersion: z.ZodOptional<z.
|
|
35
|
-
createdAtFrom: z.ZodOptional<z.
|
|
36
|
-
createdAtTo: z.ZodOptional<z.
|
|
37
|
-
sentAtFrom: z.ZodOptional<z.
|
|
38
|
-
sentAtTo: z.ZodOptional<z.
|
|
39
|
-
orderByField: z.ZodOptional<z.ZodEnum<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
tenant?: string | undefined;
|
|
52
|
-
requestedTemplateVersion?: number | undefined;
|
|
53
|
-
usedTemplateVersion?: number | undefined;
|
|
54
|
-
createdAtFrom?: string | undefined;
|
|
55
|
-
createdAtTo?: string | undefined;
|
|
56
|
-
sentAtFrom?: string | undefined;
|
|
57
|
-
sentAtTo?: string | undefined;
|
|
58
|
-
orderByField?: "sendAfter" | "sentAt" | "readAt" | "createdAt" | "updatedAt" | undefined;
|
|
59
|
-
orderByDirection?: "asc" | "desc" | undefined;
|
|
60
|
-
}, {
|
|
61
|
-
page?: number | undefined;
|
|
62
|
-
pageSize?: number | undefined;
|
|
63
|
-
status?: "PENDING_SEND" | "SENT" | "FAILED" | "READ" | "CANCELLED" | undefined;
|
|
64
|
-
notificationType?: "EMAIL" | "SMS" | "PUSH" | "IN_APP" | undefined;
|
|
65
|
-
adapterUsed?: string | undefined;
|
|
66
|
-
userId?: string | undefined;
|
|
67
|
-
bodyTemplate?: string | undefined;
|
|
68
|
-
subjectTemplate?: string | undefined;
|
|
69
|
-
contextName?: string | undefined;
|
|
70
|
-
tenant?: string | undefined;
|
|
71
|
-
requestedTemplateVersion?: number | undefined;
|
|
72
|
-
usedTemplateVersion?: number | undefined;
|
|
73
|
-
createdAtFrom?: string | undefined;
|
|
74
|
-
createdAtTo?: string | undefined;
|
|
75
|
-
sentAtFrom?: string | undefined;
|
|
76
|
-
sentAtTo?: string | undefined;
|
|
77
|
-
orderByField?: "sendAfter" | "sentAt" | "readAt" | "createdAt" | "updatedAt" | undefined;
|
|
78
|
-
orderByDirection?: "asc" | "desc" | undefined;
|
|
79
|
-
}>;
|
|
37
|
+
requestedTemplateVersion: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
38
|
+
usedTemplateVersion: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
39
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
40
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
41
|
+
sentAtFrom: z.ZodOptional<z.ZodString>;
|
|
42
|
+
sentAtTo: z.ZodOptional<z.ZodString>;
|
|
43
|
+
orderByField: z.ZodOptional<z.ZodEnum<{
|
|
44
|
+
sendAfter: "sendAfter";
|
|
45
|
+
sentAt: "sentAt";
|
|
46
|
+
readAt: "readAt";
|
|
47
|
+
createdAt: "createdAt";
|
|
48
|
+
updatedAt: "updatedAt";
|
|
49
|
+
}>>;
|
|
50
|
+
orderByDirection: z.ZodOptional<z.ZodEnum<{
|
|
51
|
+
asc: "asc";
|
|
52
|
+
desc: "desc";
|
|
53
|
+
}>>;
|
|
54
|
+
}, z.core.$strip>;
|
|
80
55
|
/** Body of `POST /notifications/{id}/resend`. Optional: an omitted body regenerates the context. */
|
|
81
56
|
export declare const resendBodySchema: z.ZodObject<{
|
|
82
57
|
useStoredContext: z.ZodDefault<z.ZodBoolean>;
|
|
83
|
-
},
|
|
84
|
-
useStoredContext: boolean;
|
|
85
|
-
}, {
|
|
86
|
-
useStoredContext?: boolean | undefined;
|
|
87
|
-
}>;
|
|
58
|
+
}, z.core.$strip>;
|
|
88
59
|
export type NotificationListQueryInput = z.infer<typeof notificationListQuerySchema>;
|
|
89
60
|
export type PaginationQueryInput = z.infer<typeof paginationQuerySchema>;
|
|
90
61
|
export type ResendBodyInput = z.infer<typeof resendBodySchema>;
|
package/dist/exports.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public entrypoint for mounting the API inside a host's own server, and for sharing the wire
|
|
3
3
|
* contract with TypeScript clients.
|
|
4
|
+
*
|
|
5
|
+
* It imports no Node built-in, so it loads wherever `fetch` does, a browser included. The
|
|
6
|
+
* standalone server's pieces are on `./server`.
|
|
4
7
|
*/
|
|
5
8
|
export { API_BASE_PATH, type AppDependencies, createApp } from './app.js';
|
|
6
|
-
export { loadServerConfig, type ServerConfig } from './config.js';
|
|
7
9
|
export * from './contract/types.js';
|
|
8
10
|
export { ApiError, invalidRequest } from './errors.js';
|
|
9
|
-
export { type Authenticated, type Authenticator, apiKeyAuthenticator, authenticated, } from './middleware/authenticate.js';
|
|
11
|
+
export { type Authenticated, type Authenticator, apiKeyAuthenticator, authenticated, bearerToken, } from './middleware/authenticate.js';
|
|
10
12
|
export { logUnhandledError, REQUEST_ID_HEADER, type UnhandledErrorHandler, } from './middleware/error-handler.js';
|
|
11
13
|
export { createGitHubTemplateClientFromEnv, GitHubTemplateClient, } from './services/github-template-client.js';
|
|
12
14
|
export type { TemplateSourceClient } from './services/notification-preview.js';
|
|
13
15
|
export { asNotificationServicePort, type NotificationServicePort, } from './services/notification-service-port.js';
|
|
14
|
-
export { createServiceProvider, loadNotificationService } from './services/service-loader.js';
|
package/dist/exports.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public entrypoint for mounting the API inside a host's own server, and for sharing the wire
|
|
3
3
|
* contract with TypeScript clients.
|
|
4
|
+
*
|
|
5
|
+
* It imports no Node built-in, so it loads wherever `fetch` does, a browser included. The
|
|
6
|
+
* standalone server's pieces are on `./server`.
|
|
4
7
|
*/
|
|
5
8
|
export { API_BASE_PATH, createApp } from './app.js';
|
|
6
|
-
export { loadServerConfig } from './config.js';
|
|
7
9
|
export * from './contract/types.js';
|
|
8
10
|
export { ApiError, invalidRequest } from './errors.js';
|
|
9
|
-
export { apiKeyAuthenticator, authenticated, } from './middleware/authenticate.js';
|
|
11
|
+
export { apiKeyAuthenticator, authenticated, bearerToken, } from './middleware/authenticate.js';
|
|
10
12
|
export { logUnhandledError, REQUEST_ID_HEADER, } from './middleware/error-handler.js';
|
|
11
13
|
export { createGitHubTemplateClientFromEnv, GitHubTemplateClient, } from './services/github-template-client.js';
|
|
12
14
|
export { asNotificationServicePort, } from './services/notification-service-port.js';
|
|
13
|
-
export { createServiceProvider, loadNotificationService } from './services/service-loader.js';
|
|
14
15
|
//# sourceMappingURL=exports.js.map
|
package/dist/exports.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAwB,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1E,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAGL,mBAAmB,EACnB,aAAa,EACb,WAAW,GACZ,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,iBAAiB,EACjB,iBAAiB,GAElB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,iCAAiC,EACjC,oBAAoB,GACrB,MAAM,sCAAsC,CAAC;AAE9C,OAAO,EACL,yBAAyB,GAE1B,MAAM,yCAAyC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,14 +5,29 @@
|
|
|
5
5
|
* Installed as the package's `vintasend-api` command, for running the API on its own. A host that
|
|
6
6
|
* already has a server mounts `createApp` instead and never loads this file.
|
|
7
7
|
*/
|
|
8
|
-
import { serve } from '@hono/node-server';
|
|
9
8
|
import { createApp } from './app.js';
|
|
10
|
-
import { loadServerConfig } from './config.js';
|
|
11
9
|
import { apiKeyAuthenticator } from './middleware/authenticate.js';
|
|
10
|
+
import { createServiceProvider, loadServerConfig } from './server.js';
|
|
12
11
|
import { createGitHubTemplateClientFromEnv } from './services/github-template-client.js';
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* `@hono/node-server`, which only this command uses. It is an optional peer dependency, so a host
|
|
14
|
+
* that mounts `createApp` in its own server does not install it.
|
|
15
|
+
*/
|
|
16
|
+
async function loadNodeServer() {
|
|
17
|
+
try {
|
|
18
|
+
return await import('@hono/node-server');
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
const { code, message } = error;
|
|
22
|
+
if (code === 'ERR_MODULE_NOT_FOUND' && message?.includes("'@hono/node-server'")) {
|
|
23
|
+
throw new Error('The standalone server needs @hono/node-server: npm install @hono/node-server');
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
14
28
|
async function main() {
|
|
15
29
|
const config = loadServerConfig();
|
|
30
|
+
const { serve } = await loadNodeServer();
|
|
16
31
|
const getService = createServiceProvider(config.serviceModule);
|
|
17
32
|
const app = createApp({
|
|
18
33
|
authenticate: apiKeyAuthenticator(config.apiKey),
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,iCAAiC,EAAE,MAAM,sCAAsC,CAAC;AAEzF;;;GAGG;AACH,KAAK,UAAU,cAAc;IAC3B,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,KAA4C,CAAC;QACvE,IAAI,IAAI,KAAK,sBAAsB,IAAI,OAAO,EAAE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAChF,MAAM,IAAI,KAAK,CACb,8EAA8E,CAC/E,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAClC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,cAAc,EAAE,CAAC;IACzC,MAAM,UAAU,GAAG,qBAAqB,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAE/D,MAAM,GAAG,GAAG,SAAS,CAAC;QACpB,YAAY,EAAE,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC;QAChD,UAAU;QACV,iBAAiB,EAAE,GAAG,EAAE,CAAC,iCAAiC,EAAE;QAC5D,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,MAAM,UAAU,EAAE,CAAC;IAEnB,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;QAC7E,OAAO,CAAC,IAAI,CAAC,uCAAuC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;AACL,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;IACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -27,6 +27,14 @@ export type Authenticator = (c: Context) => Authenticated | Promise<Authenticate
|
|
|
27
27
|
export declare function authenticateWith(authenticate: Authenticator): MiddlewareHandler;
|
|
28
28
|
/** What the authenticator learned about this request's caller. */
|
|
29
29
|
export declare function authenticated(c: Context): Authenticated;
|
|
30
|
+
/**
|
|
31
|
+
* The token in an `Authorization: Bearer <token>` header, or `null` when there is none.
|
|
32
|
+
*
|
|
33
|
+
* The scheme is matched in any case, as RFC 9110 has it. For a host writing its own
|
|
34
|
+
* `Authenticator` around a token it verifies itself, such as the caller's own identity-provider
|
|
35
|
+
* token.
|
|
36
|
+
*/
|
|
37
|
+
export declare function bearerToken(header: string | undefined): string | null;
|
|
30
38
|
/**
|
|
31
39
|
* The shared-secret authenticator: every request must carry `Authorization: Bearer <apiKey>`.
|
|
32
40
|
* It says nothing about who is calling.
|
|
@@ -45,12 +45,16 @@ async function safeEquals(a, b) {
|
|
|
45
45
|
});
|
|
46
46
|
return difference === 0;
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
/**
|
|
49
|
+
* The token in an `Authorization: Bearer <token>` header, or `null` when there is none.
|
|
50
|
+
*
|
|
51
|
+
* The scheme is matched in any case, as RFC 9110 has it. For a host writing its own
|
|
52
|
+
* `Authenticator` around a token it verifies itself, such as the caller's own identity-provider
|
|
53
|
+
* token.
|
|
54
|
+
*/
|
|
55
|
+
export function bearerToken(header) {
|
|
56
|
+
const token = header?.match(/^Bearer\s+(.+)$/i)?.[1]?.trim();
|
|
57
|
+
return token ? token : null;
|
|
54
58
|
}
|
|
55
59
|
/**
|
|
56
60
|
* The shared-secret authenticator: every request must carry `Authorization: Bearer <apiKey>`.
|
|
@@ -58,7 +62,7 @@ function extractToken(header) {
|
|
|
58
62
|
*/
|
|
59
63
|
export function apiKeyAuthenticator(apiKey) {
|
|
60
64
|
return async (c) => {
|
|
61
|
-
const token =
|
|
65
|
+
const token = bearerToken(c.req.header('authorization'));
|
|
62
66
|
if (!token || !(await safeEquals(token, apiKey))) {
|
|
63
67
|
throw ApiError.unauthorized('A valid API key is required.');
|
|
64
68
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticate.js","sourceRoot":"","sources":["../../src/middleware/authenticate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAcxC,MAAM,aAAa,GAAG,kDAAkD,CAAC;AAEzE,gGAAgG;AAChG,MAAM,UAAU,gBAAgB,CAAC,YAA2B;IAC1D,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,CAAC,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,aAAa,CAAC,CAAU;IACtC,OAAQ,CAAC,CAAC,GAAG,CAAC,aAAa,CAA+B,IAAI,EAAE,CAAC;AACnE,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAElC,KAAK,UAAU,MAAM,CAAC,KAAa;IACjC,OAAO,IAAI,UAAU,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACtF,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,UAAU,CAAC,CAAS,EAAE,CAAS;IAC5C,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC3B,UAAU,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,OAAO,UAAU,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"authenticate.js","sourceRoot":"","sources":["../../src/middleware/authenticate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAcxC,MAAM,aAAa,GAAG,kDAAkD,CAAC;AAEzE,gGAAgG;AAChG,MAAM,UAAU,gBAAgB,CAAC,YAA2B;IAC1D,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,CAAC,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,aAAa,CAAC,CAAU;IACtC,OAAQ,CAAC,CAAC,GAAG,CAAC,aAAa,CAA+B,IAAI,EAAE,CAAC;AACnE,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAElC,KAAK,UAAU,MAAM,CAAC,KAAa;IACjC,OAAO,IAAI,UAAU,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACtF,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,UAAU,CAAC,CAAS,EAAE,CAAS;IAC5C,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC3B,UAAU,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,OAAO,UAAU,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,MAA0B;IACpD,MAAM,KAAK,GAAG,MAAM,EAAE,KAAK,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IAC7D,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,OAAO,KAAK,EAAE,CAAC,EAAE,EAAE;QACjB,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;QAEzD,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;YACjD,MAAM,QAAQ,CAAC,YAAY,CAAC,8BAA8B,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -9,6 +9,6 @@ import type { ZodType } from 'zod';
|
|
|
9
9
|
* refused when it is not.
|
|
10
10
|
*/
|
|
11
11
|
export declare function validate<Target extends 'query' | 'json' | 'param', Schema extends ZodType>(target: Target, schema: Schema): import("hono").MiddlewareHandler<import("hono").Env, string, {
|
|
12
|
-
in: (undefined extends import("zod").input<Schema> ? true : false) extends true ? { [K in Target]?: (import("zod").input<Schema> extends
|
|
13
|
-
out: { [K_2 in Target]: import("zod").output<Schema
|
|
14
|
-
}>;
|
|
12
|
+
in: (undefined extends (Schema extends import("zod/v3").ZodType<any, import("zod/v3").ZodTypeDef, any> ? import("zod/v3").input<Schema> : Schema extends import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>> ? import("zod/v4/core").input<Schema> : never) ? true : false) extends true ? { [K in Target]?: ([Schema extends import("zod/v3").ZodType<any, import("zod/v3").ZodTypeDef, any> ? import("zod/v3").input<Schema> : Schema extends import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>> ? import("zod/v4/core").input<Schema> : never] extends [import("hono").ValidationTargets[K]] ? import("hono").ValidationTargets[K] & (Schema extends import("zod/v3").ZodType<any, import("zod/v3").ZodTypeDef, any> ? import("zod/v3").input<Schema> : Schema extends import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>> ? import("zod/v4/core").input<Schema> : never) : import("hono/validator").InferInput<Schema extends import("zod/v3").ZodType<any, import("zod/v3").ZodTypeDef, any> ? import("zod/v3").input<Schema> : Schema extends import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>> ? import("zod/v4/core").input<Schema> : never, K, import("hono/types").ParsedFormValue>) | undefined; } : { [K_1 in Target]: [Schema extends import("zod/v3").ZodType<any, import("zod/v3").ZodTypeDef, any> ? import("zod/v3").input<Schema> : Schema extends import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>> ? import("zod/v4/core").input<Schema> : never] extends [import("hono").ValidationTargets[K_1]] ? import("hono").ValidationTargets[K_1] & (Schema extends import("zod/v3").ZodType<any, import("zod/v3").ZodTypeDef, any> ? import("zod/v3").input<Schema> : Schema extends import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>> ? import("zod/v4/core").input<Schema> : never) : import("hono/validator").InferInput<Schema extends import("zod/v3").ZodType<any, import("zod/v3").ZodTypeDef, any> ? import("zod/v3").input<Schema> : Schema extends import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>> ? import("zod/v4/core").input<Schema> : never, K_1, import("hono/types").ParsedFormValue>; };
|
|
13
|
+
out: { [K_2 in Target]: Schema extends import("zod/v3").ZodType<any, import("zod/v3").ZodTypeDef, any> ? import("zod/v3").output<Schema> : Schema extends import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>> ? import("zod").infer<Schema> : never; };
|
|
14
|
+
}, never>;
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the standalone server is built from: its environment configuration and the loader for the
|
|
3
|
+
* service module it names.
|
|
4
|
+
*
|
|
5
|
+
* Node only, and of no use to a host that mounts `createApp` itself, so it is not on the package
|
|
6
|
+
* entry: that one loads in a browser.
|
|
7
|
+
*/
|
|
8
|
+
export { loadServerConfig, type ServerConfig } from './config.js';
|
|
9
|
+
export { createServiceProvider, loadNotificationService, type VintaSendServiceFactory, } from './services/service-loader.js';
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the standalone server is built from: its environment configuration and the loader for the
|
|
3
|
+
* service module it names.
|
|
4
|
+
*
|
|
5
|
+
* Node only, and of no use to a host that mounts `createApp` itself, so it is not on the package
|
|
6
|
+
* entry: that one loads in a browser.
|
|
7
|
+
*/
|
|
8
|
+
export { loadServerConfig } from './config.js';
|
|
9
|
+
export { createServiceProvider, loadNotificationService, } from './services/service-loader.js';
|
|
10
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,gBAAgB,EAAqB,MAAM,aAAa,CAAC;AAClE,OAAO,EACL,qBAAqB,EACrB,uBAAuB,GAExB,MAAM,8BAA8B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vintasend-api",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-alpha7",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/vintasoftware/vintasend-ts-api.git"
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"types": "./dist/exports.d.ts",
|
|
17
17
|
"import": "./dist/exports.js"
|
|
18
18
|
},
|
|
19
|
+
"./server": {
|
|
20
|
+
"types": "./dist/server.d.ts",
|
|
21
|
+
"import": "./dist/server.js"
|
|
22
|
+
},
|
|
19
23
|
"./openapi.yaml": "./openapi.yaml",
|
|
20
24
|
"./package.json": "./package.json"
|
|
21
25
|
},
|
|
@@ -28,6 +32,7 @@
|
|
|
28
32
|
"README.md",
|
|
29
33
|
"LICENSE"
|
|
30
34
|
],
|
|
35
|
+
"sideEffects": false,
|
|
31
36
|
"scripts": {
|
|
32
37
|
"dev": "tsx watch src/index.ts",
|
|
33
38
|
"build": "tsc",
|
|
@@ -39,20 +44,27 @@
|
|
|
39
44
|
"format": "biome check --write ."
|
|
40
45
|
},
|
|
41
46
|
"dependencies": {
|
|
42
|
-
"@hono/
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"zod": "^3.24.2"
|
|
47
|
+
"@hono/zod-validator": "^0.9.1",
|
|
48
|
+
"hono": "^4.11.2",
|
|
49
|
+
"zod": "^4.6.5"
|
|
46
50
|
},
|
|
47
51
|
"peerDependencies": {
|
|
48
|
-
"
|
|
52
|
+
"@hono/node-server": "^1.14.0",
|
|
53
|
+
"vintasend": "^1.0.0-alpha7"
|
|
54
|
+
},
|
|
55
|
+
"peerDependenciesMeta": {
|
|
56
|
+
"@hono/node-server": {
|
|
57
|
+
"optional": true
|
|
58
|
+
}
|
|
49
59
|
},
|
|
50
60
|
"devDependencies": {
|
|
51
61
|
"@biomejs/biome": "^2.5.10",
|
|
62
|
+
"@hono/node-server": "^1.14.0",
|
|
52
63
|
"@types/node": "^25.0.8",
|
|
64
|
+
"esbuild": "^0.28.2",
|
|
53
65
|
"tsx": "^4.19.3",
|
|
54
66
|
"typescript": "^5.9.3",
|
|
55
|
-
"vintasend": "1.0.0-
|
|
67
|
+
"vintasend": "1.0.0-alpha7",
|
|
56
68
|
"vitest": "^4.0.18"
|
|
57
69
|
},
|
|
58
70
|
"engines": {
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared-secret authentication.
|
|
3
|
-
*
|
|
4
|
-
* Every `/api/v1` request must carry `Authorization: Bearer <VINTASEND_API_KEY>`.
|
|
5
|
-
* The dashboard calls the API from its server side only, so the key never
|
|
6
|
-
* reaches a browser.
|
|
7
|
-
*/
|
|
8
|
-
import type { MiddlewareHandler } from 'hono';
|
|
9
|
-
export declare function apiKeyAuth(apiKey: string): MiddlewareHandler;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared-secret authentication.
|
|
3
|
-
*
|
|
4
|
-
* Every `/api/v1` request must carry `Authorization: Bearer <VINTASEND_API_KEY>`.
|
|
5
|
-
* The dashboard calls the API from its server side only, so the key never
|
|
6
|
-
* reaches a browser.
|
|
7
|
-
*/
|
|
8
|
-
import { timingSafeEqual } from 'node:crypto';
|
|
9
|
-
import { ApiError } from '../errors.js';
|
|
10
|
-
function safeEquals(a, b) {
|
|
11
|
-
const left = Buffer.from(a);
|
|
12
|
-
const right = Buffer.from(b);
|
|
13
|
-
if (left.length !== right.length) {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
return timingSafeEqual(left, right);
|
|
17
|
-
}
|
|
18
|
-
function extractToken(header) {
|
|
19
|
-
if (!header) {
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
const match = header.match(/^Bearer\s+(.+)$/i);
|
|
23
|
-
return match?.[1]?.trim();
|
|
24
|
-
}
|
|
25
|
-
export function apiKeyAuth(apiKey) {
|
|
26
|
-
return async (c, next) => {
|
|
27
|
-
const token = extractToken(c.req.header('authorization'));
|
|
28
|
-
if (!token || !safeEquals(token, apiKey)) {
|
|
29
|
-
throw new ApiError('UNAUTHORIZED', 'A valid API key is required.');
|
|
30
|
-
}
|
|
31
|
-
await next();
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
//# sourceMappingURL=api-key-auth.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api-key-auth.js","sourceRoot":"","sources":["../../src/middleware/api-key-auth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,SAAS,UAAU,CAAC,CAAS,EAAE,CAAS;IACtC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE7B,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,YAAY,CAAC,MAA0B;IAC9C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC/C,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;QAE1D,IAAI,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,8BAA8B,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
|