@walkeros/server-source-gcp 0.2.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/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # @walkeros/server-source-gcp
2
+
3
+ Google Cloud Platform server sources for walkerOS - lightweight, single-purpose
4
+ runtime adapters for GCP services.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ npm install @walkeros/server-source-gcp @google-cloud/functions-framework
10
+ ```
11
+
12
+ ## Cloud Functions Source
13
+
14
+ The Cloud Functions source provides an HTTP handler that receives walker events
15
+ and forwards them to the walkerOS collector.
16
+
17
+ ### Basic Usage
18
+
19
+ ```typescript
20
+ import { sourceCloudFunction } from '@walkeros/server-source-gcp';
21
+ import { http } from '@google-cloud/functions-framework';
22
+
23
+ // Create source with collector
24
+ const source = await sourceCloudFunction(
25
+ { settings: { cors: true, batch: true } },
26
+ { elb: collector.push },
27
+ );
28
+
29
+ // Plug-and-play: source.push IS the Cloud Function handler
30
+ http('walkerHandler', source.push);
31
+ ```
32
+
33
+ ## Bundler Integration
34
+
35
+ Use with minimal config:
36
+
37
+ ```json
38
+ {
39
+ "sources": {
40
+ "api": { "type": "cloudfunction", "cors": true }
41
+ }
42
+ }
43
+ ```
44
+
45
+ Bundler auto-generates deployable exports.
46
+
47
+ ### Configuration Options
48
+
49
+ ```typescript
50
+ interface Settings {
51
+ cors?: boolean | CorsOptions; // Enable CORS (default: true)
52
+ batch?: boolean; // Enable batch processing (default: true)
53
+ maxBatchSize?: number; // Max events per batch (default: 100)
54
+ timeout?: number; // Request timeout (default: 30000ms)
55
+ }
56
+
57
+ interface CorsOptions {
58
+ origin?: string | string[]; // Allowed origins
59
+ methods?: string[]; // Allowed methods
60
+ headers?: string[]; // Allowed headers
61
+ credentials?: boolean; // Allow credentials
62
+ maxAge?: number; // Preflight cache time
63
+ }
64
+ ```
65
+
66
+ ### Request Format
67
+
68
+ **Single Event:**
69
+
70
+ ```json
71
+ {
72
+ "event": "page view",
73
+ "data": {
74
+ "title": "Home Page",
75
+ "path": "/"
76
+ },
77
+ "context": {
78
+ "stage": ["prod", 1]
79
+ }
80
+ }
81
+ ```
82
+
83
+ **Batch Events:**
84
+
85
+ ```json
86
+ {
87
+ "events": [
88
+ {
89
+ "event": "page view",
90
+ "data": { "title": "Page 1" }
91
+ },
92
+ {
93
+ "event": "button click",
94
+ "data": { "id": "btn1" }
95
+ }
96
+ ]
97
+ }
98
+ ```
99
+
100
+ ### Response Format
101
+
102
+ **Success:**
103
+
104
+ ```json
105
+ {
106
+ "success": true,
107
+ "id": "event-id-123"
108
+ }
109
+ ```
110
+
111
+ **Batch Success:**
112
+
113
+ ```json
114
+ {
115
+ "success": true,
116
+ "processed": 2,
117
+ "errors": []
118
+ }
119
+ ```
120
+
121
+ **Error:**
122
+
123
+ ```json
124
+ {
125
+ "success": false,
126
+ "error": "Invalid event format"
127
+ }
128
+ ```
129
+
130
+ ### Deployment
131
+
132
+ The source is designed to work with the walkerOS deployment system:
133
+
134
+ ```json
135
+ {
136
+ "providers": [
137
+ {
138
+ "name": "api-endpoint",
139
+ "type": "gcp-functions",
140
+ "artifact": {
141
+ "source": "bundler",
142
+ "bundle": "api-collector"
143
+ },
144
+ "settings": {
145
+ "functionName": "walker-collector",
146
+ "runtime": "nodejs18",
147
+ "memory": 256
148
+ }
149
+ }
150
+ ]
151
+ }
152
+ ```
153
+
154
+ ### Testing
155
+
156
+ The source uses environment injection for testability:
157
+
158
+ ```typescript
159
+ import { sourceCloudFunction } from '@walkeros/server-source-gcp';
160
+
161
+ const mockElb = jest.fn().mockResolvedValue({
162
+ ok: true,
163
+ event: { id: 'test-id' },
164
+ });
165
+
166
+ const source = await sourceCloudFunction(
167
+ { settings: { cors: false } },
168
+ { elb: mockElb },
169
+ );
170
+
171
+ // Test the handler
172
+ const mockReq = { method: 'POST', body: { event: 'test' } };
173
+ const mockRes = { status: jest.fn().mockReturnThis(), json: jest.fn() };
174
+
175
+ await source.push(mockReq, mockRes);
176
+
177
+ expect(mockElb).toHaveBeenCalledWith('test', {});
178
+ expect(mockRes.status).toHaveBeenCalledWith(200);
179
+ ```
180
+
181
+ ## Architecture
182
+
183
+ This source follows the walkerOS patterns:
184
+
185
+ - **Stateless**: No collector references, communicates via elb function
186
+ - **Environment Injection**: All dependencies provided through environment
187
+ - **Lean Implementation**: Minimal required fields, focused on HTTP handling
188
+ - **Standard Interface**: The `push` function IS the Cloud Function handler
189
+ - **Plug-and-Play**: Direct assignment: `http('handler', source.push)`
190
+
191
+ The source's `push` function accepts HTTP requests, transforms them into walker
192
+ events, and forwards them to the collector for processing by destinations.
@@ -0,0 +1,98 @@
1
+ import { Source, WalkerOS } from '@walkeros/core';
2
+
3
+ interface Request {
4
+ method: string;
5
+ body?: unknown;
6
+ headers: Record<string, string | string[]>;
7
+ get(name: string): string | undefined;
8
+ }
9
+ interface Response {
10
+ status(code: number): Response;
11
+ json(body: unknown): Response;
12
+ send(body?: unknown): Response;
13
+ set(key: string, value: string): Response;
14
+ }
15
+ interface Settings {
16
+ cors?: boolean | CorsOptions;
17
+ timeout?: number;
18
+ }
19
+ interface CorsOptions {
20
+ origin?: string | string[];
21
+ methods?: string[];
22
+ headers?: string[];
23
+ credentials?: boolean;
24
+ maxAge?: number;
25
+ }
26
+ interface Mapping {
27
+ }
28
+ type Push = (req: Request, res: Response) => Promise<void>;
29
+ interface Env extends Source.Env {
30
+ req?: Request;
31
+ res?: Response;
32
+ }
33
+ type Types = Source.Types<Settings, Mapping, Push, Env>;
34
+ interface CloudFunctionSource extends Omit<Source.Instance<Types>, 'push'> {
35
+ push: Push;
36
+ }
37
+ type PartialConfig = Source.PartialConfig<Types>;
38
+ interface EventRequest {
39
+ event: string;
40
+ data?: WalkerOS.AnyObject;
41
+ context?: WalkerOS.AnyObject;
42
+ user?: WalkerOS.AnyObject;
43
+ globals?: WalkerOS.AnyObject;
44
+ consent?: WalkerOS.AnyObject;
45
+ }
46
+ interface EventResponse {
47
+ success: boolean;
48
+ id?: string;
49
+ error?: string;
50
+ }
51
+ type RequestBody = EventRequest;
52
+ type ResponseBody = EventResponse;
53
+
54
+ type types_CloudFunctionSource = CloudFunctionSource;
55
+ type types_CorsOptions = CorsOptions;
56
+ type types_Env = Env;
57
+ type types_EventRequest = EventRequest;
58
+ type types_EventResponse = EventResponse;
59
+ type types_Mapping = Mapping;
60
+ type types_PartialConfig = PartialConfig;
61
+ type types_Push = Push;
62
+ type types_Request = Request;
63
+ type types_RequestBody = RequestBody;
64
+ type types_Response = Response;
65
+ type types_ResponseBody = ResponseBody;
66
+ type types_Settings = Settings;
67
+ type types_Types = Types;
68
+ declare namespace types {
69
+ export type { types_CloudFunctionSource as CloudFunctionSource, types_CorsOptions as CorsOptions, types_Env as Env, types_EventRequest as EventRequest, types_EventResponse as EventResponse, types_Mapping as Mapping, types_PartialConfig as PartialConfig, types_Push as Push, types_Request as Request, types_RequestBody as RequestBody, types_Response as Response, types_ResponseBody as ResponseBody, types_Settings as Settings, types_Types as Types };
70
+ }
71
+
72
+ /**
73
+ * Example environment configurations for GCP Cloud Function source
74
+ *
75
+ * These environments provide standardized mock structures for testing
76
+ * HTTP request handling without requiring actual cloud function deployment.
77
+ */
78
+ /**
79
+ * Standard mock environment for testing cloud function source
80
+ *
81
+ * Use this for testing HTTP event ingestion and request/response handling
82
+ * without requiring a real GCP Cloud Function environment.
83
+ */
84
+ declare const push: Env;
85
+
86
+ declare const env_push: typeof push;
87
+ declare namespace env {
88
+ export { env_push as push };
89
+ }
90
+
91
+ declare const index_env: typeof env;
92
+ declare namespace index {
93
+ export { index_env as env };
94
+ }
95
+
96
+ declare const sourceCloudFunction: (config: Partial<Source.Config<Types>> | undefined, env: Env) => Promise<CloudFunctionSource>;
97
+
98
+ export { types as SourceCloudFunction, index as examples, sourceCloudFunction };
@@ -0,0 +1,98 @@
1
+ import { Source, WalkerOS } from '@walkeros/core';
2
+
3
+ interface Request {
4
+ method: string;
5
+ body?: unknown;
6
+ headers: Record<string, string | string[]>;
7
+ get(name: string): string | undefined;
8
+ }
9
+ interface Response {
10
+ status(code: number): Response;
11
+ json(body: unknown): Response;
12
+ send(body?: unknown): Response;
13
+ set(key: string, value: string): Response;
14
+ }
15
+ interface Settings {
16
+ cors?: boolean | CorsOptions;
17
+ timeout?: number;
18
+ }
19
+ interface CorsOptions {
20
+ origin?: string | string[];
21
+ methods?: string[];
22
+ headers?: string[];
23
+ credentials?: boolean;
24
+ maxAge?: number;
25
+ }
26
+ interface Mapping {
27
+ }
28
+ type Push = (req: Request, res: Response) => Promise<void>;
29
+ interface Env extends Source.Env {
30
+ req?: Request;
31
+ res?: Response;
32
+ }
33
+ type Types = Source.Types<Settings, Mapping, Push, Env>;
34
+ interface CloudFunctionSource extends Omit<Source.Instance<Types>, 'push'> {
35
+ push: Push;
36
+ }
37
+ type PartialConfig = Source.PartialConfig<Types>;
38
+ interface EventRequest {
39
+ event: string;
40
+ data?: WalkerOS.AnyObject;
41
+ context?: WalkerOS.AnyObject;
42
+ user?: WalkerOS.AnyObject;
43
+ globals?: WalkerOS.AnyObject;
44
+ consent?: WalkerOS.AnyObject;
45
+ }
46
+ interface EventResponse {
47
+ success: boolean;
48
+ id?: string;
49
+ error?: string;
50
+ }
51
+ type RequestBody = EventRequest;
52
+ type ResponseBody = EventResponse;
53
+
54
+ type types_CloudFunctionSource = CloudFunctionSource;
55
+ type types_CorsOptions = CorsOptions;
56
+ type types_Env = Env;
57
+ type types_EventRequest = EventRequest;
58
+ type types_EventResponse = EventResponse;
59
+ type types_Mapping = Mapping;
60
+ type types_PartialConfig = PartialConfig;
61
+ type types_Push = Push;
62
+ type types_Request = Request;
63
+ type types_RequestBody = RequestBody;
64
+ type types_Response = Response;
65
+ type types_ResponseBody = ResponseBody;
66
+ type types_Settings = Settings;
67
+ type types_Types = Types;
68
+ declare namespace types {
69
+ export type { types_CloudFunctionSource as CloudFunctionSource, types_CorsOptions as CorsOptions, types_Env as Env, types_EventRequest as EventRequest, types_EventResponse as EventResponse, types_Mapping as Mapping, types_PartialConfig as PartialConfig, types_Push as Push, types_Request as Request, types_RequestBody as RequestBody, types_Response as Response, types_ResponseBody as ResponseBody, types_Settings as Settings, types_Types as Types };
70
+ }
71
+
72
+ /**
73
+ * Example environment configurations for GCP Cloud Function source
74
+ *
75
+ * These environments provide standardized mock structures for testing
76
+ * HTTP request handling without requiring actual cloud function deployment.
77
+ */
78
+ /**
79
+ * Standard mock environment for testing cloud function source
80
+ *
81
+ * Use this for testing HTTP event ingestion and request/response handling
82
+ * without requiring a real GCP Cloud Function environment.
83
+ */
84
+ declare const push: Env;
85
+
86
+ declare const env_push: typeof push;
87
+ declare namespace env {
88
+ export { env_push as push };
89
+ }
90
+
91
+ declare const index_env: typeof env;
92
+ declare namespace index {
93
+ export { index_env as env };
94
+ }
95
+
96
+ declare const sourceCloudFunction: (config: Partial<Source.Config<Types>> | undefined, env: Env) => Promise<CloudFunctionSource>;
97
+
98
+ export { types as SourceCloudFunction, index as examples, sourceCloudFunction };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var e,r=Object.defineProperty,t=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.prototype.hasOwnProperty,n=(e,t)=>{for(var o in t)r(e,o,{get:t[o],enumerable:!0})},c={};n(c,{SourceCloudFunction:()=>i,examples:()=>u,sourceCloudFunction:()=>A}),module.exports=(e=c,((e,n,c,i)=>{if(n&&"object"==typeof n||"function"==typeof n)for(let u of o(n))s.call(e,u)||u===c||r(e,u,{get:()=>n[u],enumerable:!(i=t(n,u))||i.enumerable});return e})(r({},"__esModule",{value:!0}),e));var i={},u={};n(u,{env:()=>a});var a={};n(a,{push:()=>l});var l={elb:jest.fn()},d={cors:!0,timeout:3e4},f=async(e={},r)=>{const{elb:t}=r;if(!t)throw new Error("Cloud Function source requires elb function in environment");const o={...d,...e.settings||{}};return{type:"cloudfunction",config:{...e,settings:o},push:async(e,r)=>{try{if(function(e,r){if(r)if(!0===r)e.set("Access-Control-Allow-Origin","*"),e.set("Access-Control-Allow-Methods","POST, OPTIONS"),e.set("Access-Control-Allow-Headers","Content-Type, Authorization"),e.set("Access-Control-Max-Age","3600");else{if(r.origin){const t=Array.isArray(r.origin)?r.origin.join(", "):r.origin;e.set("Access-Control-Allow-Origin",t)}r.methods&&e.set("Access-Control-Allow-Methods",r.methods.join(", ")),r.headers&&e.set("Access-Control-Allow-Headers",r.headers.join(", ")),r.credentials&&e.set("Access-Control-Allow-Credentials","true"),void 0!==r.maxAge&&e.set("Access-Control-Max-Age",r.maxAge.toString())}}(r,o.cors||!1),"OPTIONS"===e.method)return void r.status(204).send();if("POST"!==e.method)return void r.status(405).json({success:!1,error:"Method not allowed. Use POST."});if(!e.body)return void r.status(400).json({success:!1,error:"Request body is required"});const s=e.body;if(function(e){return"event"in e&&"string"==typeof e.event}(s)){const e=await async function(e,r){var t;try{const o=await r(e.event,e.data||{});return{id:null==(t=null==o?void 0:o.event)?void 0:t.id}}catch(e){return{error:e instanceof Error?e.message:"Unknown error"}}}(s,t);e.error?r.status(400).json({success:!1,error:e.error}):r.status(200).json({success:!0,id:e.id})}else r.status(400).json({success:!1,error:"Invalid request format. Expected event object."})}catch(e){r.status(500).json({success:!1,error:e instanceof Error?e.message:"Internal server error"})}}}},A=f;//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/cloudfunction/utils.ts","../src/cloudfunction/push.ts","../src/cloudfunction/types.ts","../src/cloudfunction/examples/index.ts","../src/cloudfunction/examples/env.ts","../src/cloudfunction/index.ts"],"sourcesContent":["export * from './cloudfunction';\nexport { default as sourceCloudFunction } from './cloudfunction';\n","import type { RequestBody, EventRequest, CorsOptions, Response } from './types';\n\nexport function isEventRequest(body: RequestBody): body is EventRequest {\n return 'event' in body && typeof body.event === 'string';\n}\n\nexport function setCorsHeaders(\n res: Response,\n corsOptions: boolean | CorsOptions,\n): void {\n if (!corsOptions) return;\n\n if (corsOptions === true) {\n res.set('Access-Control-Allow-Origin', '*');\n res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');\n res.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');\n res.set('Access-Control-Max-Age', '3600');\n } else {\n if (corsOptions.origin) {\n const origin = Array.isArray(corsOptions.origin)\n ? corsOptions.origin.join(', ')\n : corsOptions.origin;\n res.set('Access-Control-Allow-Origin', origin);\n }\n if (corsOptions.methods) {\n res.set('Access-Control-Allow-Methods', corsOptions.methods.join(', '));\n }\n if (corsOptions.headers) {\n res.set('Access-Control-Allow-Headers', corsOptions.headers.join(', '));\n }\n if (corsOptions.credentials) {\n res.set('Access-Control-Allow-Credentials', 'true');\n }\n if (corsOptions.maxAge !== undefined) {\n res.set('Access-Control-Max-Age', corsOptions.maxAge.toString());\n }\n }\n}\n","import type { Elb, WalkerOS } from '@walkeros/core';\nimport type { EventRequest } from './types';\n\nexport async function processEvent(\n eventReq: EventRequest,\n elb: Elb.Fn,\n): Promise<{ id?: string; error?: string }> {\n try {\n const result = await elb(\n eventReq.event,\n (eventReq.data || {}) as WalkerOS.Properties,\n );\n\n return { id: result?.event?.id };\n } catch (error) {\n return { error: error instanceof Error ? error.message : 'Unknown error' };\n }\n}\n","import type { WalkerOS, Source as CoreSource } from '@walkeros/core';\n\n// Minimal request/response interfaces\nexport interface Request {\n method: string;\n body?: unknown;\n headers: Record<string, string | string[]>;\n get(name: string): string | undefined;\n}\n\nexport interface Response {\n status(code: number): Response;\n json(body: unknown): Response;\n send(body?: unknown): Response;\n set(key: string, value: string): Response;\n}\n\nexport interface Settings {\n cors?: boolean | CorsOptions;\n timeout?: number;\n}\n\nexport interface CorsOptions {\n origin?: string | string[];\n methods?: string[];\n headers?: string[];\n credentials?: boolean;\n maxAge?: number;\n}\n\nexport interface Mapping {\n // Custom source event mapping properties\n}\n\n// CloudFunction-specific push type\nexport type Push = (req: Request, res: Response) => Promise<void>;\n\nexport interface Env extends CoreSource.Env {\n req?: Request;\n res?: Response;\n}\n\n// Type bundle (must be after Settings, Mapping, Push, Env are defined)\nexport type Types = CoreSource.Types<Settings, Mapping, Push, Env>;\n\nexport interface CloudFunctionSource\n extends Omit<CoreSource.Instance<Types>, 'push'> {\n push: Push;\n}\n\n// Removed custom Config type - using Source.Config<Types> directly\nexport type PartialConfig = CoreSource.PartialConfig<Types>;\n\n// Cloud function source doesn't follow standard Source.Init pattern due to HTTP handler interface\n\nexport interface EventRequest {\n event: string;\n data?: WalkerOS.AnyObject;\n context?: WalkerOS.AnyObject;\n user?: WalkerOS.AnyObject;\n globals?: WalkerOS.AnyObject;\n consent?: WalkerOS.AnyObject;\n}\n\nexport interface EventResponse {\n success: boolean;\n id?: string;\n error?: string;\n}\n\nexport type RequestBody = EventRequest;\nexport type ResponseBody = EventResponse;\n","export * as env from './env';\n","import type { Env } from '../types';\n\n/**\n * Example environment configurations for GCP Cloud Function source\n *\n * These environments provide standardized mock structures for testing\n * HTTP request handling without requiring actual cloud function deployment.\n */\n\n/**\n * Standard mock environment for testing cloud function source\n *\n * Use this for testing HTTP event ingestion and request/response handling\n * without requiring a real GCP Cloud Function environment.\n */\nexport const push: Env = {\n elb: jest.fn(),\n};\n","import type {\n Env,\n CloudFunctionSource,\n Settings,\n EventResponse,\n RequestBody,\n Request,\n Response,\n Mapping,\n Types,\n} from './types';\nimport type { Source } from '@walkeros/core';\nimport { isEventRequest, setCorsHeaders } from './utils';\nimport { processEvent } from './push';\n\nexport * as SourceCloudFunction from './types';\n\n// Export examples\nexport * as examples from './examples';\n\nconst DEFAULT_SETTINGS: Settings = {\n cors: true,\n timeout: 30000,\n};\n\nexport const sourceCloudFunction = async (\n config: Partial<Source.Config<Types>> = {},\n env: Env,\n): Promise<CloudFunctionSource> => {\n const { elb } = env;\n\n if (!elb) {\n throw new Error(\n 'Cloud Function source requires elb function in environment',\n );\n }\n\n const settings: Settings = {\n ...DEFAULT_SETTINGS,\n ...(config.settings || {}),\n };\n\n const fullConfig: Source.Config<Types> = {\n ...config,\n settings,\n };\n\n const push = async (req: Request, res: Response): Promise<void> => {\n try {\n setCorsHeaders(res, settings.cors || false);\n\n if (req.method === 'OPTIONS') {\n res.status(204).send();\n return;\n }\n\n if (req.method !== 'POST') {\n res.status(405).json({\n success: false,\n error: 'Method not allowed. Use POST.',\n });\n return;\n }\n\n if (!req.body) {\n res.status(400).json({\n success: false,\n error: 'Request body is required',\n });\n return;\n }\n\n const body = req.body as RequestBody;\n\n if (isEventRequest(body)) {\n const result = await processEvent(body, elb);\n\n if (result.error) {\n res.status(400).json({\n success: false,\n error: result.error,\n } as EventResponse);\n } else {\n res.status(200).json({\n success: true,\n id: result.id,\n } as EventResponse);\n }\n } else {\n res.status(400).json({\n success: false,\n error: 'Invalid request format. Expected event object.',\n });\n }\n } catch (error) {\n res.status(500).json({\n success: false,\n error: error instanceof Error ? error.message : 'Internal server error',\n });\n }\n };\n\n return {\n type: 'cloudfunction',\n config: fullConfig,\n push,\n };\n};\n\nexport default sourceCloudFunction;\n"],"mappings":"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","names":["push"]}
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,s=(s,r)=>{for(var o in r)e(s,o,{get:r[o],enumerable:!0})};var r={},o={};s(o,{env:()=>t});var t={};s(t,{push:()=>n});var n={elb:jest.fn()},i={cors:!0,timeout:3e4},c=async(e={},s)=>{const{elb:r}=s;if(!r)throw new Error("Cloud Function source requires elb function in environment");const o={...i,...e.settings||{}};return{type:"cloudfunction",config:{...e,settings:o},push:async(e,s)=>{try{if(function(e,s){if(s)if(!0===s)e.set("Access-Control-Allow-Origin","*"),e.set("Access-Control-Allow-Methods","POST, OPTIONS"),e.set("Access-Control-Allow-Headers","Content-Type, Authorization"),e.set("Access-Control-Max-Age","3600");else{if(s.origin){const r=Array.isArray(s.origin)?s.origin.join(", "):s.origin;e.set("Access-Control-Allow-Origin",r)}s.methods&&e.set("Access-Control-Allow-Methods",s.methods.join(", ")),s.headers&&e.set("Access-Control-Allow-Headers",s.headers.join(", ")),s.credentials&&e.set("Access-Control-Allow-Credentials","true"),void 0!==s.maxAge&&e.set("Access-Control-Max-Age",s.maxAge.toString())}}(s,o.cors||!1),"OPTIONS"===e.method)return void s.status(204).send();if("POST"!==e.method)return void s.status(405).json({success:!1,error:"Method not allowed. Use POST."});if(!e.body)return void s.status(400).json({success:!1,error:"Request body is required"});const t=e.body;if(function(e){return"event"in e&&"string"==typeof e.event}(t)){const e=await async function(e,s){var r;try{const o=await s(e.event,e.data||{});return{id:null==(r=null==o?void 0:o.event)?void 0:r.id}}catch(e){return{error:e instanceof Error?e.message:"Unknown error"}}}(t,r);e.error?s.status(400).json({success:!1,error:e.error}):s.status(200).json({success:!0,id:e.id})}else s.status(400).json({success:!1,error:"Invalid request format. Expected event object."})}catch(e){s.status(500).json({success:!1,error:e instanceof Error?e.message:"Internal server error"})}}}};export{r as SourceCloudFunction,o as examples,c as sourceCloudFunction};//# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cloudfunction/utils.ts","../src/cloudfunction/push.ts","../src/cloudfunction/types.ts","../src/cloudfunction/examples/index.ts","../src/cloudfunction/examples/env.ts","../src/cloudfunction/index.ts"],"sourcesContent":["import type { RequestBody, EventRequest, CorsOptions, Response } from './types';\n\nexport function isEventRequest(body: RequestBody): body is EventRequest {\n return 'event' in body && typeof body.event === 'string';\n}\n\nexport function setCorsHeaders(\n res: Response,\n corsOptions: boolean | CorsOptions,\n): void {\n if (!corsOptions) return;\n\n if (corsOptions === true) {\n res.set('Access-Control-Allow-Origin', '*');\n res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');\n res.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');\n res.set('Access-Control-Max-Age', '3600');\n } else {\n if (corsOptions.origin) {\n const origin = Array.isArray(corsOptions.origin)\n ? corsOptions.origin.join(', ')\n : corsOptions.origin;\n res.set('Access-Control-Allow-Origin', origin);\n }\n if (corsOptions.methods) {\n res.set('Access-Control-Allow-Methods', corsOptions.methods.join(', '));\n }\n if (corsOptions.headers) {\n res.set('Access-Control-Allow-Headers', corsOptions.headers.join(', '));\n }\n if (corsOptions.credentials) {\n res.set('Access-Control-Allow-Credentials', 'true');\n }\n if (corsOptions.maxAge !== undefined) {\n res.set('Access-Control-Max-Age', corsOptions.maxAge.toString());\n }\n }\n}\n","import type { Elb, WalkerOS } from '@walkeros/core';\nimport type { EventRequest } from './types';\n\nexport async function processEvent(\n eventReq: EventRequest,\n elb: Elb.Fn,\n): Promise<{ id?: string; error?: string }> {\n try {\n const result = await elb(\n eventReq.event,\n (eventReq.data || {}) as WalkerOS.Properties,\n );\n\n return { id: result?.event?.id };\n } catch (error) {\n return { error: error instanceof Error ? error.message : 'Unknown error' };\n }\n}\n","import type { WalkerOS, Source as CoreSource } from '@walkeros/core';\n\n// Minimal request/response interfaces\nexport interface Request {\n method: string;\n body?: unknown;\n headers: Record<string, string | string[]>;\n get(name: string): string | undefined;\n}\n\nexport interface Response {\n status(code: number): Response;\n json(body: unknown): Response;\n send(body?: unknown): Response;\n set(key: string, value: string): Response;\n}\n\nexport interface Settings {\n cors?: boolean | CorsOptions;\n timeout?: number;\n}\n\nexport interface CorsOptions {\n origin?: string | string[];\n methods?: string[];\n headers?: string[];\n credentials?: boolean;\n maxAge?: number;\n}\n\nexport interface Mapping {\n // Custom source event mapping properties\n}\n\n// CloudFunction-specific push type\nexport type Push = (req: Request, res: Response) => Promise<void>;\n\nexport interface Env extends CoreSource.Env {\n req?: Request;\n res?: Response;\n}\n\n// Type bundle (must be after Settings, Mapping, Push, Env are defined)\nexport type Types = CoreSource.Types<Settings, Mapping, Push, Env>;\n\nexport interface CloudFunctionSource\n extends Omit<CoreSource.Instance<Types>, 'push'> {\n push: Push;\n}\n\n// Removed custom Config type - using Source.Config<Types> directly\nexport type PartialConfig = CoreSource.PartialConfig<Types>;\n\n// Cloud function source doesn't follow standard Source.Init pattern due to HTTP handler interface\n\nexport interface EventRequest {\n event: string;\n data?: WalkerOS.AnyObject;\n context?: WalkerOS.AnyObject;\n user?: WalkerOS.AnyObject;\n globals?: WalkerOS.AnyObject;\n consent?: WalkerOS.AnyObject;\n}\n\nexport interface EventResponse {\n success: boolean;\n id?: string;\n error?: string;\n}\n\nexport type RequestBody = EventRequest;\nexport type ResponseBody = EventResponse;\n","export * as env from './env';\n","import type { Env } from '../types';\n\n/**\n * Example environment configurations for GCP Cloud Function source\n *\n * These environments provide standardized mock structures for testing\n * HTTP request handling without requiring actual cloud function deployment.\n */\n\n/**\n * Standard mock environment for testing cloud function source\n *\n * Use this for testing HTTP event ingestion and request/response handling\n * without requiring a real GCP Cloud Function environment.\n */\nexport const push: Env = {\n elb: jest.fn(),\n};\n","import type {\n Env,\n CloudFunctionSource,\n Settings,\n EventResponse,\n RequestBody,\n Request,\n Response,\n Mapping,\n Types,\n} from './types';\nimport type { Source } from '@walkeros/core';\nimport { isEventRequest, setCorsHeaders } from './utils';\nimport { processEvent } from './push';\n\nexport * as SourceCloudFunction from './types';\n\n// Export examples\nexport * as examples from './examples';\n\nconst DEFAULT_SETTINGS: Settings = {\n cors: true,\n timeout: 30000,\n};\n\nexport const sourceCloudFunction = async (\n config: Partial<Source.Config<Types>> = {},\n env: Env,\n): Promise<CloudFunctionSource> => {\n const { elb } = env;\n\n if (!elb) {\n throw new Error(\n 'Cloud Function source requires elb function in environment',\n );\n }\n\n const settings: Settings = {\n ...DEFAULT_SETTINGS,\n ...(config.settings || {}),\n };\n\n const fullConfig: Source.Config<Types> = {\n ...config,\n settings,\n };\n\n const push = async (req: Request, res: Response): Promise<void> => {\n try {\n setCorsHeaders(res, settings.cors || false);\n\n if (req.method === 'OPTIONS') {\n res.status(204).send();\n return;\n }\n\n if (req.method !== 'POST') {\n res.status(405).json({\n success: false,\n error: 'Method not allowed. Use POST.',\n });\n return;\n }\n\n if (!req.body) {\n res.status(400).json({\n success: false,\n error: 'Request body is required',\n });\n return;\n }\n\n const body = req.body as RequestBody;\n\n if (isEventRequest(body)) {\n const result = await processEvent(body, elb);\n\n if (result.error) {\n res.status(400).json({\n success: false,\n error: result.error,\n } as EventResponse);\n } else {\n res.status(200).json({\n success: true,\n id: result.id,\n } as EventResponse);\n }\n } else {\n res.status(400).json({\n success: false,\n error: 'Invalid request format. Expected event object.',\n });\n }\n } catch (error) {\n res.status(500).json({\n success: false,\n error: error instanceof Error ? error.message : 'Internal server error',\n });\n }\n };\n\n return {\n type: 'cloudfunction',\n config: fullConfig,\n push,\n };\n};\n\nexport default sourceCloudFunction;\n"],"mappings":"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","names":["push"]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@walkeros/server-source-gcp",
3
+ "description": "Google Cloud Platform server sources for walkerOS (Cloud Functions)",
4
+ "version": "0.2.0",
5
+ "license": "MIT",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist/**"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup --silent",
14
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
15
+ "dev": "jest --watchAll --colors",
16
+ "lint": "tsc && eslint \"**/*.ts*\"",
17
+ "test": "jest",
18
+ "update": "npx npm-check-updates -u && npm update"
19
+ },
20
+ "dependencies": {
21
+ "@walkeros/core": "0.2.0"
22
+ },
23
+ "peerDependencies": {
24
+ "@google-cloud/functions-framework": "^3.0.0"
25
+ },
26
+ "devDependencies": {},
27
+ "repository": {
28
+ "url": "git+https://github.com/elbwalker/walkerOS.git",
29
+ "directory": "packages/server/sources/gcp"
30
+ },
31
+ "author": "elbwalker <hello@elbwalker.com>",
32
+ "homepage": "https://github.com/elbwalker/walkerOS#readme",
33
+ "bugs": {
34
+ "url": "https://github.com/elbwalker/walkerOS/issues"
35
+ },
36
+ "keywords": [
37
+ "walker",
38
+ "walkerOS",
39
+ "source",
40
+ "server",
41
+ "gcp",
42
+ "cloud functions"
43
+ ],
44
+ "funding": [
45
+ {
46
+ "type": "GitHub Sponsors",
47
+ "url": "https://github.com/sponsors/elbwalker"
48
+ }
49
+ ]
50
+ }