@cloudnux/core-cloud-provider 0.19.0 → 0.21.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/dist/index.d.ts CHANGED
@@ -96,7 +96,7 @@ type Entrypoint = {
96
96
 
97
97
  interface IService {
98
98
  }
99
- type ServiceKind = "storage" | "location" | "event-broker" | "functions" | "websocket" | "invoke" | "logger";
99
+ type ServiceKind = "storage" | "location" | "event-broker" | "functions" | "websocket" | "invoke" | "logger" | "email";
100
100
  declare function registerService(kind: ServiceKind, factory: () => IService): void;
101
101
  declare function registerServices(services: Record<ServiceKind, () => IService>): void;
102
102
  declare function getService<T extends IService>(kind: ServiceKind): T;
@@ -391,10 +391,21 @@ declare enum ErrorCode {
391
391
  EXTERNAL_API_ERROR = "EXTERNAL_API_ERROR",
392
392
  UNKNOWN_ERROR = "UNKNOWN_ERROR"
393
393
  }
394
+ type SameSite = "Strict" | "Lax" | "None";
395
+ type CookieOptions = {
396
+ path?: string;
397
+ domain?: string;
398
+ maxAge?: number;
399
+ expires?: Date;
400
+ httpOnly?: boolean;
401
+ secure?: boolean;
402
+ sameSite?: SameSite;
403
+ };
394
404
  type HTTPRequest = {
395
405
  method: HttpMethod;
396
406
  body?: string;
397
407
  headers: Record<string, string | string[] | undefined>;
408
+ cookies: Record<string, string>;
398
409
  url: string;
399
410
  params: Record<string, string | undefined>;
400
411
  matchingKey?: string;
@@ -405,21 +416,13 @@ type HTTPRequest = {
405
416
  };
406
417
  type HTTPResponse = {
407
418
  headers?: Record<string, string | string[] | undefined>;
419
+ cookies?: string[];
408
420
  body?: string;
409
421
  status: number;
410
422
  };
411
- type HTTPAuth = {
412
- token: string;
413
- appId: string;
414
- memberId: string;
415
- customerId: string;
416
- claims: Record<string, string>;
417
- identity: "facebook" | "google" | "apple" | "password";
418
- };
419
423
  type HttpFunctionContext = FunctionContext & {
420
424
  type: "Http";
421
425
  request: HTTPRequest;
422
- auth?: HTTPAuth;
423
426
  response: HTTPResponse;
424
427
  model<T = Record<string, any>>(): T;
425
428
  params<T = Record<string, string>>(): T;
@@ -433,6 +436,19 @@ type HttpFunctionContext = FunctionContext & {
433
436
  unauthorized(message?: string): void;
434
437
  forbidden(message?: string): void;
435
438
  output(status: number, body?: string | object, headers?: Record<string, string | string[]>): void;
439
+ /**
440
+ * Queues a `Set-Cookie` header on the response.
441
+ * @param name - The cookie name.
442
+ * @param value - The cookie value.
443
+ * @param options - Cookie attributes (Path, Domain, Max-Age, Expires, HttpOnly, Secure, SameSite).
444
+ */
445
+ setCookie(name: string, value: string, options?: CookieOptions): void;
446
+ /**
447
+ * Queues a `Set-Cookie` header that expires the named cookie immediately.
448
+ * @param name - The cookie name.
449
+ * @param options - Must match the Path/Domain the cookie was originally set with.
450
+ */
451
+ deleteCookie(name: string, options?: Pick<CookieOptions, "path" | "domain">): void;
436
452
  };
437
453
  type ScheduleResponse = {
438
454
  status: "success" | "error";
@@ -523,7 +539,7 @@ type WebSocketFunctionContext = FunctionContext & {
523
539
  validationError(details: Record<string, any>): void;
524
540
  };
525
541
  interface FunctionsService extends IService {
526
- createHttRequest(...args: any[]): [HTTPRequest, HTTPAuth?];
542
+ createHttRequest(...args: any[]): [HTTPRequest];
527
543
  createScheduleRequest(...args: any[]): [ScheduleRequest];
528
544
  createEventRequest(...args: any[]): [EventRequest];
529
545
  createWebSocketRequest(...args: any[]): [WebSocketRequest];
@@ -535,6 +551,16 @@ interface FunctionsService extends IService {
535
551
  buildInvokeResponse(invokeFunctionContext: InvokeFunctionContext, ...args: any[]): any;
536
552
  }
537
553
 
554
+ /**
555
+ * Serializes a single cookie into a `Set-Cookie` header value.
556
+ * Throws if `sameSite` is "None" without `secure` set, since browsers reject that combination.
557
+ */
558
+ declare function serializeCookie(name: string, value: string, options?: CookieOptions): string;
559
+ /**
560
+ * Parses a raw `Cookie` request header (e.g. "a=1; b=2") into a name/value map.
561
+ */
562
+ declare function parseCookieHeader(header?: string | null): Record<string, string>;
563
+
538
564
  /**
539
565
  * Thrown when a sendToClient/disconnect is attempted against a connectionId
540
566
  * that is no longer connected (AWS API Gateway GoneException equivalent, HTTP 410).
@@ -557,6 +583,46 @@ interface WebSocketService {
557
583
  disconnect(connectionId: string): Promise<void>;
558
584
  }
559
585
 
586
+ type EmailAddress = string;
587
+ type EmailAttachment = {
588
+ filename: string;
589
+ content: Buffer | string;
590
+ contentType: string;
591
+ };
592
+ type EmailMessage = {
593
+ to: EmailAddress | EmailAddress[];
594
+ from: EmailAddress;
595
+ subject: string;
596
+ html?: string;
597
+ text?: string;
598
+ replyTo?: EmailAddress;
599
+ cc?: EmailAddress | EmailAddress[];
600
+ bcc?: EmailAddress | EmailAddress[];
601
+ attachments?: EmailAttachment[];
602
+ /**
603
+ * Named SES configuration set to send through (event destinations,
604
+ * reputation/dedicated-IP behavior, etc). No-op on providers that
605
+ * don't have the concept.
606
+ */
607
+ configurationSet?: string;
608
+ /**
609
+ * Provider-specific passthrough (SES tags, ACS headers, etc).
610
+ * Adapters may use this; handlers should avoid depending on it
611
+ * to stay provider-agnostic.
612
+ */
613
+ metadata?: Record<string, string>;
614
+ };
615
+ type EmailSendResult = {
616
+ status: "sent";
617
+ messageId: string;
618
+ } | {
619
+ status: "failed";
620
+ error: string;
621
+ };
622
+ interface EmailService extends IService {
623
+ send(message: EmailMessage): Promise<EmailSendResult>;
624
+ }
625
+
560
626
  interface CloudProvider {
561
627
  name: string;
562
628
  createStorageService(): StorageService;
@@ -566,6 +632,7 @@ interface CloudProvider {
566
632
  createWebSocketService(): WebSocketService;
567
633
  createInvokeService(): InvokeService;
568
634
  createLoggerService(): LoggerService;
635
+ createEmailService(): EmailService;
569
636
  }
570
637
 
571
- export { type BaseEntry, type CloudProvider, type Coordinates, type Entry, type Entrypoint, ErrorCode, type EventBatchItemResult, type EventBrokerService, type EventEntry, type EventFunctionContext, type EventMessage, type EventRequest, type EventResponse, type EventTrigger, type FunctionContext, type FunctionsService, type HTTPAuth, type HTTPRequest, type HTTPResponse, type HandlerType, type HttpEntry, type HttpFunctionContext, type HttpMethod, type HttpTrigger, type IService, type InvokeEntry, type InvokeFunctionContext, type InvokeRequest, type InvokeResponse, type InvokeService, type InvokeTrigger, type LocationAddress, type LocationProviderConfig, type LocationService, type LocationSuggestion, type LogEntry, type LoggerService, type MessageFilter, type PeekOptions, type PublishOptions, type ReadOptions, type ReverseGeocodeParams, type RoutePoint, type RouteResult, type ScheduleEntry, type ScheduleFunctionContext, type ScheduleRequest, type ScheduleResponse, type ScheduleTrigger, type ServiceKind, type StorageService, type StorageWriteOptions, type StorageWriteResult, type SuggestionParams, WebSocketConnectionGoneError, type WebSocketEntry, type WebSocketEvent, type WebSocketFunctionContext, type WebSocketRequest, type WebSocketResponse, type WebSocketService, type WebSocketTrigger, getService, registerService, registerServices };
638
+ export { type BaseEntry, type CloudProvider, type CookieOptions, type Coordinates, type EmailAddress, type EmailAttachment, type EmailMessage, type EmailSendResult, type EmailService, type Entry, type Entrypoint, ErrorCode, type EventBatchItemResult, type EventBrokerService, type EventEntry, type EventFunctionContext, type EventMessage, type EventRequest, type EventResponse, type EventTrigger, type FunctionContext, type FunctionsService, type HTTPRequest, type HTTPResponse, type HandlerType, type HttpEntry, type HttpFunctionContext, type HttpMethod, type HttpTrigger, type IService, type InvokeEntry, type InvokeFunctionContext, type InvokeRequest, type InvokeResponse, type InvokeService, type InvokeTrigger, type LocationAddress, type LocationProviderConfig, type LocationService, type LocationSuggestion, type LogEntry, type LoggerService, type MessageFilter, type PeekOptions, type PublishOptions, type ReadOptions, type ReverseGeocodeParams, type RoutePoint, type RouteResult, type SameSite, type ScheduleEntry, type ScheduleFunctionContext, type ScheduleRequest, type ScheduleResponse, type ScheduleTrigger, type ServiceKind, type StorageService, type StorageWriteOptions, type StorageWriteResult, type SuggestionParams, WebSocketConnectionGoneError, type WebSocketEntry, type WebSocketEvent, type WebSocketFunctionContext, type WebSocketRequest, type WebSocketResponse, type WebSocketService, type WebSocketTrigger, getService, parseCookieHeader, registerService, registerServices, serializeCookie };
package/dist/index.js CHANGED
@@ -16,6 +16,48 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
16
16
  return ErrorCode2;
17
17
  })(ErrorCode || {});
18
18
 
19
+ // src/utils/cookies.ts
20
+ function serializeCookie(name, value, options = {}) {
21
+ const {
22
+ path = "/",
23
+ domain,
24
+ maxAge,
25
+ expires,
26
+ httpOnly = true,
27
+ secure = true,
28
+ sameSite = "Lax"
29
+ } = options;
30
+ if (sameSite === "None" && !secure) {
31
+ throw new Error(`Cookie "${name}" sets SameSite=None but not Secure; browsers require Secure with SameSite=None`);
32
+ }
33
+ const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
34
+ parts.push(`Path=${path}`);
35
+ if (domain) parts.push(`Domain=${domain}`);
36
+ if (maxAge !== void 0) parts.push(`Max-Age=${Math.trunc(maxAge)}`);
37
+ if (expires) parts.push(`Expires=${expires.toUTCString()}`);
38
+ if (httpOnly) parts.push("HttpOnly");
39
+ if (secure) parts.push("Secure");
40
+ parts.push(`SameSite=${sameSite}`);
41
+ return parts.join("; ");
42
+ }
43
+ function parseCookieHeader(header) {
44
+ const cookies = {};
45
+ if (!header) return cookies;
46
+ for (const pair of header.split(";")) {
47
+ const idx = pair.indexOf("=");
48
+ if (idx === -1) continue;
49
+ const name = pair.slice(0, idx).trim();
50
+ const value = pair.slice(idx + 1).trim();
51
+ if (!name) continue;
52
+ try {
53
+ cookies[name] = decodeURIComponent(value);
54
+ } catch {
55
+ cookies[name] = value;
56
+ }
57
+ }
58
+ return cookies;
59
+ }
60
+
19
61
  // src/services/websocket.ts
20
62
  var WebSocketConnectionGoneError = class extends Error {
21
63
  connectionId;
@@ -34,7 +76,8 @@ var _cloudContainer = {
34
76
  functions: void 0,
35
77
  websocket: void 0,
36
78
  invoke: void 0,
37
- logger: void 0
79
+ logger: void 0,
80
+ email: void 0
38
81
  };
39
82
  function registerService(kind, factory) {
40
83
  _cloudContainer[kind] = factory;
@@ -57,7 +100,9 @@ export {
57
100
  ErrorCode,
58
101
  WebSocketConnectionGoneError,
59
102
  getService,
103
+ parseCookieHeader,
60
104
  registerService,
61
- registerServices
105
+ registerServices,
106
+ serializeCookie
62
107
  };
63
108
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/services/functions.ts","../src/services/websocket.ts","../src/cloud-container.ts"],"sourcesContent":["import { IService } from \"../cloud-container\";\nimport { HandlerType, HttpMethod, WebSocketEvent } from \"../entrypoint\";\nimport { LoggerService } from \"./logger\";\n\n/**\n * Represents the context object for a function handler.\n */\nexport type FunctionContext = {\n /**\n * The underlying context handler type.\n */\n type: HandlerType;\n\n /**\n * Logger bound to this invocation's module name and request id.\n */\n logger: LoggerService;\n\n /**\n * Creates a success response. This should be called when the handler is done successfully.\n * @param data - The response body.\n */\n success<T>(data: T): void;\n\n /**\n * Creates a server error response. This should be called when the handler encounters an unexpected error.\n * @param err - The error object.\n */\n serverError(err: Error): void;\n /**\n * Creates an error response. This should be called when the handler is done with an error.\n * @param message - The response body.\n * \n */\n error(message: string, code?: ErrorCode, details?: Record<string, any>, stack?: string): void;\n /**\n * Creates a not found response. This should be called when the requested resource is not found.\n * @param resource - The name of the resource that was not found.\n * @param id - The id of the resource that was not found (optional).\n */\n notFound(resource: string, id?: string | number): void;\n};\n\n/**\n * Error codes for API error responses\n */\nexport enum ErrorCode {\n // Validation errors\n VALIDATION_ERROR = 'VALIDATION_ERROR',\n INVALID_INPUT = 'INVALID_INPUT',\n\n // Authentication errors\n UNAUTHORIZED = 'UNAUTHORIZED',\n ACCESS_DENIED = 'ACCESS_DENIED',\n\n // Resource errors\n RESOURCE_NOT_FOUND = 'RESOURCE_NOT_FOUND',\n RESOURCE_ALREADY_EXISTS = 'RESOURCE_ALREADY_EXISTS',\n RESOURCE_CONFLICT = 'RESOURCE_CONFLICT',\n\n // System errors\n INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR',\n SERVICE_UNAVAILABLE = 'SERVICE_UNAVAILABLE',\n DATABASE_ERROR = 'DATABASE_ERROR',\n\n // Business logic errors\n BUSINESS_RULE_VIOLATION = 'BUSINESS_RULE_VIOLATION',\n\n // External API errors\n EXTERNAL_API_ERROR = 'EXTERNAL_API_ERROR',\n\n // Other errors\n UNKNOWN_ERROR = 'UNKNOWN_ERROR'\n}\n\n//#region [ Http ]\n\nexport type HTTPRequest = {\n method: HttpMethod;\n body?: string;\n headers: Record<string, string | string[] | undefined>;\n url: string;\n params: Record<string, string | undefined>\n matchingKey?: string;\n rawQueryString?: string,\n requestId?: string,\n host?: string,\n moduleName?: string,\n};\n\nexport type HTTPResponse = {\n headers?: Record<string, string | string[] | undefined>;\n body?: string;\n status: number;\n};\n\nexport type HTTPAuth = {\n token: string;\n appId: string;\n memberId: string;\n customerId: string;\n claims: Record<string, string>;\n identity: \"facebook\" | \"google\" | \"apple\" | \"password\";\n};\n\nexport type HttpFunctionContext = FunctionContext & {\n type: \"Http\";\n request: HTTPRequest;\n auth?: HTTPAuth;\n response: HTTPResponse;\n model<T = Record<string, any>>(): T;\n params<T = Record<string, string>>(): T;\n\n list<T>(items: T[], page: number, pageSize: number, totalItems: number): void;\n cursorList<T>(items: T[], nextCursor: string | null, pageSize: number, prevCursor?: string | null): void;\n created<T>(data: T): void;\n deleted(id?: string | number): void;\n validationError(details: Record<string, any>): void;\n serverError(err: Error): void;\n businessError(message: string, details?: Record<string, any>): void;\n\n unauthorized(message?: string): void;\n forbidden(message?: string): void;\n output(status: number, body?: string | object, headers?: Record<string, string | string[]>): void;\n}\n\n//#endregion\n\n//#region [Schedule]\n\nexport type ScheduleResponse = {\n status: \"success\" | \"error\",\n body?: string | Record<string, any>;\n}\n\nexport type ScheduleRequest = {\n name: string,\n requestId?: string,\n moduleName?: string,\n};\n\nexport type ScheduleFunctionContext = FunctionContext & {\n response: ScheduleResponse;\n request: ScheduleRequest;\n type: \"Schedule\";\n}\n//#endregion\n\n//#region [Event]\nexport type EventRequest = {\n body: string | Record<string, any>,\n attributes: Record<string, string>,\n requestId?: string,\n timestamp: Date,\n attempts?: number;\n moduleName?: string,\n}\n\nexport type EventResponse = {\n status: \"success\" | \"error\",\n code?: ErrorCode,\n body?: Record<string, any>\n stack?: string,\n retryDelay?: number,\n};\n\nexport type EventFunctionContext = FunctionContext & {\n type: \"Event\";\n timestamp: Date;\n attempts?: number;\n request: EventRequest;\n response: EventResponse;\n message<T = Record<string, any>>(): T;\n attributes<T = Record<string, any>>(): T;\n retryWithDelay(seconds: number): void;\n}\n\nexport type EventBatchItemResult = { failureId: string } | undefined;\n//#endregion\n\n//#region [Invoke]\nexport type InvokeRequest = {\n payload: any,\n calledModule: string,\n invokeTriggerName: string,\n requestId?: string,\n}\n\nexport type InvokeResponse = {\n status: \"success\" | \"error\",\n body?: any,\n code?: ErrorCode,\n}\n\nexport type InvokeFunctionContext = FunctionContext & {\n type: \"Invoke\";\n request: InvokeRequest;\n response: InvokeResponse;\n payload<T = Record<string, any>>(): T;\n}\n//#endregion\n\n//#region [WebSocket]\nexport type WebSocketRequest = {\n connectionId: string,\n event: WebSocketEvent,\n route?: string,\n url?: string,\n params: Record<string, string | undefined> | null,\n body?: string | Record<string, any> | null,\n headers?: Record<string, string | string[] | undefined>,\n queryString: Record<string, string | undefined> | null,\n requestId?: string,\n moduleName?: string,\n}\n\nexport type WebSocketResponse = {\n status: \"success\" | \"error\",\n statusCode?: number,\n body?: any,\n}\n\nexport type WebSocketFunctionContext = FunctionContext & {\n type: \"WebSocket\";\n connectionId: string;\n event: WebSocketEvent;\n request: WebSocketRequest;\n response: WebSocketResponse;\n message<T = Record<string, any>>(): T;\n params<T = Record<string, string>>(): T;\n unauthorized(body?: any): void;\n forbidden(body?: any): void;\n validationError(details: Record<string, any>): void;\n}\n//#endregion\n\nexport interface FunctionsService extends IService {\n createHttRequest(...args: any[]): [HTTPRequest, HTTPAuth?];\n createScheduleRequest(...args: any[]): [ScheduleRequest];\n createEventRequest(...args: any[]): [EventRequest];\n createWebSocketRequest(...args: any[]): [WebSocketRequest];\n createInvokeRequest(...args: any[]): [InvokeRequest];\n\n buildHttpResponse(httpFunctionContext: HttpFunctionContext, ...args: any[]): any\n buildScheduleResponse(scheduleFunctionContext: ScheduleFunctionContext, ...args: any[]): any;\n buildEventResponse(eventFunctionContext: EventFunctionContext, ...args: any[]): Promise<EventBatchItemResult>;\n buildWebSocketResponse(webSocketFunctionContext: WebSocketFunctionContext, ...args: any[]): any;\n buildInvokeResponse(invokeFunctionContext: InvokeFunctionContext, ...args: any[]): any;\n}","/**\n * Thrown when a sendToClient/disconnect is attempted against a connectionId\n * that is no longer connected (AWS API Gateway GoneException equivalent, HTTP 410).\n */\nexport class WebSocketConnectionGoneError extends Error {\n public readonly connectionId: string;\n\n constructor(connectionId: string) {\n super(`WebSocket connection ${connectionId} is gone`);\n this.name = \"WebSocketConnectionGoneError\";\n this.connectionId = connectionId;\n }\n}\n\nexport interface WebSocketService {\n /**\n * Send a message to a specific connected client\n * @param connectionId The connection ID of the target client\n * @param data The data to send\n */\n sendToClient(connectionId: string, data: any): Promise<void>;\n\n /**\n * Disconnect a specific connected client\n * @param connectionId The connection ID of the client to disconnect\n */\n disconnect(connectionId: string): Promise<void>;\n}\n","export interface IService { };\nexport type ServiceKind = \"storage\" | \"location\" | \"event-broker\" | \"functions\" | \"websocket\" | \"invoke\" | \"logger\";\n\n\nconst _cloudContainer: Record<ServiceKind, (() => IService) | undefined> = {\n storage: undefined,\n location: undefined,\n \"event-broker\": undefined,\n functions: undefined,\n websocket: undefined,\n invoke: undefined,\n logger: undefined\n};\n\nexport function registerService(kind: ServiceKind, factory: () => IService) {\n _cloudContainer[kind] = factory;\n}\n\nexport function registerServices(services: Record<ServiceKind, () => IService>) {\n for (const kind in services) {\n if (services.hasOwnProperty(kind)) {\n _cloudContainer[kind as ServiceKind] = services[kind as ServiceKind];\n }\n }\n}\n\nexport function getService<T extends IService>(kind: ServiceKind): T {\n const service = _cloudContainer[kind];\n if (!service) {\n throw new Error(`Service of kind ${kind} is not registered.`);\n }\n return service() as T;\n}\n"],"mappings":";AA8CO,IAAK,YAAL,kBAAKA,eAAL;AAEH,EAAAA,WAAA,sBAAmB;AACnB,EAAAA,WAAA,mBAAgB;AAGhB,EAAAA,WAAA,kBAAe;AACf,EAAAA,WAAA,mBAAgB;AAGhB,EAAAA,WAAA,wBAAqB;AACrB,EAAAA,WAAA,6BAA0B;AAC1B,EAAAA,WAAA,uBAAoB;AAGpB,EAAAA,WAAA,2BAAwB;AACxB,EAAAA,WAAA,yBAAsB;AACtB,EAAAA,WAAA,oBAAiB;AAGjB,EAAAA,WAAA,6BAA0B;AAG1B,EAAAA,WAAA,wBAAqB;AAGrB,EAAAA,WAAA,mBAAgB;AA1BR,SAAAA;AAAA,GAAA;;;AC1CL,IAAM,+BAAN,cAA2C,MAAM;AAAA,EACpC;AAAA,EAEhB,YAAY,cAAsB;AAC9B,UAAM,wBAAwB,YAAY,UAAU;AACpD,SAAK,OAAO;AACZ,SAAK,eAAe;AAAA,EACxB;AACJ;;;ACRA,IAAM,kBAAqE;AAAA,EACvE,SAAS;AAAA,EACT,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AACZ;AAEO,SAAS,gBAAgB,MAAmB,SAAyB;AACxE,kBAAgB,IAAI,IAAI;AAC5B;AAEO,SAAS,iBAAiB,UAA+C;AAC5E,aAAW,QAAQ,UAAU;AACzB,QAAI,SAAS,eAAe,IAAI,GAAG;AAC/B,sBAAgB,IAAmB,IAAI,SAAS,IAAmB;AAAA,IACvE;AAAA,EACJ;AACJ;AAEO,SAAS,WAA+B,MAAsB;AACjE,QAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,CAAC,SAAS;AACV,UAAM,IAAI,MAAM,mBAAmB,IAAI,qBAAqB;AAAA,EAChE;AACA,SAAO,QAAQ;AACnB;","names":["ErrorCode"]}
1
+ {"version":3,"sources":["../src/services/functions.ts","../src/utils/cookies.ts","../src/services/websocket.ts","../src/cloud-container.ts"],"sourcesContent":["import { IService } from \"../cloud-container\";\nimport { HandlerType, HttpMethod, WebSocketEvent } from \"../entrypoint\";\nimport { LoggerService } from \"./logger\";\n\n/**\n * Represents the context object for a function handler.\n */\nexport type FunctionContext = {\n /**\n * The underlying context handler type.\n */\n type: HandlerType;\n\n /**\n * Logger bound to this invocation's module name and request id.\n */\n logger: LoggerService;\n\n /**\n * Creates a success response. This should be called when the handler is done successfully.\n * @param data - The response body.\n */\n success<T>(data: T): void;\n\n /**\n * Creates a server error response. This should be called when the handler encounters an unexpected error.\n * @param err - The error object.\n */\n serverError(err: Error): void;\n /**\n * Creates an error response. This should be called when the handler is done with an error.\n * @param message - The response body.\n * \n */\n error(message: string, code?: ErrorCode, details?: Record<string, any>, stack?: string): void;\n /**\n * Creates a not found response. This should be called when the requested resource is not found.\n * @param resource - The name of the resource that was not found.\n * @param id - The id of the resource that was not found (optional).\n */\n notFound(resource: string, id?: string | number): void;\n};\n\n/**\n * Error codes for API error responses\n */\nexport enum ErrorCode {\n // Validation errors\n VALIDATION_ERROR = 'VALIDATION_ERROR',\n INVALID_INPUT = 'INVALID_INPUT',\n\n // Authentication errors\n UNAUTHORIZED = 'UNAUTHORIZED',\n ACCESS_DENIED = 'ACCESS_DENIED',\n\n // Resource errors\n RESOURCE_NOT_FOUND = 'RESOURCE_NOT_FOUND',\n RESOURCE_ALREADY_EXISTS = 'RESOURCE_ALREADY_EXISTS',\n RESOURCE_CONFLICT = 'RESOURCE_CONFLICT',\n\n // System errors\n INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR',\n SERVICE_UNAVAILABLE = 'SERVICE_UNAVAILABLE',\n DATABASE_ERROR = 'DATABASE_ERROR',\n\n // Business logic errors\n BUSINESS_RULE_VIOLATION = 'BUSINESS_RULE_VIOLATION',\n\n // External API errors\n EXTERNAL_API_ERROR = 'EXTERNAL_API_ERROR',\n\n // Other errors\n UNKNOWN_ERROR = 'UNKNOWN_ERROR'\n}\n\n//#region [ Http ]\n\nexport type SameSite = \"Strict\" | \"Lax\" | \"None\";\n\nexport type CookieOptions = {\n path?: string;\n domain?: string;\n maxAge?: number;\n expires?: Date;\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: SameSite;\n};\n\nexport type HTTPRequest = {\n method: HttpMethod;\n body?: string;\n headers: Record<string, string | string[] | undefined>;\n cookies: Record<string, string>;\n url: string;\n params: Record<string, string | undefined>\n matchingKey?: string;\n rawQueryString?: string,\n requestId?: string,\n host?: string,\n moduleName?: string,\n};\n\nexport type HTTPResponse = {\n headers?: Record<string, string | string[] | undefined>;\n cookies?: string[];\n body?: string;\n status: number;\n};\n\nexport type HttpFunctionContext = FunctionContext & {\n type: \"Http\";\n request: HTTPRequest;\n response: HTTPResponse;\n model<T = Record<string, any>>(): T;\n params<T = Record<string, string>>(): T;\n\n list<T>(items: T[], page: number, pageSize: number, totalItems: number): void;\n cursorList<T>(items: T[], nextCursor: string | null, pageSize: number, prevCursor?: string | null): void;\n created<T>(data: T): void;\n deleted(id?: string | number): void;\n validationError(details: Record<string, any>): void;\n serverError(err: Error): void;\n businessError(message: string, details?: Record<string, any>): void;\n\n unauthorized(message?: string): void;\n forbidden(message?: string): void;\n output(status: number, body?: string | object, headers?: Record<string, string | string[]>): void;\n\n /**\n * Queues a `Set-Cookie` header on the response.\n * @param name - The cookie name.\n * @param value - The cookie value.\n * @param options - Cookie attributes (Path, Domain, Max-Age, Expires, HttpOnly, Secure, SameSite).\n */\n setCookie(name: string, value: string, options?: CookieOptions): void;\n /**\n * Queues a `Set-Cookie` header that expires the named cookie immediately.\n * @param name - The cookie name.\n * @param options - Must match the Path/Domain the cookie was originally set with.\n */\n deleteCookie(name: string, options?: Pick<CookieOptions, \"path\" | \"domain\">): void;\n}\n\n//#endregion\n\n//#region [Schedule]\n\nexport type ScheduleResponse = {\n status: \"success\" | \"error\",\n body?: string | Record<string, any>;\n}\n\nexport type ScheduleRequest = {\n name: string,\n requestId?: string,\n moduleName?: string,\n};\n\nexport type ScheduleFunctionContext = FunctionContext & {\n response: ScheduleResponse;\n request: ScheduleRequest;\n type: \"Schedule\";\n}\n//#endregion\n\n//#region [Event]\nexport type EventRequest = {\n body: string | Record<string, any>,\n attributes: Record<string, string>,\n requestId?: string,\n timestamp: Date,\n attempts?: number;\n moduleName?: string,\n}\n\nexport type EventResponse = {\n status: \"success\" | \"error\",\n code?: ErrorCode,\n body?: Record<string, any>\n stack?: string,\n retryDelay?: number,\n};\n\nexport type EventFunctionContext = FunctionContext & {\n type: \"Event\";\n timestamp: Date;\n attempts?: number;\n request: EventRequest;\n response: EventResponse;\n message<T = Record<string, any>>(): T;\n attributes<T = Record<string, any>>(): T;\n retryWithDelay(seconds: number): void;\n}\n\nexport type EventBatchItemResult = { failureId: string } | undefined;\n//#endregion\n\n//#region [Invoke]\nexport type InvokeRequest = {\n payload: any,\n calledModule: string,\n invokeTriggerName: string,\n requestId?: string,\n}\n\nexport type InvokeResponse = {\n status: \"success\" | \"error\",\n body?: any,\n code?: ErrorCode,\n}\n\nexport type InvokeFunctionContext = FunctionContext & {\n type: \"Invoke\";\n request: InvokeRequest;\n response: InvokeResponse;\n payload<T = Record<string, any>>(): T;\n}\n//#endregion\n\n//#region [WebSocket]\nexport type WebSocketRequest = {\n connectionId: string,\n event: WebSocketEvent,\n route?: string,\n url?: string,\n params: Record<string, string | undefined> | null,\n body?: string | Record<string, any> | null,\n headers?: Record<string, string | string[] | undefined>,\n queryString: Record<string, string | undefined> | null,\n requestId?: string,\n moduleName?: string,\n}\n\nexport type WebSocketResponse = {\n status: \"success\" | \"error\",\n statusCode?: number,\n body?: any,\n}\n\nexport type WebSocketFunctionContext = FunctionContext & {\n type: \"WebSocket\";\n connectionId: string;\n event: WebSocketEvent;\n request: WebSocketRequest;\n response: WebSocketResponse;\n message<T = Record<string, any>>(): T;\n params<T = Record<string, string>>(): T;\n unauthorized(body?: any): void;\n forbidden(body?: any): void;\n validationError(details: Record<string, any>): void;\n}\n//#endregion\n\nexport interface FunctionsService extends IService {\n createHttRequest(...args: any[]): [HTTPRequest];\n createScheduleRequest(...args: any[]): [ScheduleRequest];\n createEventRequest(...args: any[]): [EventRequest];\n createWebSocketRequest(...args: any[]): [WebSocketRequest];\n createInvokeRequest(...args: any[]): [InvokeRequest];\n\n buildHttpResponse(httpFunctionContext: HttpFunctionContext, ...args: any[]): any\n buildScheduleResponse(scheduleFunctionContext: ScheduleFunctionContext, ...args: any[]): any;\n buildEventResponse(eventFunctionContext: EventFunctionContext, ...args: any[]): Promise<EventBatchItemResult>;\n buildWebSocketResponse(webSocketFunctionContext: WebSocketFunctionContext, ...args: any[]): any;\n buildInvokeResponse(invokeFunctionContext: InvokeFunctionContext, ...args: any[]): any;\n}","import { CookieOptions } from \"../services/functions\";\n\n/**\n * Serializes a single cookie into a `Set-Cookie` header value.\n * Throws if `sameSite` is \"None\" without `secure` set, since browsers reject that combination.\n */\nexport function serializeCookie(name: string, value: string, options: CookieOptions = {}): string {\n const {\n path = \"/\",\n domain,\n maxAge,\n expires,\n httpOnly = true,\n secure = true,\n sameSite = \"Lax\",\n } = options;\n\n if (sameSite === \"None\" && !secure) {\n throw new Error(`Cookie \"${name}\" sets SameSite=None but not Secure; browsers require Secure with SameSite=None`);\n }\n\n const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];\n parts.push(`Path=${path}`);\n if (domain) parts.push(`Domain=${domain}`);\n if (maxAge !== undefined) parts.push(`Max-Age=${Math.trunc(maxAge)}`);\n if (expires) parts.push(`Expires=${expires.toUTCString()}`);\n if (httpOnly) parts.push(\"HttpOnly\");\n if (secure) parts.push(\"Secure\");\n parts.push(`SameSite=${sameSite}`);\n\n return parts.join(\"; \");\n}\n\n/**\n * Parses a raw `Cookie` request header (e.g. \"a=1; b=2\") into a name/value map.\n */\nexport function parseCookieHeader(header?: string | null): Record<string, string> {\n const cookies: Record<string, string> = {};\n if (!header) return cookies;\n\n for (const pair of header.split(\";\")) {\n const idx = pair.indexOf(\"=\");\n if (idx === -1) continue;\n\n const name = pair.slice(0, idx).trim();\n const value = pair.slice(idx + 1).trim();\n if (!name) continue;\n\n try {\n cookies[name] = decodeURIComponent(value);\n } catch {\n cookies[name] = value;\n }\n }\n\n return cookies;\n}\n","/**\n * Thrown when a sendToClient/disconnect is attempted against a connectionId\n * that is no longer connected (AWS API Gateway GoneException equivalent, HTTP 410).\n */\nexport class WebSocketConnectionGoneError extends Error {\n public readonly connectionId: string;\n\n constructor(connectionId: string) {\n super(`WebSocket connection ${connectionId} is gone`);\n this.name = \"WebSocketConnectionGoneError\";\n this.connectionId = connectionId;\n }\n}\n\nexport interface WebSocketService {\n /**\n * Send a message to a specific connected client\n * @param connectionId The connection ID of the target client\n * @param data The data to send\n */\n sendToClient(connectionId: string, data: any): Promise<void>;\n\n /**\n * Disconnect a specific connected client\n * @param connectionId The connection ID of the client to disconnect\n */\n disconnect(connectionId: string): Promise<void>;\n}\n","export interface IService { };\nexport type ServiceKind = \"storage\" | \"location\" | \"event-broker\" | \"functions\" | \"websocket\" | \"invoke\" | \"logger\" | \"email\";\n\n\nconst _cloudContainer: Record<ServiceKind, (() => IService) | undefined> = {\n storage: undefined,\n location: undefined,\n \"event-broker\": undefined,\n functions: undefined,\n websocket: undefined,\n invoke: undefined,\n logger: undefined,\n email: undefined\n};\n\nexport function registerService(kind: ServiceKind, factory: () => IService) {\n _cloudContainer[kind] = factory;\n}\n\nexport function registerServices(services: Record<ServiceKind, () => IService>) {\n for (const kind in services) {\n if (services.hasOwnProperty(kind)) {\n _cloudContainer[kind as ServiceKind] = services[kind as ServiceKind];\n }\n }\n}\n\nexport function getService<T extends IService>(kind: ServiceKind): T {\n const service = _cloudContainer[kind];\n if (!service) {\n throw new Error(`Service of kind ${kind} is not registered.`);\n }\n return service() as T;\n}\n"],"mappings":";AA8CO,IAAK,YAAL,kBAAKA,eAAL;AAEH,EAAAA,WAAA,sBAAmB;AACnB,EAAAA,WAAA,mBAAgB;AAGhB,EAAAA,WAAA,kBAAe;AACf,EAAAA,WAAA,mBAAgB;AAGhB,EAAAA,WAAA,wBAAqB;AACrB,EAAAA,WAAA,6BAA0B;AAC1B,EAAAA,WAAA,uBAAoB;AAGpB,EAAAA,WAAA,2BAAwB;AACxB,EAAAA,WAAA,yBAAsB;AACtB,EAAAA,WAAA,oBAAiB;AAGjB,EAAAA,WAAA,6BAA0B;AAG1B,EAAAA,WAAA,wBAAqB;AAGrB,EAAAA,WAAA,mBAAgB;AA1BR,SAAAA;AAAA,GAAA;;;ACxCL,SAAS,gBAAgB,MAAc,OAAe,UAAyB,CAAC,GAAW;AAC9F,QAAM;AAAA,IACF,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,EACf,IAAI;AAEJ,MAAI,aAAa,UAAU,CAAC,QAAQ;AAChC,UAAM,IAAI,MAAM,WAAW,IAAI,iFAAiF;AAAA,EACpH;AAEA,QAAM,QAAQ,CAAC,GAAG,mBAAmB,IAAI,CAAC,IAAI,mBAAmB,KAAK,CAAC,EAAE;AACzE,QAAM,KAAK,QAAQ,IAAI,EAAE;AACzB,MAAI,OAAQ,OAAM,KAAK,UAAU,MAAM,EAAE;AACzC,MAAI,WAAW,OAAW,OAAM,KAAK,WAAW,KAAK,MAAM,MAAM,CAAC,EAAE;AACpE,MAAI,QAAS,OAAM,KAAK,WAAW,QAAQ,YAAY,CAAC,EAAE;AAC1D,MAAI,SAAU,OAAM,KAAK,UAAU;AACnC,MAAI,OAAQ,OAAM,KAAK,QAAQ;AAC/B,QAAM,KAAK,YAAY,QAAQ,EAAE;AAEjC,SAAO,MAAM,KAAK,IAAI;AAC1B;AAKO,SAAS,kBAAkB,QAAgD;AAC9E,QAAM,UAAkC,CAAC;AACzC,MAAI,CAAC,OAAQ,QAAO;AAEpB,aAAW,QAAQ,OAAO,MAAM,GAAG,GAAG;AAClC,UAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,QAAI,QAAQ,GAAI;AAEhB,UAAM,OAAO,KAAK,MAAM,GAAG,GAAG,EAAE,KAAK;AACrC,UAAM,QAAQ,KAAK,MAAM,MAAM,CAAC,EAAE,KAAK;AACvC,QAAI,CAAC,KAAM;AAEX,QAAI;AACA,cAAQ,IAAI,IAAI,mBAAmB,KAAK;AAAA,IAC5C,QAAQ;AACJ,cAAQ,IAAI,IAAI;AAAA,IACpB;AAAA,EACJ;AAEA,SAAO;AACX;;;ACpDO,IAAM,+BAAN,cAA2C,MAAM;AAAA,EACpC;AAAA,EAEhB,YAAY,cAAsB;AAC9B,UAAM,wBAAwB,YAAY,UAAU;AACpD,SAAK,OAAO;AACZ,SAAK,eAAe;AAAA,EACxB;AACJ;;;ACRA,IAAM,kBAAqE;AAAA,EACvE,SAAS;AAAA,EACT,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AACX;AAEO,SAAS,gBAAgB,MAAmB,SAAyB;AACxE,kBAAgB,IAAI,IAAI;AAC5B;AAEO,SAAS,iBAAiB,UAA+C;AAC5E,aAAW,QAAQ,UAAU;AACzB,QAAI,SAAS,eAAe,IAAI,GAAG;AAC/B,sBAAgB,IAAmB,IAAI,SAAS,IAAmB;AAAA,IACvE;AAAA,EACJ;AACJ;AAEO,SAAS,WAA+B,MAAsB;AACjE,QAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,CAAC,SAAS;AACV,UAAM,IAAI,MAAM,mBAAmB,IAAI,qBAAqB;AAAA,EAChE;AACA,SAAO,QAAQ;AACnB;","names":["ErrorCode"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudnux/core-cloud-provider",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "CloudNux core interfaces and types for multi-cloud abstraction",
5
5
  "license": "MIT",
6
6
  "type": "module",