@walkeros/web-source-session 1.1.0-next-1769684633114

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,212 @@
1
+ # @walkeros/web-source-session
2
+
3
+ Standalone session detection and management for walkerOS.
4
+
5
+ [Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/sources/session)
6
+ | [NPM](https://www.npmjs.com/package/@walkeros/web-source-session) |
7
+ [Documentation](https://www.walkeros.io/docs/sources/web/session)
8
+
9
+ ## Quick Start
10
+
11
+ ```typescript
12
+ import { startFlow } from '@walkeros/collector';
13
+ import { sourceBrowser } from '@walkeros/web-source-browser';
14
+ import { sourceSession } from '@walkeros/web-source-session';
15
+
16
+ const { collector, elb } = await startFlow({
17
+ sources: {
18
+ browser: sourceBrowser,
19
+ session: {
20
+ code: sourceSession,
21
+ config: {
22
+ settings: {
23
+ storage: true, // Enable persistent session storage
24
+ },
25
+ },
26
+ },
27
+ },
28
+ });
29
+ ```
30
+
31
+ ## Features
32
+
33
+ - **Session detection**: Automatic detection of new sessions based on
34
+ navigation, referrer, and marketing parameters
35
+ - **Device tracking**: Persistent device ID across sessions (with consent)
36
+ - **Consent-aware**: Switches between window-only and storage-based sessions
37
+ based on user consent
38
+ - **Composable**: Works alongside any source (browser, dataLayer, custom)
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ npm install @walkeros/web-source-session
44
+ ```
45
+
46
+ ## Why Separate Session Source?
47
+
48
+ The session source was extracted from the browser source to enable:
49
+
50
+ 1. **Composability**: Use session detection with any source, not just browser
51
+ 2. **Single responsibility**: Browser source focuses on DOM events, session
52
+ source on identity
53
+ 3. **Flexibility**: Configure session independently from event capture
54
+ 4. **Server-side ready**: Session logic can work with server sources too
55
+
56
+ ## Configuration Reference
57
+
58
+ | Name | Type | Description | Default |
59
+ | ---------------- | -------------------------- | ------------------------------------------------ | ---------------- |
60
+ | `storage` | `boolean` | Enable persistent storage for session/device IDs | `false` |
61
+ | `consent` | `string \| string[]` | Consent key(s) required to enable storage mode | - |
62
+ | `length` | `number` | Session timeout in minutes | `30` |
63
+ | `pulse` | `boolean` | Keep session alive on each event | `false` |
64
+ | `sessionKey` | `string` | Storage key for session ID | `'elbSessionId'` |
65
+ | `sessionStorage` | `'local' \| 'session'` | Storage type for session | `'local'` |
66
+ | `deviceKey` | `string` | Storage key for device ID | `'elbDeviceId'` |
67
+ | `deviceStorage` | `'local' \| 'session'` | Storage type for device | `'local'` |
68
+ | `cb` | `SessionCallback \| false` | Custom callback or disable default | - |
69
+
70
+ ## Examples
71
+
72
+ ### Basic (Window-only)
73
+
74
+ No persistent storage, session detected per page load:
75
+
76
+ ```typescript
77
+ const { elb } = await startFlow({
78
+ sources: {
79
+ browser: sourceBrowser,
80
+ session: sourceSession, // Defaults to window-only mode
81
+ },
82
+ });
83
+ ```
84
+
85
+ ### With Persistent Storage
86
+
87
+ Store session and device IDs in localStorage:
88
+
89
+ ```typescript
90
+ const { elb } = await startFlow({
91
+ sources: {
92
+ browser: sourceBrowser,
93
+ session: {
94
+ code: sourceSession,
95
+ config: {
96
+ settings: {
97
+ storage: true,
98
+ length: 30, // 30-minute session timeout
99
+ },
100
+ },
101
+ },
102
+ },
103
+ });
104
+ ```
105
+
106
+ ### Consent-Aware
107
+
108
+ Switch to storage mode only when user grants consent:
109
+
110
+ ```typescript
111
+ const { elb } = await startFlow({
112
+ sources: {
113
+ browser: sourceBrowser,
114
+ session: {
115
+ code: sourceSession,
116
+ config: {
117
+ settings: {
118
+ consent: 'analytics', // Wait for analytics consent
119
+ storage: true,
120
+ },
121
+ },
122
+ },
123
+ },
124
+ });
125
+
126
+ // Later, when user grants consent:
127
+ elb('walker consent', { analytics: true });
128
+ // Session source automatically switches to storage mode
129
+ ```
130
+
131
+ ## Session Start Event
132
+
133
+ When a new session is detected, the source pushes a `session start` event:
134
+
135
+ ```typescript
136
+ {
137
+ name: 'session start',
138
+ data: {
139
+ isStart: true,
140
+ id: 'abc123', // Session ID
141
+ device: 'xyz789', // Device ID (if storage enabled)
142
+ storage: true, // Whether storage mode is active
143
+ // ... additional session metadata
144
+ }
145
+ }
146
+ ```
147
+
148
+ ## Migration from Browser Source
149
+
150
+ If you were using the browser source with session enabled:
151
+
152
+ ```typescript
153
+ // Before (browser source with built-in session)
154
+ const { elb } = await startFlow({
155
+ sources: {
156
+ browser: {
157
+ code: sourceBrowser,
158
+ config: { settings: { session: true } },
159
+ },
160
+ },
161
+ });
162
+
163
+ // After (separate session source)
164
+ const { elb } = await startFlow({
165
+ sources: {
166
+ browser: sourceBrowser,
167
+ session: {
168
+ code: sourceSession,
169
+ config: { settings: { storage: true } },
170
+ },
171
+ },
172
+ });
173
+ ```
174
+
175
+ ## Exported Functions
176
+
177
+ The session source exports session functions for direct usage:
178
+
179
+ ```typescript
180
+ import {
181
+ // Session functions
182
+ sessionStart,
183
+ sessionWindow,
184
+ sessionStorage,
185
+ } from '@walkeros/web-source-session';
186
+
187
+ // Use sessionStart directly (advanced usage)
188
+ const session = sessionStart({
189
+ storage: true,
190
+ collector: collectorInstance,
191
+ });
192
+ ```
193
+
194
+ Storage utilities are available from `@walkeros/web-core`:
195
+
196
+ ```typescript
197
+ import { storageRead, storageWrite, storageDelete } from '@walkeros/web-core';
198
+
199
+ storageWrite('key', 'value', 30); // 30-minute expiration
200
+ const value = storageRead('key');
201
+ storageDelete('key');
202
+ ```
203
+
204
+ ## Type Definitions
205
+
206
+ See [src/types/index.ts](./src/types/index.ts) for TypeScript interfaces.
207
+
208
+ ## Related
209
+
210
+ - [Browser Source](/docs/sources/web/browser) - DOM-based event tracking
211
+ - [DataLayer Source](/docs/sources/web/dataLayer) - GTM/GA4 integration
212
+ - [Session Documentation](https://www.walkeros.io/docs/sources/web/session)
@@ -0,0 +1,10 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ export {
8
+ __export
9
+ };
10
+ //# sourceMappingURL=chunk-J5LGTIGS.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/dev.d.mts ADDED
@@ -0,0 +1,36 @@
1
+ import * as _walkeros_core_dev from '@walkeros/core/dev';
2
+ import { z } from '@walkeros/core/dev';
3
+
4
+ /**
5
+ * Session source settings schema
6
+ */
7
+ declare const SettingsSchema: z.ZodObject<{
8
+ storage: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
9
+ consent: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
10
+ length: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
11
+ pulse: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
12
+ sessionKey: z.ZodOptional<z.ZodDefault<z.ZodString>>;
13
+ sessionStorage: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
14
+ local: "local";
15
+ session: "session";
16
+ }>>>;
17
+ deviceKey: z.ZodOptional<z.ZodDefault<z.ZodString>>;
18
+ deviceStorage: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
19
+ local: "local";
20
+ session: "session";
21
+ }>>>;
22
+ deviceAge: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
23
+ cb: z.ZodOptional<z.ZodAny>;
24
+ }, z.core.$strip>;
25
+ type Settings = z.infer<typeof SettingsSchema>;
26
+
27
+ declare const settings: _walkeros_core_dev.JSONSchema;
28
+
29
+ type index_Settings = Settings;
30
+ declare const index_SettingsSchema: typeof SettingsSchema;
31
+ declare const index_settings: typeof settings;
32
+ declare namespace index {
33
+ export { type index_Settings as Settings, index_SettingsSchema as SettingsSchema, index_settings as settings };
34
+ }
35
+
36
+ export { index as schemas };
package/dist/dev.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ import * as _walkeros_core_dev from '@walkeros/core/dev';
2
+ import { z } from '@walkeros/core/dev';
3
+
4
+ /**
5
+ * Session source settings schema
6
+ */
7
+ declare const SettingsSchema: z.ZodObject<{
8
+ storage: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
9
+ consent: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
10
+ length: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
11
+ pulse: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
12
+ sessionKey: z.ZodOptional<z.ZodDefault<z.ZodString>>;
13
+ sessionStorage: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
14
+ local: "local";
15
+ session: "session";
16
+ }>>>;
17
+ deviceKey: z.ZodOptional<z.ZodDefault<z.ZodString>>;
18
+ deviceStorage: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
19
+ local: "local";
20
+ session: "session";
21
+ }>>>;
22
+ deviceAge: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
23
+ cb: z.ZodOptional<z.ZodAny>;
24
+ }, z.core.$strip>;
25
+ type Settings = z.infer<typeof SettingsSchema>;
26
+
27
+ declare const settings: _walkeros_core_dev.JSONSchema;
28
+
29
+ type index_Settings = Settings;
30
+ declare const index_SettingsSchema: typeof SettingsSchema;
31
+ declare const index_settings: typeof settings;
32
+ declare namespace index {
33
+ export { type index_Settings as Settings, index_SettingsSchema as SettingsSchema, index_settings as settings };
34
+ }
35
+
36
+ export { index as schemas };
package/dist/dev.js ADDED
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/dev.ts
21
+ var dev_exports = {};
22
+ __export(dev_exports, {
23
+ schemas: () => schemas_exports
24
+ });
25
+ module.exports = __toCommonJS(dev_exports);
26
+
27
+ // src/schemas/index.ts
28
+ var schemas_exports = {};
29
+ __export(schemas_exports, {
30
+ SettingsSchema: () => SettingsSchema,
31
+ settings: () => settings
32
+ });
33
+ var import_dev2 = require("@walkeros/core/dev");
34
+
35
+ // src/schemas/settings.ts
36
+ var import_dev = require("@walkeros/core/dev");
37
+ var SettingsSchema = import_dev.z.object({
38
+ storage: import_dev.z.boolean().default(false).describe("Enable persistent storage for session/device IDs").optional(),
39
+ consent: import_dev.z.union([import_dev.z.string(), import_dev.z.array(import_dev.z.string())]).describe("Consent key(s) required to enable storage mode").optional(),
40
+ length: import_dev.z.number().default(30).describe("Session timeout in minutes").optional(),
41
+ pulse: import_dev.z.boolean().default(false).describe("Keep session alive on each event").optional(),
42
+ sessionKey: import_dev.z.string().default("elbSessionId").describe("Storage key for session ID").optional(),
43
+ sessionStorage: import_dev.z.enum(["local", "session"]).default("local").describe("Storage type for session").optional(),
44
+ deviceKey: import_dev.z.string().default("elbDeviceId").describe("Storage key for device ID").optional(),
45
+ deviceStorage: import_dev.z.enum(["local", "session"]).default("local").describe("Storage type for device").optional(),
46
+ deviceAge: import_dev.z.number().default(30).describe("Device ID age in days").optional(),
47
+ cb: import_dev.z.any().describe("Custom session callback function or false to disable").optional()
48
+ });
49
+
50
+ // src/schemas/index.ts
51
+ var settings = (0, import_dev2.zodToSchema)(SettingsSchema);
52
+ // Annotate the CommonJS export names for ESM import in node:
53
+ 0 && (module.exports = {
54
+ schemas
55
+ });
56
+ //# sourceMappingURL=dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dev.ts","../src/schemas/index.ts","../src/schemas/settings.ts"],"sourcesContent":["export * as schemas from './schemas';\n","import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\n\n// Export Zod schemas and types\nexport { SettingsSchema, type Settings } from './settings';\n\n// JSON Schema exports (for website PropertyTable)\nexport const settings = zodToSchema(SettingsSchema);\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Session source settings schema\n */\nexport const SettingsSchema = z.object({\n storage: z\n .boolean()\n .default(false)\n .describe('Enable persistent storage for session/device IDs')\n .optional(),\n\n consent: z\n .union([z.string(), z.array(z.string())])\n .describe('Consent key(s) required to enable storage mode')\n .optional(),\n\n length: z\n .number()\n .default(30)\n .describe('Session timeout in minutes')\n .optional(),\n\n pulse: z\n .boolean()\n .default(false)\n .describe('Keep session alive on each event')\n .optional(),\n\n sessionKey: z\n .string()\n .default('elbSessionId')\n .describe('Storage key for session ID')\n .optional(),\n\n sessionStorage: z\n .enum(['local', 'session'])\n .default('local')\n .describe('Storage type for session')\n .optional(),\n\n deviceKey: z\n .string()\n .default('elbDeviceId')\n .describe('Storage key for device ID')\n .optional(),\n\n deviceStorage: z\n .enum(['local', 'session'])\n .default('local')\n .describe('Storage type for device')\n .optional(),\n\n deviceAge: z\n .number()\n .default(30)\n .describe('Device ID age in days')\n .optional(),\n\n cb: z\n .any()\n .describe('Custom session callback function or false to disable')\n .optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,cAA4B;;;ACA5B,iBAAkB;AAKX,IAAM,iBAAiB,aAAE,OAAO;AAAA,EACrC,SAAS,aACN,QAAQ,EACR,QAAQ,KAAK,EACb,SAAS,kDAAkD,EAC3D,SAAS;AAAA,EAEZ,SAAS,aACN,MAAM,CAAC,aAAE,OAAO,GAAG,aAAE,MAAM,aAAE,OAAO,CAAC,CAAC,CAAC,EACvC,SAAS,gDAAgD,EACzD,SAAS;AAAA,EAEZ,QAAQ,aACL,OAAO,EACP,QAAQ,EAAE,EACV,SAAS,4BAA4B,EACrC,SAAS;AAAA,EAEZ,OAAO,aACJ,QAAQ,EACR,QAAQ,KAAK,EACb,SAAS,kCAAkC,EAC3C,SAAS;AAAA,EAEZ,YAAY,aACT,OAAO,EACP,QAAQ,cAAc,EACtB,SAAS,4BAA4B,EACrC,SAAS;AAAA,EAEZ,gBAAgB,aACb,KAAK,CAAC,SAAS,SAAS,CAAC,EACzB,QAAQ,OAAO,EACf,SAAS,0BAA0B,EACnC,SAAS;AAAA,EAEZ,WAAW,aACR,OAAO,EACP,QAAQ,aAAa,EACrB,SAAS,2BAA2B,EACpC,SAAS;AAAA,EAEZ,eAAe,aACZ,KAAK,CAAC,SAAS,SAAS,CAAC,EACzB,QAAQ,OAAO,EACf,SAAS,yBAAyB,EAClC,SAAS;AAAA,EAEZ,WAAW,aACR,OAAO,EACP,QAAQ,EAAE,EACV,SAAS,uBAAuB,EAChC,SAAS;AAAA,EAEZ,IAAI,aACD,IAAI,EACJ,SAAS,sDAAsD,EAC/D,SAAS;AACd,CAAC;;;ADxDM,IAAM,eAAW,yBAAY,cAAc;","names":["import_dev"]}
package/dist/dev.mjs ADDED
@@ -0,0 +1,33 @@
1
+ import {
2
+ __export
3
+ } from "./chunk-J5LGTIGS.mjs";
4
+
5
+ // src/schemas/index.ts
6
+ var schemas_exports = {};
7
+ __export(schemas_exports, {
8
+ SettingsSchema: () => SettingsSchema,
9
+ settings: () => settings
10
+ });
11
+ import { zodToSchema } from "@walkeros/core/dev";
12
+
13
+ // src/schemas/settings.ts
14
+ import { z } from "@walkeros/core/dev";
15
+ var SettingsSchema = z.object({
16
+ storage: z.boolean().default(false).describe("Enable persistent storage for session/device IDs").optional(),
17
+ consent: z.union([z.string(), z.array(z.string())]).describe("Consent key(s) required to enable storage mode").optional(),
18
+ length: z.number().default(30).describe("Session timeout in minutes").optional(),
19
+ pulse: z.boolean().default(false).describe("Keep session alive on each event").optional(),
20
+ sessionKey: z.string().default("elbSessionId").describe("Storage key for session ID").optional(),
21
+ sessionStorage: z.enum(["local", "session"]).default("local").describe("Storage type for session").optional(),
22
+ deviceKey: z.string().default("elbDeviceId").describe("Storage key for device ID").optional(),
23
+ deviceStorage: z.enum(["local", "session"]).default("local").describe("Storage type for device").optional(),
24
+ deviceAge: z.number().default(30).describe("Device ID age in days").optional(),
25
+ cb: z.any().describe("Custom session callback function or false to disable").optional()
26
+ });
27
+
28
+ // src/schemas/index.ts
29
+ var settings = zodToSchema(SettingsSchema);
30
+ export {
31
+ schemas_exports as schemas
32
+ };
33
+ //# sourceMappingURL=dev.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\n\n// Export Zod schemas and types\nexport { SettingsSchema, type Settings } from './settings';\n\n// JSON Schema exports (for website PropertyTable)\nexport const settings = zodToSchema(SettingsSchema);\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Session source settings schema\n */\nexport const SettingsSchema = z.object({\n storage: z\n .boolean()\n .default(false)\n .describe('Enable persistent storage for session/device IDs')\n .optional(),\n\n consent: z\n .union([z.string(), z.array(z.string())])\n .describe('Consent key(s) required to enable storage mode')\n .optional(),\n\n length: z\n .number()\n .default(30)\n .describe('Session timeout in minutes')\n .optional(),\n\n pulse: z\n .boolean()\n .default(false)\n .describe('Keep session alive on each event')\n .optional(),\n\n sessionKey: z\n .string()\n .default('elbSessionId')\n .describe('Storage key for session ID')\n .optional(),\n\n sessionStorage: z\n .enum(['local', 'session'])\n .default('local')\n .describe('Storage type for session')\n .optional(),\n\n deviceKey: z\n .string()\n .default('elbDeviceId')\n .describe('Storage key for device ID')\n .optional(),\n\n deviceStorage: z\n .enum(['local', 'session'])\n .default('local')\n .describe('Storage type for device')\n .optional(),\n\n deviceAge: z\n .number()\n .default(30)\n .describe('Device ID age in days')\n .optional(),\n\n cb: z\n .any()\n .describe('Custom session callback function or false to disable')\n .optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n"],"mappings":";;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,mBAAmB;;;ACA5B,SAAS,SAAS;AAKX,IAAM,iBAAiB,EAAE,OAAO;AAAA,EACrC,SAAS,EACN,QAAQ,EACR,QAAQ,KAAK,EACb,SAAS,kDAAkD,EAC3D,SAAS;AAAA,EAEZ,SAAS,EACN,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EACvC,SAAS,gDAAgD,EACzD,SAAS;AAAA,EAEZ,QAAQ,EACL,OAAO,EACP,QAAQ,EAAE,EACV,SAAS,4BAA4B,EACrC,SAAS;AAAA,EAEZ,OAAO,EACJ,QAAQ,EACR,QAAQ,KAAK,EACb,SAAS,kCAAkC,EAC3C,SAAS;AAAA,EAEZ,YAAY,EACT,OAAO,EACP,QAAQ,cAAc,EACtB,SAAS,4BAA4B,EACrC,SAAS;AAAA,EAEZ,gBAAgB,EACb,KAAK,CAAC,SAAS,SAAS,CAAC,EACzB,QAAQ,OAAO,EACf,SAAS,0BAA0B,EACnC,SAAS;AAAA,EAEZ,WAAW,EACR,OAAO,EACP,QAAQ,aAAa,EACrB,SAAS,2BAA2B,EACpC,SAAS;AAAA,EAEZ,eAAe,EACZ,KAAK,CAAC,SAAS,SAAS,CAAC,EACzB,QAAQ,OAAO,EACf,SAAS,yBAAyB,EAClC,SAAS;AAAA,EAEZ,WAAW,EACR,OAAO,EACP,QAAQ,EAAE,EACV,SAAS,uBAAuB,EAChC,SAAS;AAAA,EAEZ,IAAI,EACD,IAAI,EACJ,SAAS,sDAAsD,EAC/D,SAAS;AACd,CAAC;;;ADxDM,IAAM,WAAW,YAAY,cAAc;","names":[]}
@@ -0,0 +1,68 @@
1
+ import { WalkerOS, MarketingParameters, Collector, StorageType, Source, Elb } from '@walkeros/core';
2
+
3
+ interface SessionWindowConfig {
4
+ data?: WalkerOS.Properties;
5
+ domains?: string[];
6
+ isStart?: boolean;
7
+ parameters?: MarketingParameters;
8
+ referrer?: string;
9
+ url?: string;
10
+ }
11
+ declare function sessionWindow(config?: SessionWindowConfig): Collector.SessionData;
12
+
13
+ interface SessionStorageConfig extends SessionWindowConfig {
14
+ deviceKey?: string;
15
+ deviceStorage?: StorageType;
16
+ deviceAge?: number;
17
+ sessionKey?: string;
18
+ sessionStorage?: StorageType;
19
+ length?: number;
20
+ pulse?: boolean;
21
+ }
22
+ declare function sessionStorage(config?: SessionStorageConfig): Collector.SessionData;
23
+
24
+ interface SessionConfig extends SessionStorageConfig {
25
+ consent?: string | string[];
26
+ storage?: boolean;
27
+ cb?: SessionCallback | false;
28
+ collector?: Collector.Instance;
29
+ }
30
+ type SessionFunction = typeof sessionStorage | typeof sessionWindow;
31
+ type SessionCallback = (session: Collector.SessionData, collector: Collector.Instance | undefined, defaultCb: SessionCallback) => void;
32
+ declare function sessionStart(config?: SessionConfig): Collector.SessionData | void;
33
+
34
+ interface Settings extends SessionConfig {
35
+ }
36
+ type InitSettings = Partial<Settings>;
37
+ interface Mapping {
38
+ }
39
+ type Push = Elb.Fn;
40
+ interface Env extends Source.BaseEnv {
41
+ }
42
+ type Types = Source.Types<Settings, Mapping, Push, Env, InitSettings>;
43
+ type Config = Source.Config<Types>;
44
+
45
+ type index_Config = Config;
46
+ type index_Env = Env;
47
+ type index_InitSettings = InitSettings;
48
+ type index_Mapping = Mapping;
49
+ type index_Push = Push;
50
+ type index_SessionCallback = SessionCallback;
51
+ type index_SessionConfig = SessionConfig;
52
+ type index_SessionFunction = SessionFunction;
53
+ type index_SessionStorageConfig = SessionStorageConfig;
54
+ type index_SessionWindowConfig = SessionWindowConfig;
55
+ type index_Settings = Settings;
56
+ type index_Types = Types;
57
+ declare namespace index {
58
+ export type { index_Config as Config, index_Env as Env, index_InitSettings as InitSettings, index_Mapping as Mapping, index_Push as Push, index_SessionCallback as SessionCallback, index_SessionConfig as SessionConfig, index_SessionFunction as SessionFunction, index_SessionStorageConfig as SessionStorageConfig, index_SessionWindowConfig as SessionWindowConfig, index_Settings as Settings, index_Types as Types };
59
+ }
60
+
61
+ /**
62
+ * Session source implementation.
63
+ *
64
+ * This source handles session detection and management.
65
+ */
66
+ declare const sourceSession: Source.Init<Types>;
67
+
68
+ export { type SessionCallback, type SessionConfig, type SessionFunction, type SessionStorageConfig, type SessionWindowConfig, index as SourceSession, sourceSession as default, sessionStart, sessionStorage, sessionWindow, sourceSession };
@@ -0,0 +1,68 @@
1
+ import { WalkerOS, MarketingParameters, Collector, StorageType, Source, Elb } from '@walkeros/core';
2
+
3
+ interface SessionWindowConfig {
4
+ data?: WalkerOS.Properties;
5
+ domains?: string[];
6
+ isStart?: boolean;
7
+ parameters?: MarketingParameters;
8
+ referrer?: string;
9
+ url?: string;
10
+ }
11
+ declare function sessionWindow(config?: SessionWindowConfig): Collector.SessionData;
12
+
13
+ interface SessionStorageConfig extends SessionWindowConfig {
14
+ deviceKey?: string;
15
+ deviceStorage?: StorageType;
16
+ deviceAge?: number;
17
+ sessionKey?: string;
18
+ sessionStorage?: StorageType;
19
+ length?: number;
20
+ pulse?: boolean;
21
+ }
22
+ declare function sessionStorage(config?: SessionStorageConfig): Collector.SessionData;
23
+
24
+ interface SessionConfig extends SessionStorageConfig {
25
+ consent?: string | string[];
26
+ storage?: boolean;
27
+ cb?: SessionCallback | false;
28
+ collector?: Collector.Instance;
29
+ }
30
+ type SessionFunction = typeof sessionStorage | typeof sessionWindow;
31
+ type SessionCallback = (session: Collector.SessionData, collector: Collector.Instance | undefined, defaultCb: SessionCallback) => void;
32
+ declare function sessionStart(config?: SessionConfig): Collector.SessionData | void;
33
+
34
+ interface Settings extends SessionConfig {
35
+ }
36
+ type InitSettings = Partial<Settings>;
37
+ interface Mapping {
38
+ }
39
+ type Push = Elb.Fn;
40
+ interface Env extends Source.BaseEnv {
41
+ }
42
+ type Types = Source.Types<Settings, Mapping, Push, Env, InitSettings>;
43
+ type Config = Source.Config<Types>;
44
+
45
+ type index_Config = Config;
46
+ type index_Env = Env;
47
+ type index_InitSettings = InitSettings;
48
+ type index_Mapping = Mapping;
49
+ type index_Push = Push;
50
+ type index_SessionCallback = SessionCallback;
51
+ type index_SessionConfig = SessionConfig;
52
+ type index_SessionFunction = SessionFunction;
53
+ type index_SessionStorageConfig = SessionStorageConfig;
54
+ type index_SessionWindowConfig = SessionWindowConfig;
55
+ type index_Settings = Settings;
56
+ type index_Types = Types;
57
+ declare namespace index {
58
+ export type { index_Config as Config, index_Env as Env, index_InitSettings as InitSettings, index_Mapping as Mapping, index_Push as Push, index_SessionCallback as SessionCallback, index_SessionConfig as SessionConfig, index_SessionFunction as SessionFunction, index_SessionStorageConfig as SessionStorageConfig, index_SessionWindowConfig as SessionWindowConfig, index_Settings as Settings, index_Types as Types };
59
+ }
60
+
61
+ /**
62
+ * Session source implementation.
63
+ *
64
+ * This source handles session detection and management.
65
+ */
66
+ declare const sourceSession: Source.Init<Types>;
67
+
68
+ export { type SessionCallback, type SessionConfig, type SessionFunction, type SessionStorageConfig, type SessionWindowConfig, index as SourceSession, sourceSession as default, sessionStart, sessionStorage, sessionWindow, sourceSession };
package/dist/index.js ADDED
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SourceSession: () => types_exports,
24
+ default: () => index_default,
25
+ sessionStart: () => sessionStart,
26
+ sessionStorage: () => sessionStorage,
27
+ sessionWindow: () => sessionWindow,
28
+ sourceSession: () => sourceSession
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/lib/sessionStorage.ts
33
+ var import_core2 = require("@walkeros/core");
34
+ var import_web_core = require("@walkeros/web-core");
35
+
36
+ // src/lib/sessionWindow.ts
37
+ var import_core = require("@walkeros/core");
38
+ function sessionWindow(config = {}) {
39
+ let isStart = config.isStart || false;
40
+ const known = { isStart, storage: false };
41
+ if (config.isStart === false) return known;
42
+ if (!isStart) {
43
+ const [perf] = performance.getEntriesByType(
44
+ "navigation"
45
+ );
46
+ if (perf.type !== "navigate") return known;
47
+ }
48
+ const url = new URL(config.url || window.location.href);
49
+ const ref = config.referrer || document.referrer;
50
+ const referrer = ref && new URL(ref).hostname;
51
+ const marketing = (0, import_core.getMarketingParameters)(url, config.parameters);
52
+ if (Object.keys(marketing).length) {
53
+ if (!marketing.marketing)
54
+ marketing.marketing = true;
55
+ isStart = true;
56
+ }
57
+ if (!isStart) {
58
+ const domains = config.domains || [];
59
+ domains.push(url.hostname);
60
+ isStart = !domains.includes(referrer);
61
+ }
62
+ return isStart ? (
63
+ // It's a session start, moin
64
+ Object.assign(
65
+ {
66
+ isStart,
67
+ storage: false,
68
+ start: Date.now(),
69
+ id: (0, import_core.getId)(12),
70
+ referrer
71
+ },
72
+ marketing,
73
+ config.data
74
+ )
75
+ ) : (
76
+ // No session start
77
+ known
78
+ );
79
+ }
80
+
81
+ // src/lib/sessionStorage.ts
82
+ function sessionStorage(config = {}) {
83
+ const now = Date.now();
84
+ const {
85
+ length = 30,
86
+ // Session length in minutes
87
+ deviceKey = "elbDeviceId",
88
+ deviceStorage = "local",
89
+ deviceAge = 30,
90
+ // Device ID age in days
91
+ sessionKey = "elbSessionId",
92
+ sessionStorage: sessionStorage2 = "local",
93
+ pulse = false
94
+ // Handle the counting
95
+ } = config;
96
+ const windowSession = sessionWindow(config);
97
+ let isStart = false;
98
+ const device = (0, import_core2.tryCatch)((key, age, storage) => {
99
+ let id = (0, import_web_core.storageRead)(key, storage);
100
+ if (!id) {
101
+ id = (0, import_core2.getId)(8);
102
+ (0, import_web_core.storageWrite)(key, id, age * 1440, storage);
103
+ }
104
+ return String(id);
105
+ })(deviceKey, deviceAge, deviceStorage);
106
+ const existingSession = (0, import_core2.tryCatch)(
107
+ (key, storage) => {
108
+ const session2 = JSON.parse(String((0, import_web_core.storageRead)(key, storage)));
109
+ if (pulse) return session2;
110
+ session2.isNew = false;
111
+ if (windowSession.marketing) {
112
+ Object.assign(session2, windowSession);
113
+ isStart = true;
114
+ }
115
+ if (isStart || session2.updated + length * 6e4 < now) {
116
+ delete session2.id;
117
+ delete session2.referrer;
118
+ session2.start = now;
119
+ session2.count++;
120
+ session2.runs = 1;
121
+ isStart = true;
122
+ } else {
123
+ session2.runs++;
124
+ }
125
+ return session2;
126
+ },
127
+ () => {
128
+ isStart = true;
129
+ }
130
+ )(sessionKey, sessionStorage2) || {};
131
+ const defaultSession = {
132
+ id: (0, import_core2.getId)(12),
133
+ start: now,
134
+ isNew: true,
135
+ count: 1,
136
+ runs: 1
137
+ };
138
+ const session = Object.assign(
139
+ defaultSession,
140
+ // Default session values
141
+ windowSession,
142
+ // Basic session data based on window
143
+ existingSession,
144
+ // (Updated) existing session
145
+ { device },
146
+ // Device ID
147
+ { isStart, storage: true, updated: now },
148
+ // Status of the session
149
+ config.data
150
+ // Given data has the highest priority
151
+ );
152
+ (0, import_web_core.storageWrite)(sessionKey, JSON.stringify(session), length * 2, sessionStorage2);
153
+ return session;
154
+ }
155
+
156
+ // src/lib/sessionStart.ts
157
+ var import_core3 = require("@walkeros/core");
158
+ function sessionStart(config = {}) {
159
+ const { cb, consent, collector, storage } = config;
160
+ const sessionFn = storage ? sessionStorage : sessionWindow;
161
+ if (consent) {
162
+ const consentHandler = onConsentFn(config, cb);
163
+ const consentConfig = ((0, import_core3.isArray)(consent) ? consent : [consent]).reduce(
164
+ (acc, key) => ({ ...acc, [key]: consentHandler }),
165
+ {}
166
+ );
167
+ if (collector) {
168
+ collector.command("on", "consent", consentConfig);
169
+ }
170
+ } else {
171
+ return callFuncAndCb(sessionFn(config), collector, cb);
172
+ }
173
+ }
174
+ function callFuncAndCb(session, collector, cb) {
175
+ if (cb === false) return session;
176
+ if (!cb) cb = defaultCb;
177
+ return cb(session, collector, defaultCb);
178
+ }
179
+ function onConsentFn(config, cb) {
180
+ let lastProcessedGroup;
181
+ const func = (collector, consent) => {
182
+ if ((0, import_core3.isDefined)(lastProcessedGroup) && lastProcessedGroup === (collector == null ? void 0 : collector.group))
183
+ return;
184
+ lastProcessedGroup = collector == null ? void 0 : collector.group;
185
+ let sessionFn = () => sessionWindow(config);
186
+ if (config.consent) {
187
+ const consentKeys = ((0, import_core3.isArray)(config.consent) ? config.consent : [config.consent]).reduce((acc, key) => ({ ...acc, [key]: true }), {});
188
+ if ((0, import_core3.getGrantedConsent)(consentKeys, consent))
189
+ sessionFn = () => sessionStorage(config);
190
+ }
191
+ return callFuncAndCb(sessionFn(), collector, cb);
192
+ };
193
+ return func;
194
+ }
195
+ var defaultCb = (session, collector) => {
196
+ const user = {};
197
+ if (session.id) user.session = session.id;
198
+ if (session.storage && session.device) user.device = session.device;
199
+ if (collector) {
200
+ collector.command("user", user);
201
+ }
202
+ if (session.isStart) {
203
+ if (collector) {
204
+ collector.push({
205
+ name: "session start",
206
+ data: session
207
+ });
208
+ }
209
+ }
210
+ return session;
211
+ };
212
+
213
+ // src/types/index.ts
214
+ var types_exports = {};
215
+
216
+ // src/index.ts
217
+ var sourceSession = async (context) => {
218
+ const { config, env } = context;
219
+ const { elb, command } = env;
220
+ const settings = {
221
+ ...config == null ? void 0 : config.settings
222
+ };
223
+ const fullConfig = {
224
+ settings
225
+ };
226
+ const collectorInterface = {
227
+ push: elb,
228
+ group: void 0,
229
+ command
230
+ };
231
+ sessionStart({
232
+ ...settings,
233
+ collector: collectorInterface
234
+ });
235
+ const handleEvent = async (event) => {
236
+ if (event === "consent") {
237
+ sessionStart({
238
+ ...settings,
239
+ collector: collectorInterface
240
+ });
241
+ }
242
+ };
243
+ return {
244
+ type: "session",
245
+ config: fullConfig,
246
+ push: elb,
247
+ on: handleEvent
248
+ };
249
+ };
250
+ var index_default = sourceSession;
251
+ // Annotate the CommonJS export names for ESM import in node:
252
+ 0 && (module.exports = {
253
+ SourceSession,
254
+ sessionStart,
255
+ sessionStorage,
256
+ sessionWindow,
257
+ sourceSession
258
+ });
259
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/sessionStorage.ts","../src/lib/sessionWindow.ts","../src/lib/sessionStart.ts","../src/types/index.ts"],"sourcesContent":["import type { Source, On, Collector } from '@walkeros/core';\nimport type { Types, Settings } from './types';\nimport { sessionStart } from './lib';\n\n// Export types for external usage\nexport * as SourceSession from './types';\n\n// Export lib functions for direct usage\nexport { sessionStart, sessionStorage, sessionWindow } from './lib';\nexport type {\n SessionConfig,\n SessionCallback,\n SessionFunction,\n SessionStorageConfig,\n SessionWindowConfig,\n} from './lib';\n\n/**\n * Session source implementation.\n *\n * This source handles session detection and management.\n */\nexport const sourceSession: Source.Init<Types> = async (context) => {\n const { config, env } = context;\n const { elb, command } = env;\n\n const settings: Settings = {\n ...config?.settings,\n };\n\n const fullConfig: Source.Config<Types> = {\n settings,\n };\n\n // Create minimal collector interface for sessionStart\n const collectorInterface: Partial<Collector.Instance> = {\n push: elb,\n group: undefined,\n command,\n };\n\n // Initialize session using local lib\n sessionStart({\n ...settings,\n collector: collectorInterface as Collector.Instance,\n });\n\n // Handle events pushed from collector (consent, session, ready, run)\n const handleEvent = async (event: On.Types) => {\n if (event === 'consent') {\n // Re-initialize session on consent changes\n sessionStart({\n ...settings,\n collector: collectorInterface as Collector.Instance,\n });\n }\n };\n\n return {\n type: 'session',\n config: fullConfig,\n push: elb,\n on: handleEvent,\n };\n};\n\nexport default sourceSession;\n","import type { Collector } from '@walkeros/core';\nimport type { SessionWindowConfig } from './sessionWindow';\nimport type { StorageType } from '@walkeros/core';\nimport { getId, tryCatch } from '@walkeros/core';\nimport { storageRead, storageWrite } from '@walkeros/web-core';\nimport { sessionWindow } from './sessionWindow';\n\nexport interface SessionStorageConfig extends SessionWindowConfig {\n deviceKey?: string;\n deviceStorage?: StorageType;\n deviceAge?: number;\n sessionKey?: string;\n sessionStorage?: StorageType;\n length?: number; // Minutes after last update to consider session as expired (default: 30)\n pulse?: boolean;\n}\n\nexport function sessionStorage(\n config: SessionStorageConfig = {},\n): Collector.SessionData {\n const now = Date.now();\n const {\n length = 30, // Session length in minutes\n deviceKey = 'elbDeviceId',\n deviceStorage = 'local',\n deviceAge = 30, // Device ID age in days\n sessionKey = 'elbSessionId',\n sessionStorage = 'local',\n pulse = false, // Handle the counting\n } = config;\n const windowSession = sessionWindow(config); // Status based on window only\n let isStart = false;\n\n // Retrieve or create device ID\n const device = tryCatch((key: string, age: number, storage: StorageType) => {\n let id = storageRead(key, storage);\n if (!id) {\n id = getId(8); // Create a new device ID\n storageWrite(key, id, age * 1440, storage); // Write device ID to storage\n }\n return String(id);\n })(deviceKey, deviceAge, deviceStorage);\n\n // Retrieve or initialize session data\n const existingSession: Collector.SessionData =\n tryCatch(\n (key: string, storage?: StorageType) => {\n const session = JSON.parse(String(storageRead(key, storage)));\n\n // Only update session if it's not a pulse check\n if (pulse) return session;\n\n // Mark session as not new by default\n session.isNew = false;\n\n // Handle new marketing entry\n if (windowSession.marketing) {\n Object.assign(session, windowSession); // Overwrite existing session with marketing data\n isStart = true; // This is a session start\n }\n\n // Check if session is still active\n if (isStart || session.updated + length * 60000 < now) {\n // Session has expired\n delete session.id; // Unset session ID\n delete session.referrer; // Unset referrer\n session.start = now; // Set new session start\n session.count++; // Increase session count\n session.runs = 1; // Reset runs\n isStart = true; // It's a new session\n } else {\n // Session is still active\n session.runs++; // Increase number of runs\n }\n\n return session;\n },\n () => {\n // No existing session or something went wrong\n isStart = true; // Start a new session\n },\n )(sessionKey, sessionStorage) || {};\n\n // Default session data\n const defaultSession: Partial<Collector.SessionData> = {\n id: getId(12),\n start: now,\n isNew: true,\n count: 1,\n runs: 1,\n };\n\n // Merge session data\n const session = Object.assign(\n defaultSession, // Default session values\n windowSession, // Basic session data based on window\n existingSession, // (Updated) existing session\n { device }, // Device ID\n { isStart, storage: true, updated: now }, // Status of the session\n config.data, // Given data has the highest priority\n );\n\n // Write (updated) session to storage\n storageWrite(sessionKey, JSON.stringify(session), length * 2, sessionStorage);\n\n return session;\n}\n","import type { Collector, WalkerOS } from '@walkeros/core';\nimport {\n getId,\n getMarketingParameters,\n type MarketingParameters,\n} from '@walkeros/core';\n\nexport interface SessionWindowConfig {\n data?: WalkerOS.Properties;\n domains?: string[];\n isStart?: boolean;\n parameters?: MarketingParameters;\n referrer?: string;\n url?: string;\n}\n\nexport function sessionWindow(\n config: SessionWindowConfig = {},\n): Collector.SessionData {\n let isStart = config.isStart || false;\n const known = { isStart, storage: false };\n\n // If session has explicitly started, return known\n if (config.isStart === false) return known;\n\n // Entry type\n if (!isStart) {\n // Only focus on linked or direct navigation types\n // and ignore reloads and all others\n const [perf] = performance.getEntriesByType(\n 'navigation',\n ) as PerformanceNavigationTiming[];\n if (perf.type !== 'navigate') return known;\n }\n\n const url = new URL(config.url || window.location.href);\n const ref = config.referrer || document.referrer;\n const referrer = ref && new URL(ref).hostname;\n\n // Marketing\n const marketing = getMarketingParameters(url, config.parameters);\n if (Object.keys(marketing).length) {\n // Check for marketing parameters like UTM and add existing\n if (!marketing.marketing)\n // Flag as a marketing session without overwriting\n marketing.marketing = true;\n\n isStart = true;\n }\n\n // Referrer\n if (!isStart) {\n // Small chance of multiple unintended events for same users\n // https://en.wikipedia.org/wiki/HTTP_referer#Referrer_hiding\n // Use domains: [''] to disable direct or hidden referrer\n\n const domains = config.domains || [];\n domains.push(url.hostname);\n isStart = !domains.includes(referrer);\n }\n\n return isStart\n ? // It's a session start, moin\n Object.assign(\n {\n isStart,\n storage: false,\n start: Date.now(),\n id: getId(12),\n referrer,\n },\n marketing,\n config.data,\n )\n : // No session start\n known;\n}\n","import type { Collector, WalkerOS, On } from '@walkeros/core';\nimport type { SessionStorageConfig } from './sessionStorage';\nimport { sessionStorage } from './sessionStorage';\nimport { sessionWindow } from './sessionWindow';\nimport { getGrantedConsent, isArray, isDefined } from '@walkeros/core';\n\nexport interface SessionConfig extends SessionStorageConfig {\n consent?: string | string[];\n storage?: boolean;\n cb?: SessionCallback | false;\n collector?: Collector.Instance;\n}\n\nexport type SessionFunction = typeof sessionStorage | typeof sessionWindow;\nexport type SessionCallback = (\n session: Collector.SessionData,\n collector: Collector.Instance | undefined,\n defaultCb: SessionCallback,\n) => void;\n\nexport function sessionStart(\n config: SessionConfig = {},\n): Collector.SessionData | void {\n const { cb, consent, collector, storage } = config;\n const sessionFn: SessionFunction = storage ? sessionStorage : sessionWindow;\n\n // Consent\n if (consent) {\n const consentHandler = onConsentFn(config, cb);\n\n const consentConfig = (\n isArray(consent) ? consent : [consent]\n ).reduce<On.ConsentConfig>(\n (acc, key) => ({ ...acc, [key]: consentHandler }),\n {},\n );\n // Register consent handlers with the collector\n if (collector) {\n collector.command('on', 'consent', consentConfig);\n }\n // No fallback - session source always provides collector\n } else {\n // just do it\n return callFuncAndCb(sessionFn(config), collector, cb);\n }\n}\n\nfunction callFuncAndCb(\n session: Collector.SessionData,\n collector?: Collector.Instance,\n cb?: SessionCallback | false,\n) {\n if (cb === false) return session; // Callback is disabled\n if (!cb) cb = defaultCb; // Default callback if none is provided\n return cb(session, collector, defaultCb);\n}\n\nfunction onConsentFn(config: SessionConfig, cb?: SessionCallback | false) {\n // Track the last processed group to prevent duplicate processing\n let lastProcessedGroup: string | undefined;\n\n const func = (collector: Collector.Instance, consent: WalkerOS.Consent) => {\n // Skip if we've already processed this group\n if (\n isDefined(lastProcessedGroup) &&\n lastProcessedGroup === collector?.group\n )\n return;\n\n // Remember this group has been processed\n lastProcessedGroup = collector?.group;\n\n let sessionFn: SessionFunction = () => sessionWindow(config); // Window by default\n\n if (config.consent) {\n const consentKeys = (\n isArray(config.consent) ? config.consent : [config.consent]\n ).reduce<WalkerOS.Consent>((acc, key) => ({ ...acc, [key]: true }), {});\n\n if (getGrantedConsent(consentKeys, consent))\n // Use storage if consent is granted\n sessionFn = () => sessionStorage(config);\n }\n\n return callFuncAndCb(sessionFn(), collector, cb);\n };\n\n return func;\n}\n\nconst defaultCb: SessionCallback = (\n session,\n collector,\n): Collector.SessionData => {\n const user: WalkerOS.User = {};\n\n // User.session is the session ID\n if (session.id) user.session = session.id;\n\n // Set device ID only in storage mode\n if (session.storage && session.device) user.device = session.device;\n\n // Set user IDs\n if (collector) {\n collector.command('user', user);\n }\n // No fallback - session source always provides collector\n\n if (session.isStart) {\n // Convert session start to an event object\n if (collector) {\n collector.push({\n name: 'session start',\n data: session,\n });\n }\n // No fallback - session source always provides collector\n }\n\n return session;\n};\n","import type { Source, Elb } from '@walkeros/core';\nimport type { SessionConfig, SessionCallback } from '../lib';\n\n// Settings: configuration for session source\nexport interface Settings extends SessionConfig {\n // All settings inherited from SessionConfig:\n // - consent?: string | string[]\n // - storage?: boolean\n // - cb?: SessionCallback | false\n // - pulse?: boolean\n // - sessionStorage?: 'local' | 'session'\n // - deviceStorage?: 'local' | 'session'\n // - sessionKey?: string\n // - deviceKey?: string\n // - length?: number (session timeout in minutes)\n}\n\n// InitSettings: user input (all optional)\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {}\n\nexport type Push = Elb.Fn;\n\nexport interface Env extends Source.BaseEnv {}\n\nexport type Types = Source.Types<Settings, Mapping, Push, Env, InitSettings>;\n\nexport type Config = Source.Config<Types>;\n\n// Re-export session types from lib\nexport type {\n SessionConfig,\n SessionCallback,\n SessionFunction,\n SessionStorageConfig,\n SessionWindowConfig,\n} from '../lib';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGA,IAAAA,eAAgC;AAChC,sBAA0C;;;ACH1C,kBAIO;AAWA,SAAS,cACd,SAA8B,CAAC,GACR;AACvB,MAAI,UAAU,OAAO,WAAW;AAChC,QAAM,QAAQ,EAAE,SAAS,SAAS,MAAM;AAGxC,MAAI,OAAO,YAAY,MAAO,QAAO;AAGrC,MAAI,CAAC,SAAS;AAGZ,UAAM,CAAC,IAAI,IAAI,YAAY;AAAA,MACzB;AAAA,IACF;AACA,QAAI,KAAK,SAAS,WAAY,QAAO;AAAA,EACvC;AAEA,QAAM,MAAM,IAAI,IAAI,OAAO,OAAO,OAAO,SAAS,IAAI;AACtD,QAAM,MAAM,OAAO,YAAY,SAAS;AACxC,QAAM,WAAW,OAAO,IAAI,IAAI,GAAG,EAAE;AAGrC,QAAM,gBAAY,oCAAuB,KAAK,OAAO,UAAU;AAC/D,MAAI,OAAO,KAAK,SAAS,EAAE,QAAQ;AAEjC,QAAI,CAAC,UAAU;AAEb,gBAAU,YAAY;AAExB,cAAU;AAAA,EACZ;AAGA,MAAI,CAAC,SAAS;AAKZ,UAAM,UAAU,OAAO,WAAW,CAAC;AACnC,YAAQ,KAAK,IAAI,QAAQ;AACzB,cAAU,CAAC,QAAQ,SAAS,QAAQ;AAAA,EACtC;AAEA,SAAO;AAAA;AAAA,IAEH,OAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,SAAS;AAAA,QACT,OAAO,KAAK,IAAI;AAAA,QAChB,QAAI,mBAAM,EAAE;AAAA,QACZ;AAAA,MACF;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IACT;AAAA;AAAA;AAAA,IAEA;AAAA;AACN;;;AD3DO,SAAS,eACd,SAA+B,CAAC,GACT;AACvB,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM;AAAA,IACJ,SAAS;AAAA;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,YAAY;AAAA;AAAA,IACZ,aAAa;AAAA,IACb,gBAAAC,kBAAiB;AAAA,IACjB,QAAQ;AAAA;AAAA,EACV,IAAI;AACJ,QAAM,gBAAgB,cAAc,MAAM;AAC1C,MAAI,UAAU;AAGd,QAAM,aAAS,uBAAS,CAAC,KAAa,KAAa,YAAyB;AAC1E,QAAI,SAAK,6BAAY,KAAK,OAAO;AACjC,QAAI,CAAC,IAAI;AACP,eAAK,oBAAM,CAAC;AACZ,wCAAa,KAAK,IAAI,MAAM,MAAM,OAAO;AAAA,IAC3C;AACA,WAAO,OAAO,EAAE;AAAA,EAClB,CAAC,EAAE,WAAW,WAAW,aAAa;AAGtC,QAAM,sBACJ;AAAA,IACE,CAAC,KAAa,YAA0B;AACtC,YAAMC,WAAU,KAAK,MAAM,WAAO,6BAAY,KAAK,OAAO,CAAC,CAAC;AAG5D,UAAI,MAAO,QAAOA;AAGlB,MAAAA,SAAQ,QAAQ;AAGhB,UAAI,cAAc,WAAW;AAC3B,eAAO,OAAOA,UAAS,aAAa;AACpC,kBAAU;AAAA,MACZ;AAGA,UAAI,WAAWA,SAAQ,UAAU,SAAS,MAAQ,KAAK;AAErD,eAAOA,SAAQ;AACf,eAAOA,SAAQ;AACf,QAAAA,SAAQ,QAAQ;AAChB,QAAAA,SAAQ;AACR,QAAAA,SAAQ,OAAO;AACf,kBAAU;AAAA,MACZ,OAAO;AAEL,QAAAA,SAAQ;AAAA,MACV;AAEA,aAAOA;AAAA,IACT;AAAA,IACA,MAAM;AAEJ,gBAAU;AAAA,IACZ;AAAA,EACF,EAAE,YAAYD,eAAc,KAAK,CAAC;AAGpC,QAAM,iBAAiD;AAAA,IACrD,QAAI,oBAAM,EAAE;AAAA,IACZ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAGA,QAAM,UAAU,OAAO;AAAA,IACrB;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA,EAAE,OAAO;AAAA;AAAA,IACT,EAAE,SAAS,SAAS,MAAM,SAAS,IAAI;AAAA;AAAA,IACvC,OAAO;AAAA;AAAA,EACT;AAGA,oCAAa,YAAY,KAAK,UAAU,OAAO,GAAG,SAAS,GAAGA,eAAc;AAE5E,SAAO;AACT;;;AEtGA,IAAAE,eAAsD;AAgB/C,SAAS,aACd,SAAwB,CAAC,GACK;AAC9B,QAAM,EAAE,IAAI,SAAS,WAAW,QAAQ,IAAI;AAC5C,QAAM,YAA6B,UAAU,iBAAiB;AAG9D,MAAI,SAAS;AACX,UAAM,iBAAiB,YAAY,QAAQ,EAAE;AAE7C,UAAM,qBACJ,sBAAQ,OAAO,IAAI,UAAU,CAAC,OAAO,GACrC;AAAA,MACA,CAAC,KAAK,SAAS,EAAE,GAAG,KAAK,CAAC,GAAG,GAAG,eAAe;AAAA,MAC/C,CAAC;AAAA,IACH;AAEA,QAAI,WAAW;AACb,gBAAU,QAAQ,MAAM,WAAW,aAAa;AAAA,IAClD;AAAA,EAEF,OAAO;AAEL,WAAO,cAAc,UAAU,MAAM,GAAG,WAAW,EAAE;AAAA,EACvD;AACF;AAEA,SAAS,cACP,SACA,WACA,IACA;AACA,MAAI,OAAO,MAAO,QAAO;AACzB,MAAI,CAAC,GAAI,MAAK;AACd,SAAO,GAAG,SAAS,WAAW,SAAS;AACzC;AAEA,SAAS,YAAY,QAAuB,IAA8B;AAExE,MAAI;AAEJ,QAAM,OAAO,CAAC,WAA+B,YAA8B;AAEzE,YACE,wBAAU,kBAAkB,KAC5B,wBAAuB,uCAAW;AAElC;AAGF,yBAAqB,uCAAW;AAEhC,QAAI,YAA6B,MAAM,cAAc,MAAM;AAE3D,QAAI,OAAO,SAAS;AAClB,YAAM,mBACJ,sBAAQ,OAAO,OAAO,IAAI,OAAO,UAAU,CAAC,OAAO,OAAO,GAC1D,OAAyB,CAAC,KAAK,SAAS,EAAE,GAAG,KAAK,CAAC,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC;AAEtE,cAAI,gCAAkB,aAAa,OAAO;AAExC,oBAAY,MAAM,eAAe,MAAM;AAAA,IAC3C;AAEA,WAAO,cAAc,UAAU,GAAG,WAAW,EAAE;AAAA,EACjD;AAEA,SAAO;AACT;AAEA,IAAM,YAA6B,CACjC,SACA,cAC0B;AAC1B,QAAM,OAAsB,CAAC;AAG7B,MAAI,QAAQ,GAAI,MAAK,UAAU,QAAQ;AAGvC,MAAI,QAAQ,WAAW,QAAQ,OAAQ,MAAK,SAAS,QAAQ;AAG7D,MAAI,WAAW;AACb,cAAU,QAAQ,QAAQ,IAAI;AAAA,EAChC;AAGA,MAAI,QAAQ,SAAS;AAEnB,QAAI,WAAW;AACb,gBAAU,KAAK;AAAA,QACb,MAAM;AAAA,QACN,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EAEF;AAEA,SAAO;AACT;;;ACxHA;;;AJsBO,IAAM,gBAAoC,OAAO,YAAY;AAClE,QAAM,EAAE,QAAQ,IAAI,IAAI;AACxB,QAAM,EAAE,KAAK,QAAQ,IAAI;AAEzB,QAAM,WAAqB;AAAA,IACzB,GAAG,iCAAQ;AAAA,EACb;AAEA,QAAM,aAAmC;AAAA,IACvC;AAAA,EACF;AAGA,QAAM,qBAAkD;AAAA,IACtD,MAAM;AAAA,IACN,OAAO;AAAA,IACP;AAAA,EACF;AAGA,eAAa;AAAA,IACX,GAAG;AAAA,IACH,WAAW;AAAA,EACb,CAAC;AAGD,QAAM,cAAc,OAAO,UAAoB;AAC7C,QAAI,UAAU,WAAW;AAEvB,mBAAa;AAAA,QACX,GAAG;AAAA,QACH,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,IAAI;AAAA,EACN;AACF;AAEA,IAAO,gBAAQ;","names":["import_core","sessionStorage","session","import_core"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,233 @@
1
+ import "./chunk-J5LGTIGS.mjs";
2
+
3
+ // src/lib/sessionStorage.ts
4
+ import { getId as getId2, tryCatch } from "@walkeros/core";
5
+ import { storageRead, storageWrite } from "@walkeros/web-core";
6
+
7
+ // src/lib/sessionWindow.ts
8
+ import {
9
+ getId,
10
+ getMarketingParameters
11
+ } from "@walkeros/core";
12
+ function sessionWindow(config = {}) {
13
+ let isStart = config.isStart || false;
14
+ const known = { isStart, storage: false };
15
+ if (config.isStart === false) return known;
16
+ if (!isStart) {
17
+ const [perf] = performance.getEntriesByType(
18
+ "navigation"
19
+ );
20
+ if (perf.type !== "navigate") return known;
21
+ }
22
+ const url = new URL(config.url || window.location.href);
23
+ const ref = config.referrer || document.referrer;
24
+ const referrer = ref && new URL(ref).hostname;
25
+ const marketing = getMarketingParameters(url, config.parameters);
26
+ if (Object.keys(marketing).length) {
27
+ if (!marketing.marketing)
28
+ marketing.marketing = true;
29
+ isStart = true;
30
+ }
31
+ if (!isStart) {
32
+ const domains = config.domains || [];
33
+ domains.push(url.hostname);
34
+ isStart = !domains.includes(referrer);
35
+ }
36
+ return isStart ? (
37
+ // It's a session start, moin
38
+ Object.assign(
39
+ {
40
+ isStart,
41
+ storage: false,
42
+ start: Date.now(),
43
+ id: getId(12),
44
+ referrer
45
+ },
46
+ marketing,
47
+ config.data
48
+ )
49
+ ) : (
50
+ // No session start
51
+ known
52
+ );
53
+ }
54
+
55
+ // src/lib/sessionStorage.ts
56
+ function sessionStorage(config = {}) {
57
+ const now = Date.now();
58
+ const {
59
+ length = 30,
60
+ // Session length in minutes
61
+ deviceKey = "elbDeviceId",
62
+ deviceStorage = "local",
63
+ deviceAge = 30,
64
+ // Device ID age in days
65
+ sessionKey = "elbSessionId",
66
+ sessionStorage: sessionStorage2 = "local",
67
+ pulse = false
68
+ // Handle the counting
69
+ } = config;
70
+ const windowSession = sessionWindow(config);
71
+ let isStart = false;
72
+ const device = tryCatch((key, age, storage) => {
73
+ let id = storageRead(key, storage);
74
+ if (!id) {
75
+ id = getId2(8);
76
+ storageWrite(key, id, age * 1440, storage);
77
+ }
78
+ return String(id);
79
+ })(deviceKey, deviceAge, deviceStorage);
80
+ const existingSession = tryCatch(
81
+ (key, storage) => {
82
+ const session2 = JSON.parse(String(storageRead(key, storage)));
83
+ if (pulse) return session2;
84
+ session2.isNew = false;
85
+ if (windowSession.marketing) {
86
+ Object.assign(session2, windowSession);
87
+ isStart = true;
88
+ }
89
+ if (isStart || session2.updated + length * 6e4 < now) {
90
+ delete session2.id;
91
+ delete session2.referrer;
92
+ session2.start = now;
93
+ session2.count++;
94
+ session2.runs = 1;
95
+ isStart = true;
96
+ } else {
97
+ session2.runs++;
98
+ }
99
+ return session2;
100
+ },
101
+ () => {
102
+ isStart = true;
103
+ }
104
+ )(sessionKey, sessionStorage2) || {};
105
+ const defaultSession = {
106
+ id: getId2(12),
107
+ start: now,
108
+ isNew: true,
109
+ count: 1,
110
+ runs: 1
111
+ };
112
+ const session = Object.assign(
113
+ defaultSession,
114
+ // Default session values
115
+ windowSession,
116
+ // Basic session data based on window
117
+ existingSession,
118
+ // (Updated) existing session
119
+ { device },
120
+ // Device ID
121
+ { isStart, storage: true, updated: now },
122
+ // Status of the session
123
+ config.data
124
+ // Given data has the highest priority
125
+ );
126
+ storageWrite(sessionKey, JSON.stringify(session), length * 2, sessionStorage2);
127
+ return session;
128
+ }
129
+
130
+ // src/lib/sessionStart.ts
131
+ import { getGrantedConsent, isArray, isDefined } from "@walkeros/core";
132
+ function sessionStart(config = {}) {
133
+ const { cb, consent, collector, storage } = config;
134
+ const sessionFn = storage ? sessionStorage : sessionWindow;
135
+ if (consent) {
136
+ const consentHandler = onConsentFn(config, cb);
137
+ const consentConfig = (isArray(consent) ? consent : [consent]).reduce(
138
+ (acc, key) => ({ ...acc, [key]: consentHandler }),
139
+ {}
140
+ );
141
+ if (collector) {
142
+ collector.command("on", "consent", consentConfig);
143
+ }
144
+ } else {
145
+ return callFuncAndCb(sessionFn(config), collector, cb);
146
+ }
147
+ }
148
+ function callFuncAndCb(session, collector, cb) {
149
+ if (cb === false) return session;
150
+ if (!cb) cb = defaultCb;
151
+ return cb(session, collector, defaultCb);
152
+ }
153
+ function onConsentFn(config, cb) {
154
+ let lastProcessedGroup;
155
+ const func = (collector, consent) => {
156
+ if (isDefined(lastProcessedGroup) && lastProcessedGroup === (collector == null ? void 0 : collector.group))
157
+ return;
158
+ lastProcessedGroup = collector == null ? void 0 : collector.group;
159
+ let sessionFn = () => sessionWindow(config);
160
+ if (config.consent) {
161
+ const consentKeys = (isArray(config.consent) ? config.consent : [config.consent]).reduce((acc, key) => ({ ...acc, [key]: true }), {});
162
+ if (getGrantedConsent(consentKeys, consent))
163
+ sessionFn = () => sessionStorage(config);
164
+ }
165
+ return callFuncAndCb(sessionFn(), collector, cb);
166
+ };
167
+ return func;
168
+ }
169
+ var defaultCb = (session, collector) => {
170
+ const user = {};
171
+ if (session.id) user.session = session.id;
172
+ if (session.storage && session.device) user.device = session.device;
173
+ if (collector) {
174
+ collector.command("user", user);
175
+ }
176
+ if (session.isStart) {
177
+ if (collector) {
178
+ collector.push({
179
+ name: "session start",
180
+ data: session
181
+ });
182
+ }
183
+ }
184
+ return session;
185
+ };
186
+
187
+ // src/types/index.ts
188
+ var types_exports = {};
189
+
190
+ // src/index.ts
191
+ var sourceSession = async (context) => {
192
+ const { config, env } = context;
193
+ const { elb, command } = env;
194
+ const settings = {
195
+ ...config == null ? void 0 : config.settings
196
+ };
197
+ const fullConfig = {
198
+ settings
199
+ };
200
+ const collectorInterface = {
201
+ push: elb,
202
+ group: void 0,
203
+ command
204
+ };
205
+ sessionStart({
206
+ ...settings,
207
+ collector: collectorInterface
208
+ });
209
+ const handleEvent = async (event) => {
210
+ if (event === "consent") {
211
+ sessionStart({
212
+ ...settings,
213
+ collector: collectorInterface
214
+ });
215
+ }
216
+ };
217
+ return {
218
+ type: "session",
219
+ config: fullConfig,
220
+ push: elb,
221
+ on: handleEvent
222
+ };
223
+ };
224
+ var index_default = sourceSession;
225
+ export {
226
+ types_exports as SourceSession,
227
+ index_default as default,
228
+ sessionStart,
229
+ sessionStorage,
230
+ sessionWindow,
231
+ sourceSession
232
+ };
233
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/sessionStorage.ts","../src/lib/sessionWindow.ts","../src/lib/sessionStart.ts","../src/types/index.ts","../src/index.ts"],"sourcesContent":["import type { Collector } from '@walkeros/core';\nimport type { SessionWindowConfig } from './sessionWindow';\nimport type { StorageType } from '@walkeros/core';\nimport { getId, tryCatch } from '@walkeros/core';\nimport { storageRead, storageWrite } from '@walkeros/web-core';\nimport { sessionWindow } from './sessionWindow';\n\nexport interface SessionStorageConfig extends SessionWindowConfig {\n deviceKey?: string;\n deviceStorage?: StorageType;\n deviceAge?: number;\n sessionKey?: string;\n sessionStorage?: StorageType;\n length?: number; // Minutes after last update to consider session as expired (default: 30)\n pulse?: boolean;\n}\n\nexport function sessionStorage(\n config: SessionStorageConfig = {},\n): Collector.SessionData {\n const now = Date.now();\n const {\n length = 30, // Session length in minutes\n deviceKey = 'elbDeviceId',\n deviceStorage = 'local',\n deviceAge = 30, // Device ID age in days\n sessionKey = 'elbSessionId',\n sessionStorage = 'local',\n pulse = false, // Handle the counting\n } = config;\n const windowSession = sessionWindow(config); // Status based on window only\n let isStart = false;\n\n // Retrieve or create device ID\n const device = tryCatch((key: string, age: number, storage: StorageType) => {\n let id = storageRead(key, storage);\n if (!id) {\n id = getId(8); // Create a new device ID\n storageWrite(key, id, age * 1440, storage); // Write device ID to storage\n }\n return String(id);\n })(deviceKey, deviceAge, deviceStorage);\n\n // Retrieve or initialize session data\n const existingSession: Collector.SessionData =\n tryCatch(\n (key: string, storage?: StorageType) => {\n const session = JSON.parse(String(storageRead(key, storage)));\n\n // Only update session if it's not a pulse check\n if (pulse) return session;\n\n // Mark session as not new by default\n session.isNew = false;\n\n // Handle new marketing entry\n if (windowSession.marketing) {\n Object.assign(session, windowSession); // Overwrite existing session with marketing data\n isStart = true; // This is a session start\n }\n\n // Check if session is still active\n if (isStart || session.updated + length * 60000 < now) {\n // Session has expired\n delete session.id; // Unset session ID\n delete session.referrer; // Unset referrer\n session.start = now; // Set new session start\n session.count++; // Increase session count\n session.runs = 1; // Reset runs\n isStart = true; // It's a new session\n } else {\n // Session is still active\n session.runs++; // Increase number of runs\n }\n\n return session;\n },\n () => {\n // No existing session or something went wrong\n isStart = true; // Start a new session\n },\n )(sessionKey, sessionStorage) || {};\n\n // Default session data\n const defaultSession: Partial<Collector.SessionData> = {\n id: getId(12),\n start: now,\n isNew: true,\n count: 1,\n runs: 1,\n };\n\n // Merge session data\n const session = Object.assign(\n defaultSession, // Default session values\n windowSession, // Basic session data based on window\n existingSession, // (Updated) existing session\n { device }, // Device ID\n { isStart, storage: true, updated: now }, // Status of the session\n config.data, // Given data has the highest priority\n );\n\n // Write (updated) session to storage\n storageWrite(sessionKey, JSON.stringify(session), length * 2, sessionStorage);\n\n return session;\n}\n","import type { Collector, WalkerOS } from '@walkeros/core';\nimport {\n getId,\n getMarketingParameters,\n type MarketingParameters,\n} from '@walkeros/core';\n\nexport interface SessionWindowConfig {\n data?: WalkerOS.Properties;\n domains?: string[];\n isStart?: boolean;\n parameters?: MarketingParameters;\n referrer?: string;\n url?: string;\n}\n\nexport function sessionWindow(\n config: SessionWindowConfig = {},\n): Collector.SessionData {\n let isStart = config.isStart || false;\n const known = { isStart, storage: false };\n\n // If session has explicitly started, return known\n if (config.isStart === false) return known;\n\n // Entry type\n if (!isStart) {\n // Only focus on linked or direct navigation types\n // and ignore reloads and all others\n const [perf] = performance.getEntriesByType(\n 'navigation',\n ) as PerformanceNavigationTiming[];\n if (perf.type !== 'navigate') return known;\n }\n\n const url = new URL(config.url || window.location.href);\n const ref = config.referrer || document.referrer;\n const referrer = ref && new URL(ref).hostname;\n\n // Marketing\n const marketing = getMarketingParameters(url, config.parameters);\n if (Object.keys(marketing).length) {\n // Check for marketing parameters like UTM and add existing\n if (!marketing.marketing)\n // Flag as a marketing session without overwriting\n marketing.marketing = true;\n\n isStart = true;\n }\n\n // Referrer\n if (!isStart) {\n // Small chance of multiple unintended events for same users\n // https://en.wikipedia.org/wiki/HTTP_referer#Referrer_hiding\n // Use domains: [''] to disable direct or hidden referrer\n\n const domains = config.domains || [];\n domains.push(url.hostname);\n isStart = !domains.includes(referrer);\n }\n\n return isStart\n ? // It's a session start, moin\n Object.assign(\n {\n isStart,\n storage: false,\n start: Date.now(),\n id: getId(12),\n referrer,\n },\n marketing,\n config.data,\n )\n : // No session start\n known;\n}\n","import type { Collector, WalkerOS, On } from '@walkeros/core';\nimport type { SessionStorageConfig } from './sessionStorage';\nimport { sessionStorage } from './sessionStorage';\nimport { sessionWindow } from './sessionWindow';\nimport { getGrantedConsent, isArray, isDefined } from '@walkeros/core';\n\nexport interface SessionConfig extends SessionStorageConfig {\n consent?: string | string[];\n storage?: boolean;\n cb?: SessionCallback | false;\n collector?: Collector.Instance;\n}\n\nexport type SessionFunction = typeof sessionStorage | typeof sessionWindow;\nexport type SessionCallback = (\n session: Collector.SessionData,\n collector: Collector.Instance | undefined,\n defaultCb: SessionCallback,\n) => void;\n\nexport function sessionStart(\n config: SessionConfig = {},\n): Collector.SessionData | void {\n const { cb, consent, collector, storage } = config;\n const sessionFn: SessionFunction = storage ? sessionStorage : sessionWindow;\n\n // Consent\n if (consent) {\n const consentHandler = onConsentFn(config, cb);\n\n const consentConfig = (\n isArray(consent) ? consent : [consent]\n ).reduce<On.ConsentConfig>(\n (acc, key) => ({ ...acc, [key]: consentHandler }),\n {},\n );\n // Register consent handlers with the collector\n if (collector) {\n collector.command('on', 'consent', consentConfig);\n }\n // No fallback - session source always provides collector\n } else {\n // just do it\n return callFuncAndCb(sessionFn(config), collector, cb);\n }\n}\n\nfunction callFuncAndCb(\n session: Collector.SessionData,\n collector?: Collector.Instance,\n cb?: SessionCallback | false,\n) {\n if (cb === false) return session; // Callback is disabled\n if (!cb) cb = defaultCb; // Default callback if none is provided\n return cb(session, collector, defaultCb);\n}\n\nfunction onConsentFn(config: SessionConfig, cb?: SessionCallback | false) {\n // Track the last processed group to prevent duplicate processing\n let lastProcessedGroup: string | undefined;\n\n const func = (collector: Collector.Instance, consent: WalkerOS.Consent) => {\n // Skip if we've already processed this group\n if (\n isDefined(lastProcessedGroup) &&\n lastProcessedGroup === collector?.group\n )\n return;\n\n // Remember this group has been processed\n lastProcessedGroup = collector?.group;\n\n let sessionFn: SessionFunction = () => sessionWindow(config); // Window by default\n\n if (config.consent) {\n const consentKeys = (\n isArray(config.consent) ? config.consent : [config.consent]\n ).reduce<WalkerOS.Consent>((acc, key) => ({ ...acc, [key]: true }), {});\n\n if (getGrantedConsent(consentKeys, consent))\n // Use storage if consent is granted\n sessionFn = () => sessionStorage(config);\n }\n\n return callFuncAndCb(sessionFn(), collector, cb);\n };\n\n return func;\n}\n\nconst defaultCb: SessionCallback = (\n session,\n collector,\n): Collector.SessionData => {\n const user: WalkerOS.User = {};\n\n // User.session is the session ID\n if (session.id) user.session = session.id;\n\n // Set device ID only in storage mode\n if (session.storage && session.device) user.device = session.device;\n\n // Set user IDs\n if (collector) {\n collector.command('user', user);\n }\n // No fallback - session source always provides collector\n\n if (session.isStart) {\n // Convert session start to an event object\n if (collector) {\n collector.push({\n name: 'session start',\n data: session,\n });\n }\n // No fallback - session source always provides collector\n }\n\n return session;\n};\n","import type { Source, Elb } from '@walkeros/core';\nimport type { SessionConfig, SessionCallback } from '../lib';\n\n// Settings: configuration for session source\nexport interface Settings extends SessionConfig {\n // All settings inherited from SessionConfig:\n // - consent?: string | string[]\n // - storage?: boolean\n // - cb?: SessionCallback | false\n // - pulse?: boolean\n // - sessionStorage?: 'local' | 'session'\n // - deviceStorage?: 'local' | 'session'\n // - sessionKey?: string\n // - deviceKey?: string\n // - length?: number (session timeout in minutes)\n}\n\n// InitSettings: user input (all optional)\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {}\n\nexport type Push = Elb.Fn;\n\nexport interface Env extends Source.BaseEnv {}\n\nexport type Types = Source.Types<Settings, Mapping, Push, Env, InitSettings>;\n\nexport type Config = Source.Config<Types>;\n\n// Re-export session types from lib\nexport type {\n SessionConfig,\n SessionCallback,\n SessionFunction,\n SessionStorageConfig,\n SessionWindowConfig,\n} from '../lib';\n","import type { Source, On, Collector } from '@walkeros/core';\nimport type { Types, Settings } from './types';\nimport { sessionStart } from './lib';\n\n// Export types for external usage\nexport * as SourceSession from './types';\n\n// Export lib functions for direct usage\nexport { sessionStart, sessionStorage, sessionWindow } from './lib';\nexport type {\n SessionConfig,\n SessionCallback,\n SessionFunction,\n SessionStorageConfig,\n SessionWindowConfig,\n} from './lib';\n\n/**\n * Session source implementation.\n *\n * This source handles session detection and management.\n */\nexport const sourceSession: Source.Init<Types> = async (context) => {\n const { config, env } = context;\n const { elb, command } = env;\n\n const settings: Settings = {\n ...config?.settings,\n };\n\n const fullConfig: Source.Config<Types> = {\n settings,\n };\n\n // Create minimal collector interface for sessionStart\n const collectorInterface: Partial<Collector.Instance> = {\n push: elb,\n group: undefined,\n command,\n };\n\n // Initialize session using local lib\n sessionStart({\n ...settings,\n collector: collectorInterface as Collector.Instance,\n });\n\n // Handle events pushed from collector (consent, session, ready, run)\n const handleEvent = async (event: On.Types) => {\n if (event === 'consent') {\n // Re-initialize session on consent changes\n sessionStart({\n ...settings,\n collector: collectorInterface as Collector.Instance,\n });\n }\n };\n\n return {\n type: 'session',\n config: fullConfig,\n push: elb,\n on: handleEvent,\n };\n};\n\nexport default sourceSession;\n"],"mappings":"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","names":["getId","sessionStorage","getId","session"]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@walkeros/web-source-session",
3
+ "description": "Session source for walkerOS",
4
+ "version": "1.1.0-next-1769684633114",
5
+ "license": "MIT",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ },
15
+ "./dev": {
16
+ "types": "./dist/dev.d.ts",
17
+ "import": "./dist/dev.mjs",
18
+ "require": "./dist/dev.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist/**"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup --silent",
26
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
27
+ "dev": "jest --watchAll --colors",
28
+ "lint": "tsc && eslint \"**/*.ts*\"",
29
+ "test": "jest",
30
+ "update": "npx npm-check-updates -u && npm update"
31
+ },
32
+ "dependencies": {
33
+ "@walkeros/core": "1.2.0-next-1769684633114",
34
+ "@walkeros/web-core": "1.0.2-next-1769684633114"
35
+ },
36
+ "devDependencies": {},
37
+ "repository": {
38
+ "url": "git+https://github.com/elbwalker/walkerOS.git",
39
+ "directory": "packages/web/sources/session"
40
+ },
41
+ "author": "elbwalker <hello@elbwalker.com>",
42
+ "homepage": "https://github.com/elbwalker/walkerOS#readme",
43
+ "bugs": {
44
+ "url": "https://github.com/elbwalker/walkerOS/issues"
45
+ },
46
+ "keywords": [
47
+ "walker",
48
+ "walkerOS",
49
+ "source",
50
+ "web",
51
+ "session"
52
+ ]
53
+ }