@product-intelligence-hub/sdk-node 0.3.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 +104 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +76 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# @product-intelligence-hub/sdk-node
|
|
2
|
+
|
|
3
|
+
Node.js Server SDK for [Product Intelligence Hub](https://github.com/product-intelligence-hub/product-intelligence-hub).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @product-intelligence-hub/sdk-node
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @product-intelligence-hub/sdk-node
|
|
11
|
+
# or
|
|
12
|
+
yarn add @product-intelligence-hub/sdk-node
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import PIHNode from "@product-intelligence-hub/sdk-node";
|
|
19
|
+
|
|
20
|
+
// Initialize (once at server startup)
|
|
21
|
+
const pih = PIHNode.init({
|
|
22
|
+
apiKey: "your-environment-api-key",
|
|
23
|
+
projectId: "your-project-id",
|
|
24
|
+
environment: "prod",
|
|
25
|
+
tenantId: "tenant_abc", // optional, can also use setTenant()
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Track an event
|
|
29
|
+
pih.capture("user-42", "purchase_completed", {
|
|
30
|
+
amount: 99.99,
|
|
31
|
+
currency: "USD",
|
|
32
|
+
plan: "pro",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Identify a user with traits
|
|
36
|
+
await pih.identify("user-42", {
|
|
37
|
+
name: "Jane Doe",
|
|
38
|
+
email: "jane@example.com",
|
|
39
|
+
plan: "pro",
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Force flush queued events
|
|
43
|
+
await pih.flush();
|
|
44
|
+
|
|
45
|
+
// Graceful shutdown (flush + cleanup)
|
|
46
|
+
await pih.shutdown();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API
|
|
50
|
+
|
|
51
|
+
### `PIHNode.init(config)`
|
|
52
|
+
|
|
53
|
+
Initialize the SDK singleton. Returns a `NodePIHClient` instance.
|
|
54
|
+
|
|
55
|
+
| Option | Type | Default | Description |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| `apiKey` | `string` | **required** | Environment API key |
|
|
58
|
+
| `projectId` | `string` | **required** | Project identifier |
|
|
59
|
+
| `environment` | `"dev" \| "staging" \| "prod"` | **required** | Environment name |
|
|
60
|
+
| `tenantId` | `string` | `""` | Tenant identifier |
|
|
61
|
+
| `apiUrl` | `string` | `"https://repoingest-production.up.railway.app"` | Ingest API URL |
|
|
62
|
+
| `appVersion` | `string` | `undefined` | Application version |
|
|
63
|
+
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
64
|
+
| `flushAt` | `number` | `20` | Events before auto-flush |
|
|
65
|
+
| `flushInterval` | `number` | `10000` | Ms between auto-flushes |
|
|
66
|
+
|
|
67
|
+
### `client.capture(distinctId, event, properties?)`
|
|
68
|
+
|
|
69
|
+
Track a server-side event. Queues the event internally (non-blocking).
|
|
70
|
+
|
|
71
|
+
### `client.identify(userId, traits?)`
|
|
72
|
+
|
|
73
|
+
Identify a user and merge traits. Sends an identify call to the server.
|
|
74
|
+
|
|
75
|
+
### `client.flush()`
|
|
76
|
+
|
|
77
|
+
Force flush all queued events to the ingest API.
|
|
78
|
+
|
|
79
|
+
### `client.shutdown()`
|
|
80
|
+
|
|
81
|
+
Graceful shutdown: flushes the queue, detaches process listeners, and destroys the client. Call this before your server process exits.
|
|
82
|
+
|
|
83
|
+
### `PIHNode.getInstance()`
|
|
84
|
+
|
|
85
|
+
Returns the current singleton instance, or `null` if not initialized.
|
|
86
|
+
|
|
87
|
+
### `PIHNode.resetInstance()`
|
|
88
|
+
|
|
89
|
+
Destroys and resets the singleton. Useful for testing.
|
|
90
|
+
|
|
91
|
+
## Graceful Shutdown
|
|
92
|
+
|
|
93
|
+
The SDK automatically listens for the `beforeExit` process event and flushes remaining events. For explicit control, call `shutdown()` in your server's cleanup handler:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
process.on("SIGTERM", async () => {
|
|
97
|
+
await pih.shutdown();
|
|
98
|
+
process.exit(0);
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';Object.defineProperty(exports,'__esModule',{value:true});var sdkCore=require('@product-intelligence-hub/sdk-core');var r=class{store=new Map;async get(e){return this.store.get(e)??null}async set(e,i){this.store.set(e,i);}async remove(e){this.store.delete(e);}};var a="0.3.0",s=class extends sdkCore.PIHClient{processListenerAttached=false;shutdownInProgress=false;boundBeforeExit;constructor(e){let i={...e,platform:"server",flushAt:e.flushAt??20};super(i,new r),this.transport.setSDKMeta({name:"pih-sdk-node",version:a}),this.boundBeforeExit=()=>{this.flush().catch(o=>{this.log("beforeExit flush error:",o);});};}async initialize(){await super.initialize(),this.attachProcessListeners();}capture(e,i,o){let d={event_id:sdkCore.generateUUID(),timestamp:sdkCore.getTimestamp(),project_id:this.config.projectId,environment:this.config.environment,tenant_id:this.tenantId,event_name:i,anonymous_id:this.identity.getAnonymousId(),user_id:e,session_id:null,platform:"server",app_version:this.config.appVersion??null,properties:o??{},user_traits:this.identity.getUserTraits(),context:this.getContext()};this.queue.enqueue(d).catch(c=>{this.log("capture enqueue error:",c);});}async identify(e,i){await this.ensureInitialized(),await super.identify(e,i);}getContext(){return {library:{name:"pih-sdk-node",version:a},runtime:"node",nodeVersion:process.version}}async flush(){await this.ensureInitialized(),await this.queue.flush();}async shutdown(){if(!this.shutdownInProgress){this.shutdownInProgress=true;try{await this.flush();}catch(e){this.log("shutdown flush error:",e);}this.detachProcessListeners(),this.destroy();}}log(...e){this.config.debug&&console.log("[PIH Node]",...e);}attachProcessListeners(){this.processListenerAttached||(process.on("beforeExit",this.boundBeforeExit),this.processListenerAttached=true);}detachProcessListeners(){this.processListenerAttached&&(process.removeListener("beforeExit",this.boundBeforeExit),this.processListenerAttached=false);}},t=null;function p(n){return t?(console.warn("[PIH Node] SDK already initialized. Returning existing instance."),t):(t=new s(n),t.initialize().catch(e=>{console.error("[PIH Node] Failed to initialize:",e);}),t)}function f(){return t}function g(){t&&(t.destroy(),t=null);}var v={init:p,getInstance:f,resetInstance:g,NodePIHClient:s},y=v;exports.MemoryStorage=r;exports.NodePIHClient=s;exports.default=y;exports.getInstance=f;exports.init=p;exports.resetInstance=g;//# sourceMappingURL=index.cjs.map
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storage.ts","../src/index.ts"],"names":["MemoryStorage","key","value","SDK_VERSION","NodePIHClient","PIHClient","config","nodeConfig","err","distinctId","event","properties","trackEvent","generateUUID","getTimestamp","userId","traits","args","instance","init","error","getInstance","resetInstance","PIHNode","index_default"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { StorageAdapter } from \"@product-intelligence-hub/sdk-core\";\n\n/**\n * In-memory storage adapter for server environments.\n * No persistence needed -- server processes manage their own lifecycle.\n */\nexport class MemoryStorage implements StorageAdapter {\n private store = new Map<string, string>();\n\n async get(key: string): Promise<string | null> {\n return this.store.get(key) ?? null;\n }\n\n async set(key: string, value: string): Promise<void> {\n this.store.set(key, value);\n }\n\n async remove(key: string): Promise<void> {\n this.store.delete(key);\n }\n}\n","import {\n PIHClient,\n type PIHConfig,\n type TrackEvent,\n generateUUID,\n getTimestamp,\n} from \"@product-intelligence-hub/sdk-core\";\n\nimport { MemoryStorage } from \"./storage.js\";\n\nconst SDK_VERSION = \"0.3.0\";\n\n/**\n * Node.js-specific config -- omits platform (always \"server\")\n */\nexport interface NodePIHConfig extends Omit<PIHConfig, \"platform\"> {\n platform?: \"server\";\n}\n\n/**\n * Node.js PIH Client for server-side event tracking\n */\nexport class NodePIHClient extends PIHClient {\n private processListenerAttached = false;\n private shutdownInProgress = false;\n private boundBeforeExit: () => void;\n\n constructor(config: NodePIHConfig) {\n const nodeConfig: PIHConfig = {\n ...config,\n platform: \"server\",\n flushAt: config.flushAt ?? 20,\n };\n\n super(nodeConfig, new MemoryStorage());\n\n // Set SDK metadata for transport headers\n this.transport.setSDKMeta({\n name: \"pih-sdk-node\",\n version: SDK_VERSION,\n });\n\n this.boundBeforeExit = () => {\n this.flush().catch((err) => {\n this.log(\"beforeExit flush error:\", err);\n });\n };\n }\n\n /**\n * Initialize the client and attach process lifecycle listeners\n */\n override async initialize(): Promise<void> {\n await super.initialize();\n this.attachProcessListeners();\n }\n\n /**\n * Track an event (synchronous -- queues internally)\n */\n capture(\n distinctId: string,\n event: string,\n properties?: Record<string, unknown>\n ): void {\n const trackEvent: TrackEvent = {\n event_id: generateUUID(),\n timestamp: getTimestamp(),\n project_id: this.config.projectId,\n environment: this.config.environment,\n tenant_id: this.tenantId,\n event_name: event,\n anonymous_id: this.identity.getAnonymousId(),\n user_id: distinctId,\n session_id: null,\n platform: \"server\",\n app_version: this.config.appVersion ?? null,\n properties: properties ?? {},\n user_traits: this.identity.getUserTraits(),\n context: this.getContext(),\n };\n\n this.queue.enqueue(trackEvent).catch((err) => {\n this.log(\"capture enqueue error:\", err);\n });\n }\n\n /**\n * Identify user and merge traits\n */\n override async identify(\n userId: string,\n traits?: Record<string, unknown>\n ): Promise<void> {\n await this.ensureInitialized();\n await super.identify(userId, traits);\n }\n\n /**\n * Return server context for every event\n */\n protected override getContext(): Record<string, unknown> {\n return {\n library: {\n name: \"pih-sdk-node\",\n version: SDK_VERSION,\n },\n runtime: \"node\",\n nodeVersion: process.version,\n };\n }\n\n /**\n * Force flush the event queue\n */\n override async flush(): Promise<void> {\n await this.ensureInitialized();\n await this.queue.flush();\n }\n\n /**\n * Graceful shutdown: flush, detach listeners, destroy\n */\n async shutdown(): Promise<void> {\n if (this.shutdownInProgress) return;\n this.shutdownInProgress = true;\n\n try {\n await this.flush();\n } catch (err) {\n this.log(\"shutdown flush error:\", err);\n }\n\n this.detachProcessListeners();\n this.destroy();\n }\n\n protected override log(...args: unknown[]): void {\n if (this.config.debug) {\n console.log(\"[PIH Node]\", ...args);\n }\n }\n\n private attachProcessListeners(): void {\n if (this.processListenerAttached) return;\n process.on(\"beforeExit\", this.boundBeforeExit);\n this.processListenerAttached = true;\n }\n\n private detachProcessListeners(): void {\n if (!this.processListenerAttached) return;\n process.removeListener(\"beforeExit\", this.boundBeforeExit);\n this.processListenerAttached = false;\n }\n}\n\n// ========================================\n// Singleton API\n// ========================================\n\nlet instance: NodePIHClient | null = null;\n\n/**\n * Initialize the PIH Node SDK\n */\nfunction init(config: NodePIHConfig): NodePIHClient {\n if (instance) {\n console.warn(\"[PIH Node] SDK already initialized. Returning existing instance.\");\n return instance;\n }\n\n instance = new NodePIHClient(config);\n\n // Auto-initialize (non-blocking)\n instance.initialize().catch((error) => {\n console.error(\"[PIH Node] Failed to initialize:\", error);\n });\n\n return instance;\n}\n\n/**\n * Get the current instance\n */\nfunction getInstance(): NodePIHClient | null {\n return instance;\n}\n\n/**\n * Reset the singleton (for testing)\n */\nfunction resetInstance(): void {\n if (instance) {\n instance.destroy();\n instance = null;\n }\n}\n\nconst PIHNode = {\n init,\n getInstance,\n resetInstance,\n NodePIHClient,\n};\n\nexport default PIHNode;\nexport { init, getInstance, resetInstance, MemoryStorage };\n\n// Re-export types from core\nexport type {\n PIHConfig,\n TrackEvent,\n TrackOptions,\n TransportOptions,\n PIHError,\n PIHErrorCode,\n} from \"@product-intelligence-hub/sdk-core\";\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { StorageAdapter, PIHClient, PIHConfig } from '@product-intelligence-hub/sdk-core';
|
|
2
|
+
export { PIHConfig, PIHError, PIHErrorCode, TrackEvent, TrackOptions, TransportOptions } from '@product-intelligence-hub/sdk-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* In-memory storage adapter for server environments.
|
|
6
|
+
* No persistence needed -- server processes manage their own lifecycle.
|
|
7
|
+
*/
|
|
8
|
+
declare class MemoryStorage implements StorageAdapter {
|
|
9
|
+
private store;
|
|
10
|
+
get(key: string): Promise<string | null>;
|
|
11
|
+
set(key: string, value: string): Promise<void>;
|
|
12
|
+
remove(key: string): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Node.js-specific config -- omits platform (always "server")
|
|
17
|
+
*/
|
|
18
|
+
interface NodePIHConfig extends Omit<PIHConfig, "platform"> {
|
|
19
|
+
platform?: "server";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Node.js PIH Client for server-side event tracking
|
|
23
|
+
*/
|
|
24
|
+
declare class NodePIHClient extends PIHClient {
|
|
25
|
+
private processListenerAttached;
|
|
26
|
+
private shutdownInProgress;
|
|
27
|
+
private boundBeforeExit;
|
|
28
|
+
constructor(config: NodePIHConfig);
|
|
29
|
+
/**
|
|
30
|
+
* Initialize the client and attach process lifecycle listeners
|
|
31
|
+
*/
|
|
32
|
+
initialize(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Track an event (synchronous -- queues internally)
|
|
35
|
+
*/
|
|
36
|
+
capture(distinctId: string, event: string, properties?: Record<string, unknown>): void;
|
|
37
|
+
/**
|
|
38
|
+
* Identify user and merge traits
|
|
39
|
+
*/
|
|
40
|
+
identify(userId: string, traits?: Record<string, unknown>): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Return server context for every event
|
|
43
|
+
*/
|
|
44
|
+
protected getContext(): Record<string, unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* Force flush the event queue
|
|
47
|
+
*/
|
|
48
|
+
flush(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Graceful shutdown: flush, detach listeners, destroy
|
|
51
|
+
*/
|
|
52
|
+
shutdown(): Promise<void>;
|
|
53
|
+
protected log(...args: unknown[]): void;
|
|
54
|
+
private attachProcessListeners;
|
|
55
|
+
private detachProcessListeners;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Initialize the PIH Node SDK
|
|
59
|
+
*/
|
|
60
|
+
declare function init(config: NodePIHConfig): NodePIHClient;
|
|
61
|
+
/**
|
|
62
|
+
* Get the current instance
|
|
63
|
+
*/
|
|
64
|
+
declare function getInstance(): NodePIHClient | null;
|
|
65
|
+
/**
|
|
66
|
+
* Reset the singleton (for testing)
|
|
67
|
+
*/
|
|
68
|
+
declare function resetInstance(): void;
|
|
69
|
+
declare const PIHNode: {
|
|
70
|
+
init: typeof init;
|
|
71
|
+
getInstance: typeof getInstance;
|
|
72
|
+
resetInstance: typeof resetInstance;
|
|
73
|
+
NodePIHClient: typeof NodePIHClient;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export { MemoryStorage, NodePIHClient, type NodePIHConfig, PIHNode as default, getInstance, init, resetInstance };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { StorageAdapter, PIHClient, PIHConfig } from '@product-intelligence-hub/sdk-core';
|
|
2
|
+
export { PIHConfig, PIHError, PIHErrorCode, TrackEvent, TrackOptions, TransportOptions } from '@product-intelligence-hub/sdk-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* In-memory storage adapter for server environments.
|
|
6
|
+
* No persistence needed -- server processes manage their own lifecycle.
|
|
7
|
+
*/
|
|
8
|
+
declare class MemoryStorage implements StorageAdapter {
|
|
9
|
+
private store;
|
|
10
|
+
get(key: string): Promise<string | null>;
|
|
11
|
+
set(key: string, value: string): Promise<void>;
|
|
12
|
+
remove(key: string): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Node.js-specific config -- omits platform (always "server")
|
|
17
|
+
*/
|
|
18
|
+
interface NodePIHConfig extends Omit<PIHConfig, "platform"> {
|
|
19
|
+
platform?: "server";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Node.js PIH Client for server-side event tracking
|
|
23
|
+
*/
|
|
24
|
+
declare class NodePIHClient extends PIHClient {
|
|
25
|
+
private processListenerAttached;
|
|
26
|
+
private shutdownInProgress;
|
|
27
|
+
private boundBeforeExit;
|
|
28
|
+
constructor(config: NodePIHConfig);
|
|
29
|
+
/**
|
|
30
|
+
* Initialize the client and attach process lifecycle listeners
|
|
31
|
+
*/
|
|
32
|
+
initialize(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Track an event (synchronous -- queues internally)
|
|
35
|
+
*/
|
|
36
|
+
capture(distinctId: string, event: string, properties?: Record<string, unknown>): void;
|
|
37
|
+
/**
|
|
38
|
+
* Identify user and merge traits
|
|
39
|
+
*/
|
|
40
|
+
identify(userId: string, traits?: Record<string, unknown>): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Return server context for every event
|
|
43
|
+
*/
|
|
44
|
+
protected getContext(): Record<string, unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* Force flush the event queue
|
|
47
|
+
*/
|
|
48
|
+
flush(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Graceful shutdown: flush, detach listeners, destroy
|
|
51
|
+
*/
|
|
52
|
+
shutdown(): Promise<void>;
|
|
53
|
+
protected log(...args: unknown[]): void;
|
|
54
|
+
private attachProcessListeners;
|
|
55
|
+
private detachProcessListeners;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Initialize the PIH Node SDK
|
|
59
|
+
*/
|
|
60
|
+
declare function init(config: NodePIHConfig): NodePIHClient;
|
|
61
|
+
/**
|
|
62
|
+
* Get the current instance
|
|
63
|
+
*/
|
|
64
|
+
declare function getInstance(): NodePIHClient | null;
|
|
65
|
+
/**
|
|
66
|
+
* Reset the singleton (for testing)
|
|
67
|
+
*/
|
|
68
|
+
declare function resetInstance(): void;
|
|
69
|
+
declare const PIHNode: {
|
|
70
|
+
init: typeof init;
|
|
71
|
+
getInstance: typeof getInstance;
|
|
72
|
+
resetInstance: typeof resetInstance;
|
|
73
|
+
NodePIHClient: typeof NodePIHClient;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export { MemoryStorage, NodePIHClient, type NodePIHConfig, PIHNode as default, getInstance, init, resetInstance };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {PIHClient,getTimestamp,generateUUID}from'@product-intelligence-hub/sdk-core';var r=class{store=new Map;async get(e){return this.store.get(e)??null}async set(e,i){this.store.set(e,i);}async remove(e){this.store.delete(e);}};var a="0.3.0",s=class extends PIHClient{processListenerAttached=false;shutdownInProgress=false;boundBeforeExit;constructor(e){let i={...e,platform:"server",flushAt:e.flushAt??20};super(i,new r),this.transport.setSDKMeta({name:"pih-sdk-node",version:a}),this.boundBeforeExit=()=>{this.flush().catch(o=>{this.log("beforeExit flush error:",o);});};}async initialize(){await super.initialize(),this.attachProcessListeners();}capture(e,i,o){let d={event_id:generateUUID(),timestamp:getTimestamp(),project_id:this.config.projectId,environment:this.config.environment,tenant_id:this.tenantId,event_name:i,anonymous_id:this.identity.getAnonymousId(),user_id:e,session_id:null,platform:"server",app_version:this.config.appVersion??null,properties:o??{},user_traits:this.identity.getUserTraits(),context:this.getContext()};this.queue.enqueue(d).catch(c=>{this.log("capture enqueue error:",c);});}async identify(e,i){await this.ensureInitialized(),await super.identify(e,i);}getContext(){return {library:{name:"pih-sdk-node",version:a},runtime:"node",nodeVersion:process.version}}async flush(){await this.ensureInitialized(),await this.queue.flush();}async shutdown(){if(!this.shutdownInProgress){this.shutdownInProgress=true;try{await this.flush();}catch(e){this.log("shutdown flush error:",e);}this.detachProcessListeners(),this.destroy();}}log(...e){this.config.debug&&console.log("[PIH Node]",...e);}attachProcessListeners(){this.processListenerAttached||(process.on("beforeExit",this.boundBeforeExit),this.processListenerAttached=true);}detachProcessListeners(){this.processListenerAttached&&(process.removeListener("beforeExit",this.boundBeforeExit),this.processListenerAttached=false);}},t=null;function p(n){return t?(console.warn("[PIH Node] SDK already initialized. Returning existing instance."),t):(t=new s(n),t.initialize().catch(e=>{console.error("[PIH Node] Failed to initialize:",e);}),t)}function f(){return t}function g(){t&&(t.destroy(),t=null);}var v={init:p,getInstance:f,resetInstance:g,NodePIHClient:s},y=v;export{r as MemoryStorage,s as NodePIHClient,y as default,f as getInstance,p as init,g as resetInstance};//# sourceMappingURL=index.js.map
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storage.ts","../src/index.ts"],"names":["MemoryStorage","key","value","SDK_VERSION","NodePIHClient","PIHClient","config","nodeConfig","err","distinctId","event","properties","trackEvent","generateUUID","getTimestamp","userId","traits","args","instance","init","error","getInstance","resetInstance","PIHNode","index_default"],"mappings":"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","file":"index.js","sourcesContent":["import type { StorageAdapter } from \"@product-intelligence-hub/sdk-core\";\n\n/**\n * In-memory storage adapter for server environments.\n * No persistence needed -- server processes manage their own lifecycle.\n */\nexport class MemoryStorage implements StorageAdapter {\n private store = new Map<string, string>();\n\n async get(key: string): Promise<string | null> {\n return this.store.get(key) ?? null;\n }\n\n async set(key: string, value: string): Promise<void> {\n this.store.set(key, value);\n }\n\n async remove(key: string): Promise<void> {\n this.store.delete(key);\n }\n}\n","import {\n PIHClient,\n type PIHConfig,\n type TrackEvent,\n generateUUID,\n getTimestamp,\n} from \"@product-intelligence-hub/sdk-core\";\n\nimport { MemoryStorage } from \"./storage.js\";\n\nconst SDK_VERSION = \"0.3.0\";\n\n/**\n * Node.js-specific config -- omits platform (always \"server\")\n */\nexport interface NodePIHConfig extends Omit<PIHConfig, \"platform\"> {\n platform?: \"server\";\n}\n\n/**\n * Node.js PIH Client for server-side event tracking\n */\nexport class NodePIHClient extends PIHClient {\n private processListenerAttached = false;\n private shutdownInProgress = false;\n private boundBeforeExit: () => void;\n\n constructor(config: NodePIHConfig) {\n const nodeConfig: PIHConfig = {\n ...config,\n platform: \"server\",\n flushAt: config.flushAt ?? 20,\n };\n\n super(nodeConfig, new MemoryStorage());\n\n // Set SDK metadata for transport headers\n this.transport.setSDKMeta({\n name: \"pih-sdk-node\",\n version: SDK_VERSION,\n });\n\n this.boundBeforeExit = () => {\n this.flush().catch((err) => {\n this.log(\"beforeExit flush error:\", err);\n });\n };\n }\n\n /**\n * Initialize the client and attach process lifecycle listeners\n */\n override async initialize(): Promise<void> {\n await super.initialize();\n this.attachProcessListeners();\n }\n\n /**\n * Track an event (synchronous -- queues internally)\n */\n capture(\n distinctId: string,\n event: string,\n properties?: Record<string, unknown>\n ): void {\n const trackEvent: TrackEvent = {\n event_id: generateUUID(),\n timestamp: getTimestamp(),\n project_id: this.config.projectId,\n environment: this.config.environment,\n tenant_id: this.tenantId,\n event_name: event,\n anonymous_id: this.identity.getAnonymousId(),\n user_id: distinctId,\n session_id: null,\n platform: \"server\",\n app_version: this.config.appVersion ?? null,\n properties: properties ?? {},\n user_traits: this.identity.getUserTraits(),\n context: this.getContext(),\n };\n\n this.queue.enqueue(trackEvent).catch((err) => {\n this.log(\"capture enqueue error:\", err);\n });\n }\n\n /**\n * Identify user and merge traits\n */\n override async identify(\n userId: string,\n traits?: Record<string, unknown>\n ): Promise<void> {\n await this.ensureInitialized();\n await super.identify(userId, traits);\n }\n\n /**\n * Return server context for every event\n */\n protected override getContext(): Record<string, unknown> {\n return {\n library: {\n name: \"pih-sdk-node\",\n version: SDK_VERSION,\n },\n runtime: \"node\",\n nodeVersion: process.version,\n };\n }\n\n /**\n * Force flush the event queue\n */\n override async flush(): Promise<void> {\n await this.ensureInitialized();\n await this.queue.flush();\n }\n\n /**\n * Graceful shutdown: flush, detach listeners, destroy\n */\n async shutdown(): Promise<void> {\n if (this.shutdownInProgress) return;\n this.shutdownInProgress = true;\n\n try {\n await this.flush();\n } catch (err) {\n this.log(\"shutdown flush error:\", err);\n }\n\n this.detachProcessListeners();\n this.destroy();\n }\n\n protected override log(...args: unknown[]): void {\n if (this.config.debug) {\n console.log(\"[PIH Node]\", ...args);\n }\n }\n\n private attachProcessListeners(): void {\n if (this.processListenerAttached) return;\n process.on(\"beforeExit\", this.boundBeforeExit);\n this.processListenerAttached = true;\n }\n\n private detachProcessListeners(): void {\n if (!this.processListenerAttached) return;\n process.removeListener(\"beforeExit\", this.boundBeforeExit);\n this.processListenerAttached = false;\n }\n}\n\n// ========================================\n// Singleton API\n// ========================================\n\nlet instance: NodePIHClient | null = null;\n\n/**\n * Initialize the PIH Node SDK\n */\nfunction init(config: NodePIHConfig): NodePIHClient {\n if (instance) {\n console.warn(\"[PIH Node] SDK already initialized. Returning existing instance.\");\n return instance;\n }\n\n instance = new NodePIHClient(config);\n\n // Auto-initialize (non-blocking)\n instance.initialize().catch((error) => {\n console.error(\"[PIH Node] Failed to initialize:\", error);\n });\n\n return instance;\n}\n\n/**\n * Get the current instance\n */\nfunction getInstance(): NodePIHClient | null {\n return instance;\n}\n\n/**\n * Reset the singleton (for testing)\n */\nfunction resetInstance(): void {\n if (instance) {\n instance.destroy();\n instance = null;\n }\n}\n\nconst PIHNode = {\n init,\n getInstance,\n resetInstance,\n NodePIHClient,\n};\n\nexport default PIHNode;\nexport { init, getInstance, resetInstance, MemoryStorage };\n\n// Re-export types from core\nexport type {\n PIHConfig,\n TrackEvent,\n TrackOptions,\n TransportOptions,\n PIHError,\n PIHErrorCode,\n} from \"@product-intelligence-hub/sdk-core\";\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@product-intelligence-hub/sdk-node",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Node.js Server SDK for Product Intelligence Hub",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/index.d.cts",
|
|
18
|
+
"default": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/product-intelligence-hub/product-intelligence-hub",
|
|
32
|
+
"directory": "packages/sdk-node"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"analytics",
|
|
36
|
+
"product-intelligence",
|
|
37
|
+
"sdk",
|
|
38
|
+
"tracking",
|
|
39
|
+
"events",
|
|
40
|
+
"node",
|
|
41
|
+
"server"
|
|
42
|
+
],
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@product-intelligence-hub/sdk-core": "0.3.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^20.12.0",
|
|
48
|
+
"tsup": "^8.0.0",
|
|
49
|
+
"typescript": "^5.4.0",
|
|
50
|
+
"vitest": "^1.6.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"dev": "tsup --watch",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"lint": "eslint src --ext .ts",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:watch": "vitest"
|
|
59
|
+
}
|
|
60
|
+
}
|