@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.
Files changed (130) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/dist/channel/channel-handler.js +16 -38
  3. package/dist/channel/channel-runner.d.ts +7 -8
  4. package/dist/channel/channel-runner.js +58 -7
  5. package/dist/channel/channel.types.d.ts +14 -27
  6. package/dist/channel/local/local-channel-runner.js +9 -4
  7. package/dist/channel/log-channels.js +4 -4
  8. package/dist/channel/serverless/serverless-channel-runner.js +17 -6
  9. package/dist/function/function-runner.d.ts +12 -0
  10. package/dist/function/function-runner.js +38 -0
  11. package/dist/function/functions.types.d.ts +37 -0
  12. package/dist/function/functions.types.js +1 -0
  13. package/dist/function/index.d.ts +2 -0
  14. package/dist/function/index.js +2 -0
  15. package/dist/handle-error.d.ts +1 -1
  16. package/dist/http/http-route-runner.d.ts +3 -3
  17. package/dist/http/http-route-runner.js +60 -35
  18. package/dist/http/http-routes.types.d.ts +38 -7
  19. package/dist/http/http-runner.d.ts +90 -0
  20. package/dist/http/http-runner.js +336 -0
  21. package/dist/http/http.types.d.ts +177 -0
  22. package/dist/http/http.types.js +1 -0
  23. package/dist/http/index.d.ts +2 -2
  24. package/dist/http/index.js +1 -1
  25. package/dist/http/log-http-routes.js +7 -5
  26. package/dist/http/pikku-fetch-http-request.d.ts +1 -1
  27. package/dist/http/pikku-fetch-http-response.d.ts +4 -2
  28. package/dist/http/pikku-fetch-http-response.js +64 -12
  29. package/dist/http/pikku-http-request.d.ts +56 -0
  30. package/dist/http/pikku-http-request.js +95 -0
  31. package/dist/http/pikku-http-response.d.ts +13 -0
  32. package/dist/http/pikku-http-response.js +60 -0
  33. package/dist/index.d.ts +3 -2
  34. package/dist/index.js +3 -2
  35. package/dist/middleware/auth-apikey.js +5 -4
  36. package/dist/middleware/auth-bearer.js +5 -4
  37. package/dist/middleware/auth-cookie.js +10 -9
  38. package/dist/parse-relative-time-offset.d.ts +12 -0
  39. package/dist/parse-relative-time-offset.js +20 -0
  40. package/dist/permissions.d.ts +2 -2
  41. package/dist/pikku-func.d.ts +12 -0
  42. package/dist/pikku-func.js +39 -0
  43. package/dist/pikku-function.d.ts +1 -0
  44. package/dist/pikku-function.js +1 -0
  45. package/dist/pikku-state.d.ts +10 -5
  46. package/dist/pikku-state.js +7 -3
  47. package/dist/scheduler/scheduler-runner.d.ts +1 -1
  48. package/dist/scheduler/scheduler-runner.js +24 -6
  49. package/dist/scheduler/scheduler.types.d.ts +3 -2
  50. package/dist/services/content-service.d.ts +34 -36
  51. package/dist/services/index.d.ts +1 -0
  52. package/dist/services/index.js +1 -0
  53. package/dist/services/local-content.d.ts +4 -2
  54. package/dist/services/local-content.js +19 -9
  55. package/dist/types/core.types.d.ts +15 -1
  56. package/dist/types/functions.types.d.ts +11 -6
  57. package/dist/utils.d.ts +2 -0
  58. package/dist/utils.js +15 -0
  59. package/package.json +2 -1
  60. package/lcov.info +0 -4310
  61. package/src/channel/channel-handler.ts +0 -227
  62. package/src/channel/channel-runner.ts +0 -98
  63. package/src/channel/channel-store.ts +0 -29
  64. package/src/channel/channel.types.ts +0 -171
  65. package/src/channel/eventhub-service.ts +0 -30
  66. package/src/channel/eventhub-store.ts +0 -8
  67. package/src/channel/index.ts +0 -8
  68. package/src/channel/local/index.ts +0 -3
  69. package/src/channel/local/local-channel-handler.ts +0 -50
  70. package/src/channel/local/local-channel-runner.test.ts +0 -158
  71. package/src/channel/local/local-channel-runner.ts +0 -126
  72. package/src/channel/local/local-eventhub-service.test.ts +0 -95
  73. package/src/channel/local/local-eventhub-service.ts +0 -95
  74. package/src/channel/log-channels.ts +0 -20
  75. package/src/channel/pikku-abstract-channel-handler.test.ts +0 -52
  76. package/src/channel/pikku-abstract-channel-handler.ts +0 -38
  77. package/src/channel/serverless/index.ts +0 -1
  78. package/src/channel/serverless/serverless-channel-runner.ts +0 -209
  79. package/src/errors/error-handler.ts +0 -61
  80. package/src/errors/error.test.ts +0 -54
  81. package/src/errors/errors.ts +0 -339
  82. package/src/errors/index.ts +0 -2
  83. package/src/handle-error.ts +0 -73
  84. package/src/http/http-route-runner.test.ts +0 -117
  85. package/src/http/http-route-runner.ts +0 -460
  86. package/src/http/http-routes.types.ts +0 -211
  87. package/src/http/incomingmessage-to-request-convertor.ts +0 -0
  88. package/src/http/index.ts +0 -8
  89. package/src/http/log-http-routes.ts +0 -20
  90. package/src/http/pikku-fetch-http-request.test.ts +0 -237
  91. package/src/http/pikku-fetch-http-request.ts +0 -167
  92. package/src/http/pikku-fetch-http-response.test.ts +0 -82
  93. package/src/http/pikku-fetch-http-response.ts +0 -84
  94. package/src/index.ts +0 -19
  95. package/src/middleware/auth-apikey.ts +0 -65
  96. package/src/middleware/auth-bearer.ts +0 -65
  97. package/src/middleware/auth-cookie.ts +0 -98
  98. package/src/middleware/index.ts +0 -3
  99. package/src/middleware/timeout.ts +0 -13
  100. package/src/middleware-runner.ts +0 -43
  101. package/src/permissions.test.ts +0 -58
  102. package/src/permissions.ts +0 -45
  103. package/src/pikku-request.ts +0 -23
  104. package/src/pikku-response.ts +0 -5
  105. package/src/pikku-state.ts +0 -71
  106. package/src/scheduler/index.ts +0 -5
  107. package/src/scheduler/log-schedulers.ts +0 -20
  108. package/src/scheduler/scheduler-runner.ts +0 -87
  109. package/src/scheduler/scheduler.types.ts +0 -29
  110. package/src/schema.test.ts +0 -57
  111. package/src/schema.ts +0 -99
  112. package/src/services/content-service.ts +0 -69
  113. package/src/services/index.ts +0 -17
  114. package/src/services/jwt-service.ts +0 -30
  115. package/src/services/local-content.ts +0 -91
  116. package/src/services/local-secrets.ts +0 -43
  117. package/src/services/local-variables.ts +0 -17
  118. package/src/services/logger-console.ts +0 -97
  119. package/src/services/logger.ts +0 -57
  120. package/src/services/schema-service.ts +0 -26
  121. package/src/services/secret-service.ts +0 -17
  122. package/src/services/user-session-service.ts +0 -53
  123. package/src/services/variables-service.ts +0 -6
  124. package/src/time-utils.test.ts +0 -56
  125. package/src/time-utils.ts +0 -32
  126. package/src/types/core.types.ts +0 -159
  127. package/src/types/functions.types.ts +0 -63
  128. package/src/utils.ts +0 -21
  129. package/tsconfig.json +0 -13
  130. 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.#body = data;
27
- this.header('Content-Type', 'application/octet-stream');
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.#body = JSON.stringify(data);
32
- this.header('Content-Type', 'application/json');
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.#body = content;
37
- this.header('Content-Type', 'text/plain');
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.#body = content;
42
- this.header('Content-Type', 'text/html');
43
- return this;
44
- }
45
- body(body) {
46
- this.#body = body;
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 './types/functions.types.js';
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 { addRoute, addMiddleware } from './http/http-route-runner.js';
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 './types/functions.types.js';
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 { addRoute, addMiddleware } from './http/http-route-runner.js';
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
- if (!http?.request || services.userSession.get()) {
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 (!services.jwt) {
22
+ if (!jwtService) {
22
23
  throw new Error('JWT service is required for JWT decoding.');
23
24
  }
24
- userSession = await services.jwt.decode(apiKey);
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
- services.userSession.setInitial(userSession);
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 || services.userSession.get()) {
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 (!services.jwt) {
21
+ if (!jwtService) {
21
22
  throw new Error('JWT service is required for JWT decoding.');
22
23
  }
23
- userSession = await services.jwt.decode(bearerToken);
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
- services.userSession.setInitial(userSession);
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
- if (!http?.request || services.userSession.get()) {
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 (!services.jwt) {
18
+ if (!jwtService) {
18
19
  throw new Error('JWT service is required for JWT decoding.');
19
20
  }
20
- userSession = await services.jwt.decode(cookieValue);
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
- services.userSession.setInitial(userSession);
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 (services.userSession.sessionChanged) {
35
- const session = services.userSession.get();
35
+ if (userSessionService.sessionChanged) {
36
+ const session = userSessionService.get();
36
37
  if (jwt) {
37
- if (!services.jwt) {
38
+ if (!jwtService) {
38
39
  throw new Error('JWT service is required for JWT encoding.');
39
40
  }
40
- http.response.cookie(name, await services.jwt.encode(expiresIn, session), {
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
- services.logger.warn('No JWT service available, unable to set cookie');
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
+ };
@@ -1,5 +1,5 @@
1
1
  import { CoreServices, CoreUserSession } from './types/core.types.js';
2
- import { CoreAPIPermission } from './types/functions.types.js';
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: Record<string, CoreAPIPermission<any>[] | CoreAPIPermission<any>> | undefined, services: CoreServices, data: any, session?: CoreUserSession) => Promise<boolean>;
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 = () => { };
@@ -1,19 +1,24 @@
1
- import { ChannelsMeta, CoreAPIChannels } from './channel/channel.types.js';
2
- import { CoreHTTPFunctionRoutes, HTTPRoutesMeta } from './http/http-routes.types.js';
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: CoreHTTPFunctionRoutes;
17
+ routes: Map<HTTPMethod, Map<string, CoreHTTPFunctionRoute<any, any, any>>>;
13
18
  meta: HTTPRoutesMeta;
14
19
  };
15
20
  channel: {
16
- channels: CoreAPIChannels;
21
+ channels: Map<string, CoreAPIChannel<any, any>>;
17
22
  meta: ChannelsMeta;
18
23
  };
19
24
  scheduler: {
@@ -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 '../types/functions.types.js';
3
+ import type { CoreAPIFunctionSessionless } from '../function/functions.types.js';
4
4
  export type RunScheduledTasksParams = {
5
5
  name: string;
6
6
  session?: CoreUserSession;