@pikku/core 0.6.26 → 0.7.0
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/CHANGELOG.md +17 -1
- package/dist/channel/channel-handler.js +16 -38
- package/dist/channel/channel-runner.d.ts +7 -8
- package/dist/channel/channel-runner.js +58 -7
- package/dist/channel/channel.types.d.ts +14 -27
- package/dist/channel/local/local-channel-runner.js +9 -4
- package/dist/channel/log-channels.js +4 -4
- package/dist/channel/serverless/serverless-channel-runner.js +17 -6
- package/dist/function/function-runner.d.ts +12 -0
- package/dist/function/function-runner.js +38 -0
- package/dist/function/functions.types.d.ts +37 -0
- package/dist/function/functions.types.js +1 -0
- package/dist/function/index.d.ts +2 -0
- package/dist/function/index.js +2 -0
- package/dist/handle-error.d.ts +1 -1
- package/dist/http/http-route-runner.d.ts +3 -3
- package/dist/http/http-route-runner.js +60 -35
- package/dist/http/http-routes.types.d.ts +38 -7
- package/dist/http/http-runner.d.ts +90 -0
- package/dist/http/http-runner.js +336 -0
- package/dist/http/http.types.d.ts +177 -0
- package/dist/http/http.types.js +1 -0
- package/dist/http/index.d.ts +2 -2
- package/dist/http/index.js +1 -1
- package/dist/http/log-http-routes.js +7 -5
- package/dist/http/pikku-fetch-http-request.d.ts +1 -1
- package/dist/http/pikku-fetch-http-response.d.ts +4 -2
- package/dist/http/pikku-fetch-http-response.js +64 -12
- package/dist/http/pikku-http-request.d.ts +56 -0
- package/dist/http/pikku-http-request.js +95 -0
- package/dist/http/pikku-http-response.d.ts +13 -0
- package/dist/http/pikku-http-response.js +60 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/middleware/auth-apikey.js +5 -4
- package/dist/middleware/auth-bearer.js +5 -4
- package/dist/middleware/auth-cookie.js +10 -9
- package/dist/parse-relative-time-offset.d.ts +12 -0
- package/dist/parse-relative-time-offset.js +20 -0
- package/dist/permissions.d.ts +2 -2
- package/dist/pikku-func.d.ts +12 -0
- package/dist/pikku-func.js +39 -0
- package/dist/pikku-function.d.ts +1 -0
- package/dist/pikku-function.js +1 -0
- package/dist/pikku-state.d.ts +10 -5
- package/dist/pikku-state.js +7 -3
- package/dist/scheduler/scheduler-runner.d.ts +1 -1
- package/dist/scheduler/scheduler-runner.js +24 -6
- package/dist/scheduler/scheduler.types.d.ts +3 -2
- package/dist/services/content-service.d.ts +34 -36
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/local-content.d.ts +4 -2
- package/dist/services/local-content.js +19 -9
- package/dist/types/core.types.d.ts +15 -1
- package/dist/types/functions.types.d.ts +11 -6
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +15 -0
- package/package.json +2 -1
- package/lcov.info +0 -4310
- package/src/channel/channel-handler.ts +0 -227
- package/src/channel/channel-runner.ts +0 -98
- package/src/channel/channel-store.ts +0 -29
- package/src/channel/channel.types.ts +0 -171
- package/src/channel/eventhub-service.ts +0 -30
- package/src/channel/eventhub-store.ts +0 -8
- package/src/channel/index.ts +0 -8
- package/src/channel/local/index.ts +0 -3
- package/src/channel/local/local-channel-handler.ts +0 -50
- package/src/channel/local/local-channel-runner.test.ts +0 -158
- package/src/channel/local/local-channel-runner.ts +0 -126
- package/src/channel/local/local-eventhub-service.test.ts +0 -95
- package/src/channel/local/local-eventhub-service.ts +0 -95
- package/src/channel/log-channels.ts +0 -20
- package/src/channel/pikku-abstract-channel-handler.test.ts +0 -52
- package/src/channel/pikku-abstract-channel-handler.ts +0 -38
- package/src/channel/serverless/index.ts +0 -1
- package/src/channel/serverless/serverless-channel-runner.ts +0 -209
- package/src/errors/error-handler.ts +0 -61
- package/src/errors/error.test.ts +0 -54
- package/src/errors/errors.ts +0 -339
- package/src/errors/index.ts +0 -2
- package/src/handle-error.ts +0 -73
- package/src/http/http-route-runner.test.ts +0 -117
- package/src/http/http-route-runner.ts +0 -460
- package/src/http/http-routes.types.ts +0 -211
- package/src/http/incomingmessage-to-request-convertor.ts +0 -0
- package/src/http/index.ts +0 -8
- package/src/http/log-http-routes.ts +0 -20
- package/src/http/pikku-fetch-http-request.test.ts +0 -237
- package/src/http/pikku-fetch-http-request.ts +0 -167
- package/src/http/pikku-fetch-http-response.test.ts +0 -82
- package/src/http/pikku-fetch-http-response.ts +0 -84
- package/src/index.ts +0 -19
- package/src/middleware/auth-apikey.ts +0 -65
- package/src/middleware/auth-bearer.ts +0 -65
- package/src/middleware/auth-cookie.ts +0 -98
- package/src/middleware/index.ts +0 -3
- package/src/middleware/timeout.ts +0 -13
- package/src/middleware-runner.ts +0 -43
- package/src/permissions.test.ts +0 -58
- package/src/permissions.ts +0 -45
- package/src/pikku-request.ts +0 -23
- package/src/pikku-response.ts +0 -5
- package/src/pikku-state.ts +0 -71
- package/src/scheduler/index.ts +0 -5
- package/src/scheduler/log-schedulers.ts +0 -20
- package/src/scheduler/scheduler-runner.ts +0 -87
- package/src/scheduler/scheduler.types.ts +0 -29
- package/src/schema.test.ts +0 -57
- package/src/schema.ts +0 -99
- package/src/services/content-service.ts +0 -69
- package/src/services/index.ts +0 -17
- package/src/services/jwt-service.ts +0 -30
- package/src/services/local-content.ts +0 -91
- package/src/services/local-secrets.ts +0 -43
- package/src/services/local-variables.ts +0 -17
- package/src/services/logger-console.ts +0 -97
- package/src/services/logger.ts +0 -57
- package/src/services/schema-service.ts +0 -26
- package/src/services/secret-service.ts +0 -17
- package/src/services/user-session-service.ts +0 -53
- package/src/services/variables-service.ts +0 -6
- package/src/time-utils.test.ts +0 -56
- package/src/time-utils.ts +0 -32
- package/src/types/core.types.ts +0 -159
- package/src/types/functions.types.ts +0 -63
- package/src/utils.ts +0 -21
- package/tsconfig.json +0 -13
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -4,6 +4,15 @@ export class PikkuFetchHTTPResponse {
|
|
|
4
4
|
#headers = new Headers();
|
|
5
5
|
#cookies = new Map();
|
|
6
6
|
#body = null;
|
|
7
|
+
#responseMode = null;
|
|
8
|
+
#send = null;
|
|
9
|
+
#close = null;
|
|
10
|
+
setMode(mode) {
|
|
11
|
+
this.#responseMode = 'stream';
|
|
12
|
+
if (mode === 'stream') {
|
|
13
|
+
this.#body = this.createStream();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
7
16
|
status(code) {
|
|
8
17
|
this.#statusCode = code;
|
|
9
18
|
return this;
|
|
@@ -23,34 +32,60 @@ export class PikkuFetchHTTPResponse {
|
|
|
23
32
|
return this;
|
|
24
33
|
}
|
|
25
34
|
arrayBuffer(data) {
|
|
26
|
-
this.#
|
|
27
|
-
|
|
35
|
+
if (this.#responseMode === 'stream') {
|
|
36
|
+
this.#send(data);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.#body = data;
|
|
40
|
+
this.header('Content-Type', 'application/octet-stream');
|
|
41
|
+
}
|
|
28
42
|
return this;
|
|
29
43
|
}
|
|
30
44
|
json(data) {
|
|
31
|
-
this.#
|
|
32
|
-
|
|
45
|
+
if (this.#responseMode === 'stream') {
|
|
46
|
+
this.#send(JSON.stringify(data));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.#body = JSON.stringify(data);
|
|
50
|
+
this.header('Content-Type', 'application/json');
|
|
51
|
+
}
|
|
33
52
|
return this;
|
|
34
53
|
}
|
|
35
54
|
text(content) {
|
|
36
|
-
this.#
|
|
37
|
-
|
|
55
|
+
if (this.#responseMode === 'stream') {
|
|
56
|
+
this.#send(content);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
this.#body = content;
|
|
60
|
+
this.header('Content-Type', 'text/plain');
|
|
61
|
+
}
|
|
38
62
|
return this;
|
|
39
63
|
}
|
|
40
64
|
html(content) {
|
|
41
|
-
this.#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
65
|
+
if (this.#responseMode === 'stream') {
|
|
66
|
+
this.#send(content);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.#body = content;
|
|
70
|
+
this.header('Content-Type', 'text/html');
|
|
71
|
+
}
|
|
47
72
|
return this;
|
|
48
73
|
}
|
|
74
|
+
// public body(body: BodyInit): this {
|
|
75
|
+
// this.#body = body
|
|
76
|
+
// return this
|
|
77
|
+
// }
|
|
49
78
|
redirect(location, status = 302) {
|
|
50
79
|
this.#statusCode = status;
|
|
51
80
|
this.header('Location', location);
|
|
52
81
|
return this;
|
|
53
82
|
}
|
|
83
|
+
close() {
|
|
84
|
+
if (this.#close) {
|
|
85
|
+
this.#close();
|
|
86
|
+
}
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
54
89
|
toResponse(args) {
|
|
55
90
|
const cookieHeader = Array.from(this.#cookies.entries()).map(([name, { value, flags }]) => serializeCookie(name, value, flags));
|
|
56
91
|
this.#headers.set('Set-Cookie', cookieHeader.join(', '));
|
|
@@ -60,4 +95,21 @@ export class PikkuFetchHTTPResponse {
|
|
|
60
95
|
headers: this.#headers,
|
|
61
96
|
});
|
|
62
97
|
}
|
|
98
|
+
createStream() {
|
|
99
|
+
const encoder = new TextEncoder();
|
|
100
|
+
return new ReadableStream({
|
|
101
|
+
start: (controller) => {
|
|
102
|
+
const send = (data) => {
|
|
103
|
+
controller.enqueue(encoder.encode(`data: ${data}\n\n`));
|
|
104
|
+
};
|
|
105
|
+
const close = () => {
|
|
106
|
+
controller.close();
|
|
107
|
+
};
|
|
108
|
+
this.#send = send;
|
|
109
|
+
this.#close = close;
|
|
110
|
+
// Force initial flush
|
|
111
|
+
controller.enqueue(encoder.encode(':\n\n'));
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
63
115
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { PikkuRequest } from '../pikku-request.js';
|
|
2
|
+
import { HTTPMethod, PikkuQuery } from './http-routes.types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Abstract class representing a pikku request.
|
|
5
|
+
* @template In - The type of the request body.
|
|
6
|
+
* @group RequestResponse
|
|
7
|
+
*/
|
|
8
|
+
export declare class PikkuHTTPRequest<In = unknown> extends PikkuRequest<In> {
|
|
9
|
+
private request;
|
|
10
|
+
private _params;
|
|
11
|
+
private url;
|
|
12
|
+
constructor(request: Request);
|
|
13
|
+
method(): HTTPMethod;
|
|
14
|
+
path(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Retrieves the request body.
|
|
17
|
+
* @returns A promise that resolves to the request body.
|
|
18
|
+
*/
|
|
19
|
+
json(): Promise<In>;
|
|
20
|
+
/**
|
|
21
|
+
* Retrieves the raw request body as a Buffer.
|
|
22
|
+
* @returns A promise that resolves to the raw request body.
|
|
23
|
+
*/
|
|
24
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
25
|
+
/**
|
|
26
|
+
* Retrieves the value of a specific header.
|
|
27
|
+
* @param headerName - The name of the header to retrieve.
|
|
28
|
+
* @returns The value of the header, or undefined if the header is not found.
|
|
29
|
+
*/
|
|
30
|
+
header(headerName: string): string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Retrieves the cookies from the request.
|
|
33
|
+
* @returns An object containing the cookies.
|
|
34
|
+
*/
|
|
35
|
+
cookies(): Partial<Record<string, string>>;
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves the request parameters.
|
|
38
|
+
* @returns An object containing the request parameters.
|
|
39
|
+
*/
|
|
40
|
+
params(): Partial<Record<string, string | string[]>>;
|
|
41
|
+
/**
|
|
42
|
+
* Sets the request parameters.
|
|
43
|
+
* @param params - An object containing the request parameters to set.
|
|
44
|
+
*/
|
|
45
|
+
setParams(params: Record<string, string | string[] | undefined>): void;
|
|
46
|
+
/**
|
|
47
|
+
* Retrieves the query parameters from the request.
|
|
48
|
+
* @returns An object containing the query parameters.
|
|
49
|
+
*/
|
|
50
|
+
query(): PikkuQuery;
|
|
51
|
+
/**
|
|
52
|
+
* Retrieves the combined data from the request, including parameters, query, and body.
|
|
53
|
+
* @returns A promise that resolves to an object containing the combined data.
|
|
54
|
+
*/
|
|
55
|
+
getData(): Promise<In>;
|
|
56
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { parse as parseQuery } from 'picoquery';
|
|
2
|
+
import { parse as parseCookie } from 'cookie';
|
|
3
|
+
import { PikkuRequest } from '../pikku-request.js';
|
|
4
|
+
/**
|
|
5
|
+
* Abstract class representing a pikku request.
|
|
6
|
+
* @template In - The type of the request body.
|
|
7
|
+
* @group RequestResponse
|
|
8
|
+
*/
|
|
9
|
+
export class PikkuHTTPRequest extends PikkuRequest {
|
|
10
|
+
request;
|
|
11
|
+
_params = {};
|
|
12
|
+
url;
|
|
13
|
+
constructor(request) {
|
|
14
|
+
super();
|
|
15
|
+
this.request = request;
|
|
16
|
+
this.url = new URL(request.url);
|
|
17
|
+
}
|
|
18
|
+
method() {
|
|
19
|
+
return this.request.method.toLowerCase();
|
|
20
|
+
}
|
|
21
|
+
path() {
|
|
22
|
+
return this.url.pathname;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Retrieves the request body.
|
|
26
|
+
* @returns A promise that resolves to the request body.
|
|
27
|
+
*/
|
|
28
|
+
json() {
|
|
29
|
+
return this.request.json();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Retrieves the raw request body as a Buffer.
|
|
33
|
+
* @returns A promise that resolves to the raw request body.
|
|
34
|
+
*/
|
|
35
|
+
arrayBuffer() {
|
|
36
|
+
return this.request.arrayBuffer();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Retrieves the value of a specific header.
|
|
40
|
+
* @param headerName - The name of the header to retrieve.
|
|
41
|
+
* @returns The value of the header, or undefined if the header is not found.
|
|
42
|
+
*/
|
|
43
|
+
header(headerName) {
|
|
44
|
+
return this.request.headers.get(headerName.toLowerCase());
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Retrieves the cookies from the request.
|
|
48
|
+
* @returns An object containing the cookies.
|
|
49
|
+
*/
|
|
50
|
+
cookies() {
|
|
51
|
+
const cookieHeader = this.header('cookie');
|
|
52
|
+
if (cookieHeader) {
|
|
53
|
+
return parseCookie(cookieHeader);
|
|
54
|
+
}
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Retrieves the request parameters.
|
|
59
|
+
* @returns An object containing the request parameters.
|
|
60
|
+
*/
|
|
61
|
+
params() {
|
|
62
|
+
return this._params;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Sets the request parameters.
|
|
66
|
+
* @param params - An object containing the request parameters to set.
|
|
67
|
+
*/
|
|
68
|
+
setParams(params) {
|
|
69
|
+
this._params = params;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Retrieves the query parameters from the request.
|
|
73
|
+
* @returns An object containing the query parameters.
|
|
74
|
+
*/
|
|
75
|
+
query() {
|
|
76
|
+
return parseQuery(this.url.searchParams.toString());
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Retrieves the combined data from the request, including parameters, query, and body.
|
|
80
|
+
* @returns A promise that resolves to an object containing the combined data.
|
|
81
|
+
*/
|
|
82
|
+
async getData() {
|
|
83
|
+
let body = {};
|
|
84
|
+
try {
|
|
85
|
+
body = await this.json();
|
|
86
|
+
}
|
|
87
|
+
catch (e) { }
|
|
88
|
+
return {
|
|
89
|
+
...this.params(),
|
|
90
|
+
...this.query(),
|
|
91
|
+
// TODO: If body isn't an object, we should insert it as the word...
|
|
92
|
+
...body,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class PikkuHTTPResponse {
|
|
2
|
+
#private;
|
|
3
|
+
status(code: number): this;
|
|
4
|
+
cookie(name: string, value: string, flags: any): this;
|
|
5
|
+
header(name: string, value: string | string[]): this;
|
|
6
|
+
arrayBuffer(data: XMLHttpRequestBodyInit): this;
|
|
7
|
+
json(data: unknown): this;
|
|
8
|
+
text(content: string): this;
|
|
9
|
+
html(content: string): this;
|
|
10
|
+
body(body: BodyInit): this;
|
|
11
|
+
redirect(location: string, status?: number): this;
|
|
12
|
+
toResponse(args?: Record<string, any>): Response;
|
|
13
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export class PikkuHTTPResponse {
|
|
2
|
+
#statusCode = 200;
|
|
3
|
+
#headers = new Headers();
|
|
4
|
+
#body = null;
|
|
5
|
+
status(code) {
|
|
6
|
+
this.#statusCode = code;
|
|
7
|
+
return this;
|
|
8
|
+
}
|
|
9
|
+
cookie(name, value, flags) {
|
|
10
|
+
// TODO
|
|
11
|
+
return this;
|
|
12
|
+
}
|
|
13
|
+
header(name, value) {
|
|
14
|
+
this.#headers.delete(name);
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
value.forEach((v) => this.#headers.append(name, v));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
this.#headers.set(name, value);
|
|
20
|
+
}
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
arrayBuffer(data) {
|
|
24
|
+
this.#body = data;
|
|
25
|
+
this.header('Content-Type', 'application/octet-stream');
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
json(data) {
|
|
29
|
+
this.#body = JSON.stringify(data);
|
|
30
|
+
this.header('Content-Type', 'application/json');
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
text(content) {
|
|
34
|
+
this.#body = content;
|
|
35
|
+
this.header('Content-Type', 'text/plain');
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
html(content) {
|
|
39
|
+
this.#body = content;
|
|
40
|
+
this.header('Content-Type', 'text/html');
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
body(body) {
|
|
44
|
+
this.#body = body;
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
redirect(location, status = 302) {
|
|
48
|
+
this.#statusCode = status;
|
|
49
|
+
this.header('Location', location);
|
|
50
|
+
this.html(`Redirecting to <a href="${location}">${location}</a>`);
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
toResponse(args) {
|
|
54
|
+
return new Response(this.#body, {
|
|
55
|
+
...args,
|
|
56
|
+
status: this.#statusCode,
|
|
57
|
+
headers: this.#headers,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module @pikku/core
|
|
3
3
|
*/
|
|
4
4
|
export * from './types/core.types.js';
|
|
5
|
-
export * from './
|
|
5
|
+
export * from './function/index.js';
|
|
6
6
|
export * from './pikku-request.js';
|
|
7
7
|
export * from './pikku-response.js';
|
|
8
8
|
export * from './services/index.js';
|
|
@@ -12,8 +12,9 @@ export * from './scheduler/index.js';
|
|
|
12
12
|
export * from './errors/index.js';
|
|
13
13
|
export * from './middleware/index.js';
|
|
14
14
|
export * from './time-utils.js';
|
|
15
|
+
export * from './utils.js';
|
|
15
16
|
export { pikkuState } from './pikku-state.js';
|
|
16
17
|
export { runMiddleware } from './middleware-runner.js';
|
|
17
|
-
export {
|
|
18
|
+
export { addHTTPRoute, addMiddleware } from './http/http-runner.js';
|
|
18
19
|
export { addChannel } from './channel/channel-runner.js';
|
|
19
20
|
export { addScheduledTask } from './scheduler/scheduler-runner.js';
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module @pikku/core
|
|
3
3
|
*/
|
|
4
4
|
export * from './types/core.types.js';
|
|
5
|
-
export * from './
|
|
5
|
+
export * from './function/index.js';
|
|
6
6
|
export * from './pikku-request.js';
|
|
7
7
|
export * from './pikku-response.js';
|
|
8
8
|
export * from './services/index.js';
|
|
@@ -12,8 +12,9 @@ export * from './scheduler/index.js';
|
|
|
12
12
|
export * from './errors/index.js';
|
|
13
13
|
export * from './middleware/index.js';
|
|
14
14
|
export * from './time-utils.js';
|
|
15
|
+
export * from './utils.js';
|
|
15
16
|
export { pikkuState } from './pikku-state.js';
|
|
16
17
|
export { runMiddleware } from './middleware-runner.js';
|
|
17
|
-
export {
|
|
18
|
+
export { addHTTPRoute, addMiddleware } from './http/http-runner.js';
|
|
18
19
|
export { addChannel } from './channel/channel-runner.js';
|
|
19
20
|
export { addScheduledTask } from './scheduler/scheduler-runner.js';
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export const authAPIKey = ({ source, getSessionForAPIKey, jwt, }) => {
|
|
7
7
|
const middleware = async (services, { http }, next) => {
|
|
8
|
-
|
|
8
|
+
const { userSession: userSessionService, jwt: jwtService } = services;
|
|
9
|
+
if (!http?.request || userSessionService.get()) {
|
|
9
10
|
return next();
|
|
10
11
|
}
|
|
11
12
|
let apiKey = null;
|
|
@@ -18,16 +19,16 @@ export const authAPIKey = ({ source, getSessionForAPIKey, jwt, }) => {
|
|
|
18
19
|
if (apiKey) {
|
|
19
20
|
let userSession = null;
|
|
20
21
|
if (jwt) {
|
|
21
|
-
if (!
|
|
22
|
+
if (!jwtService) {
|
|
22
23
|
throw new Error('JWT service is required for JWT decoding.');
|
|
23
24
|
}
|
|
24
|
-
userSession = await
|
|
25
|
+
userSession = await jwtService.decode(apiKey);
|
|
25
26
|
}
|
|
26
27
|
else {
|
|
27
28
|
userSession = await getSessionForAPIKey(services, apiKey);
|
|
28
29
|
}
|
|
29
30
|
if (userSession) {
|
|
30
|
-
|
|
31
|
+
userSessionService.setInitial(userSession);
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
return next();
|
|
@@ -4,8 +4,9 @@ import { InvalidSessionError } from '../errors/errors.js';
|
|
|
4
4
|
*/
|
|
5
5
|
export const authBearer = ({ token, jwt, getSession, } = {}) => {
|
|
6
6
|
const middleware = async (services, { http }, next) => {
|
|
7
|
+
const { userSession: userSessionService, jwt: jwtService } = services;
|
|
7
8
|
// Skip if session already exists.
|
|
8
|
-
if (!http?.request ||
|
|
9
|
+
if (!http?.request || userSessionService.get()) {
|
|
9
10
|
return next();
|
|
10
11
|
}
|
|
11
12
|
const authHeader = http.request.header('authorization') ||
|
|
@@ -17,10 +18,10 @@ export const authBearer = ({ token, jwt, getSession, } = {}) => {
|
|
|
17
18
|
}
|
|
18
19
|
let userSession = null;
|
|
19
20
|
if (jwt) {
|
|
20
|
-
if (!
|
|
21
|
+
if (!jwtService) {
|
|
21
22
|
throw new Error('JWT service is required for JWT decoding.');
|
|
22
23
|
}
|
|
23
|
-
userSession = await
|
|
24
|
+
userSession = await jwtService.decode(bearerToken);
|
|
24
25
|
}
|
|
25
26
|
else if (token) {
|
|
26
27
|
if (bearerToken === token.value) {
|
|
@@ -31,7 +32,7 @@ export const authBearer = ({ token, jwt, getSession, } = {}) => {
|
|
|
31
32
|
userSession = await getSession(services, token);
|
|
32
33
|
}
|
|
33
34
|
if (userSession) {
|
|
34
|
-
|
|
35
|
+
userSessionService.setInitial(userSession);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
return next();
|
|
@@ -7,43 +7,44 @@ import { getRelativeTimeOffsetFromNow, } from '../time-utils.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export const authCookie = ({ name, getSessionForCookieValue, jwt, options, expiresIn, }) => {
|
|
9
9
|
const middleware = async (services, { http }, next) => {
|
|
10
|
-
|
|
10
|
+
const { userSession: userSessionService, jwt: jwtService, logger, } = services;
|
|
11
|
+
if (!http?.request || userSessionService.get()) {
|
|
11
12
|
return next();
|
|
12
13
|
}
|
|
13
14
|
let userSession = null;
|
|
14
15
|
const cookieValue = http.request.cookie(name);
|
|
15
16
|
if (cookieValue) {
|
|
16
17
|
if (jwt) {
|
|
17
|
-
if (!
|
|
18
|
+
if (!jwtService) {
|
|
18
19
|
throw new Error('JWT service is required for JWT decoding.');
|
|
19
20
|
}
|
|
20
|
-
userSession = await
|
|
21
|
+
userSession = await jwtService.decode(cookieValue);
|
|
21
22
|
}
|
|
22
23
|
else if (getSessionForCookieValue) {
|
|
23
24
|
userSession = await getSessionForCookieValue(services, cookieValue, name);
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
if (userSession) {
|
|
27
|
-
|
|
28
|
+
userSessionService.setInitial(userSession);
|
|
28
29
|
}
|
|
29
30
|
await next();
|
|
30
31
|
// Set the cookie in the response if the session has changed
|
|
31
32
|
if (!http?.response) {
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
34
|
-
if (
|
|
35
|
-
const session =
|
|
35
|
+
if (userSessionService.sessionChanged) {
|
|
36
|
+
const session = userSessionService.get();
|
|
36
37
|
if (jwt) {
|
|
37
|
-
if (!
|
|
38
|
+
if (!jwtService) {
|
|
38
39
|
throw new Error('JWT service is required for JWT encoding.');
|
|
39
40
|
}
|
|
40
|
-
http.response.cookie(name, await
|
|
41
|
+
http.response.cookie(name, await jwtService.encode(expiresIn, session), {
|
|
41
42
|
...options,
|
|
42
43
|
expires: getRelativeTimeOffsetFromNow(expiresIn),
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
46
|
else {
|
|
46
|
-
|
|
47
|
+
logger.warn('No JWT service available, unable to set cookie');
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type TimeUnit = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'year';
|
|
2
|
+
export interface RelativeTimeInput {
|
|
3
|
+
value: number;
|
|
4
|
+
unit: TimeUnit;
|
|
5
|
+
}
|
|
6
|
+
export declare const getRelativeTimeOffset: ({ value, unit }: RelativeTimeInput) => number;
|
|
7
|
+
/**
|
|
8
|
+
* Returns a Unix timestamp (in seconds) offset from now by the given duration.
|
|
9
|
+
* e.g. value = -1 and unit = 'day' => 1 day ago => now - 86400 seconds
|
|
10
|
+
*/
|
|
11
|
+
export declare const getRelativeTimeOffsetFromNow: (relativeTime: RelativeTimeInput) => Date;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const getRelativeTimeOffset = ({ value, unit }) => {
|
|
2
|
+
const multipliers = {
|
|
3
|
+
second: 1,
|
|
4
|
+
minute: 60,
|
|
5
|
+
hour: 60 * 60,
|
|
6
|
+
day: 60 * 60 * 24,
|
|
7
|
+
week: 60 * 60 * 24 * 7,
|
|
8
|
+
year: Math.round(365.25 * 60 * 60 * 24),
|
|
9
|
+
};
|
|
10
|
+
return Math.round(value * multipliers[unit]);
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Returns a Unix timestamp (in seconds) offset from now by the given duration.
|
|
14
|
+
* e.g. value = -1 and unit = 'day' => 1 day ago => now - 86400 seconds
|
|
15
|
+
*/
|
|
16
|
+
export const getRelativeTimeOffsetFromNow = (relativeTime) => {
|
|
17
|
+
const now = Math.floor(Date.now() / 1000);
|
|
18
|
+
console.log(getRelativeTimeOffset(relativeTime), relativeTime, now);
|
|
19
|
+
return new Date(now + getRelativeTimeOffset(relativeTime));
|
|
20
|
+
};
|
package/dist/permissions.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CoreServices, CoreUserSession } from './types/core.types.js';
|
|
2
|
-
import {
|
|
2
|
+
import { CorePermissionGroup } from './function/functions.types.js';
|
|
3
3
|
/**
|
|
4
4
|
* This function validates permissions by iterating over permission groups and executing the corresponding permission functions. If all functions in at least one group return true, the permission is considered valid.
|
|
5
5
|
* @param services - The core services required for permission validation.
|
|
@@ -7,4 +7,4 @@ import { CoreAPIPermission } from './types/functions.types.js';
|
|
|
7
7
|
* @param session - An optional user session for permission validation.
|
|
8
8
|
* @returns A promise that resolves to void.
|
|
9
9
|
*/
|
|
10
|
-
export declare const verifyPermissions: (permissions:
|
|
10
|
+
export declare const verifyPermissions: (permissions: CorePermissionGroup, services: CoreServices, data: any, session?: CoreUserSession) => Promise<boolean>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CoreServices, CoreSingletonServices, CoreUserSession } from "./types/core.types.js";
|
|
2
|
+
import { CoreAPIFunction, CoreAPIFunctionSessionless, CorePermissionGroup } from "./types/functions.types.js";
|
|
3
|
+
export declare const getFunctionName: (func: CoreAPIFunction<any, any> | CoreAPIFunctionSessionless<any, any>) => string;
|
|
4
|
+
export declare const runPikkuFuncDirectly: <In, Out>(funcName: string, allServices: CoreServices, data: In, session?: CoreUserSession) => Promise<Out>;
|
|
5
|
+
export declare const runPikkuFunc: <In = any, Out = any>(funcName: string, { singletonServices, getAllServices, data, session, permissions, coerceDataFromSchema }: {
|
|
6
|
+
singletonServices: CoreSingletonServices;
|
|
7
|
+
getAllServices: () => Promise<CoreServices> | CoreServices;
|
|
8
|
+
data: In;
|
|
9
|
+
session?: CoreUserSession;
|
|
10
|
+
permissions?: CorePermissionGroup;
|
|
11
|
+
coerceDataFromSchema?: boolean;
|
|
12
|
+
}) => Promise<Out>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ForbiddenError } from "./errors/errors.js";
|
|
2
|
+
import { verifyPermissions } from "./permissions.js";
|
|
3
|
+
import { pikkuState } from "./pikku-state.js";
|
|
4
|
+
import { coerceTopLevelDataFromSchema, validateSchema } from "./schema.js";
|
|
5
|
+
export const getFunctionName = (func) => {
|
|
6
|
+
const funcName = pikkuState('functions', 'functionToName').get(func);
|
|
7
|
+
if (!funcName) {
|
|
8
|
+
throw new Error(`Function not found: ${funcName}`);
|
|
9
|
+
}
|
|
10
|
+
return funcName;
|
|
11
|
+
};
|
|
12
|
+
export const runPikkuFuncDirectly = async (funcName, allServices, data, session) => {
|
|
13
|
+
const func = pikkuState('functions', 'nameToFunction').get(funcName);
|
|
14
|
+
if (!func) {
|
|
15
|
+
throw new Error(`Function not found: ${funcName}`);
|
|
16
|
+
}
|
|
17
|
+
return await func(allServices, data, session);
|
|
18
|
+
};
|
|
19
|
+
export const runPikkuFunc = async (funcName, { singletonServices, getAllServices, data, session, permissions, coerceDataFromSchema }) => {
|
|
20
|
+
const func = pikkuState('functions', 'nameToFunction').get(funcName);
|
|
21
|
+
if (!func) {
|
|
22
|
+
throw new Error(`Function not found: ${funcName}`);
|
|
23
|
+
}
|
|
24
|
+
const funcMeta = pikkuState('functions', 'meta')[funcName];
|
|
25
|
+
const schemaName = funcMeta.schemaName;
|
|
26
|
+
// Validate request data against the defined schema, if any
|
|
27
|
+
await validateSchema(singletonServices.logger, singletonServices.schema, schemaName, data);
|
|
28
|
+
// Coerce (top level) query string parameters or date objects if specified by the schema
|
|
29
|
+
if (coerceDataFromSchema && schemaName) {
|
|
30
|
+
coerceTopLevelDataFromSchema(schemaName, data);
|
|
31
|
+
}
|
|
32
|
+
const allServices = await getAllServices();
|
|
33
|
+
// Execute permission checks
|
|
34
|
+
const permissioned = await verifyPermissions(permissions, allServices, data, session);
|
|
35
|
+
if (permissioned === false) {
|
|
36
|
+
throw new ForbiddenError('Permission denied');
|
|
37
|
+
}
|
|
38
|
+
return await func(allServices, data, session);
|
|
39
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const callFunction: () => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const callFunction = () => { };
|
package/dist/pikku-state.d.ts
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
import { ChannelsMeta,
|
|
2
|
-
import {
|
|
3
|
-
import { PikkuMiddleware } from './types/core.types.js';
|
|
1
|
+
import { ChannelsMeta, CoreAPIChannel } from './channel/channel.types.js';
|
|
2
|
+
import { CoreHTTPFunctionRoute, HTTPMethod, HTTPRoutesMeta } from './http/http.types.js';
|
|
3
|
+
import { FunctionsMeta, PikkuMiddleware } from './types/core.types.js';
|
|
4
4
|
import { CoreScheduledTask, ScheduledTasksMeta } from './scheduler/scheduler.types.js';
|
|
5
5
|
import { ErrorDetails, PikkuError } from './errors/error-handler.js';
|
|
6
|
+
import { CoreAPIFunction, CoreAPIFunctionSessionless } from './function/functions.types.js';
|
|
6
7
|
interface PikkuState {
|
|
8
|
+
functions: {
|
|
9
|
+
meta: FunctionsMeta;
|
|
10
|
+
nameToFunction: Map<string, CoreAPIFunction<any, any> | CoreAPIFunctionSessionless<any, any>>;
|
|
11
|
+
};
|
|
7
12
|
http: {
|
|
8
13
|
middleware: Array<{
|
|
9
14
|
route: string;
|
|
10
15
|
middleware: PikkuMiddleware[];
|
|
11
16
|
}>;
|
|
12
|
-
routes:
|
|
17
|
+
routes: Map<HTTPMethod, Map<string, CoreHTTPFunctionRoute<any, any, any>>>;
|
|
13
18
|
meta: HTTPRoutesMeta;
|
|
14
19
|
};
|
|
15
20
|
channel: {
|
|
16
|
-
channels:
|
|
21
|
+
channels: Map<string, CoreAPIChannel<any, any>>;
|
|
17
22
|
meta: ChannelsMeta;
|
|
18
23
|
};
|
|
19
24
|
scheduler: {
|
package/dist/pikku-state.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
export const resetPikkuState = () => {
|
|
2
2
|
globalThis.pikkuState = {
|
|
3
|
+
functions: {
|
|
4
|
+
meta: {},
|
|
5
|
+
nameToFunction: new Map(),
|
|
6
|
+
},
|
|
3
7
|
http: {
|
|
4
8
|
middleware: [],
|
|
5
|
-
routes:
|
|
9
|
+
routes: new Map(),
|
|
6
10
|
meta: [],
|
|
7
11
|
},
|
|
8
12
|
channel: {
|
|
9
|
-
channels:
|
|
10
|
-
meta:
|
|
13
|
+
channels: new Map(),
|
|
14
|
+
meta: {},
|
|
11
15
|
},
|
|
12
16
|
scheduler: {
|
|
13
17
|
tasks: new Map(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CoreServices, CoreSingletonServices, CoreUserSession, CreateSessionServices } from '../types/core.types.js';
|
|
2
2
|
import type { CoreScheduledTask } from './scheduler.types.js';
|
|
3
|
-
import type { CoreAPIFunctionSessionless } from '../
|
|
3
|
+
import type { CoreAPIFunctionSessionless } from '../function/functions.types.js';
|
|
4
4
|
export type RunScheduledTasksParams = {
|
|
5
5
|
name: string;
|
|
6
6
|
session?: CoreUserSession;
|