@walkeros/server-destination-file 3.4.0-next-1776749829492

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/helpers.ts","../src/types/index.ts"],"sourcesContent":["import { getMappingValue, isString, isObject } from '@walkeros/core';\nimport type { Destination, InitFn, PushFn, Settings } from './types';\nimport {\n createState,\n ensureHandle,\n getState,\n registerState,\n removeState,\n serialize,\n} from './helpers';\n\nexport * as DestinationFile from './types';\n\nconst init: InitFn = async ({ id, config, env, logger }) => {\n const partial = (config?.settings ?? {}) as Partial<Settings>;\n if (partial.filename === undefined || partial.filename === null) {\n logger.throw(\n \"file destination: 'filename' is required (string or Mapping.Value)\",\n );\n return false;\n }\n if (\n !isString(partial.filename) &&\n !(isObject(partial.filename) || Array.isArray(partial.filename))\n ) {\n logger.throw(\n \"file destination: 'filename' must be a string or a Mapping.Value\",\n );\n return false;\n }\n\n const format = partial.format ?? 'jsonl';\n if (\n (format === 'tsv' || format === 'csv') &&\n (!partial.fields || partial.fields.length === 0)\n ) {\n logger.throw(\n `file destination: format '${format}' requires non-empty 'fields'`,\n );\n return false;\n }\n\n const settings: Settings = {\n filename: partial.filename,\n format,\n fields: partial.fields,\n };\n\n const state = createState(env);\n\n // Eager open for static filename — fail-fast on bad path.\n if (isString(settings.filename)) {\n try {\n await ensureHandle(state, settings.filename);\n } catch (err) {\n logger.throw(\n `file destination: failed to open '${settings.filename}': ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n return false;\n }\n }\n\n registerState(id, state);\n\n return {\n ...config,\n settings,\n };\n};\n\nconst push: PushFn = async (event, { id, config, collector, logger }) => {\n const settings = config.settings as Settings | undefined;\n if (!settings) {\n logger.warn('file destination: settings missing');\n return;\n }\n const state = getState(id);\n if (!state) {\n logger.warn('file destination: state missing, init may have failed');\n return;\n }\n\n let filename: string;\n if (isString(settings.filename)) {\n filename = settings.filename;\n } else {\n const resolved = await getMappingValue(event, settings.filename, {\n collector,\n });\n if (!isString(resolved) || resolved.length === 0) {\n logger.warn(\n 'file destination: dynamic filename resolved to empty or non-string, dropping event',\n );\n return;\n }\n filename = resolved;\n }\n\n try {\n const handle = await ensureHandle(state, filename);\n handle.write(serialize(event, settings.format ?? 'jsonl', settings.fields));\n } catch (err) {\n logger.warn(\n `file destination: write failed for ${filename}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n};\n\nexport const destinationFile: Destination = {\n type: 'file',\n\n config: {},\n\n init,\n\n push,\n\n async destroy({ id }) {\n const state = getState(id);\n if (!state) return;\n for (const handle of state.handles.values()) {\n try {\n handle.end();\n } catch {\n // idempotent — ignore end errors on already-closed streams\n }\n }\n state.handles.clear();\n removeState(id);\n },\n};\n\nexport default destinationFile;\n","import {\n createWriteStream as nodeCreateWriteStream,\n type WriteStream,\n} from 'node:fs';\nimport { mkdir as nodeMkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { getByPath, isObject } from '@walkeros/core';\nimport type { WalkerOS } from '@walkeros/core';\nimport type { Env, FileWriteStream, Format } from './types';\n\n/**\n * State held per destination instance. Created in init(), reused by\n * push() and destroy(). Keyed by a stable instance id so tests and\n * hot-swap do not share handles across instances.\n */\nexport interface State {\n handles: Map<string, FileWriteStream>;\n fs: NonNullable<Env['fs']>;\n}\n\nexport function createState(env: Env | undefined): State {\n const fsOverride = env?.fs;\n const defaultFs: NonNullable<Env['fs']> = {\n createWriteStream: (path: string, options: { flags: string }) =>\n nodeCreateWriteStream(path, options),\n mkdir: async (path: string, options: { recursive: boolean }) => {\n await nodeMkdir(path, options);\n },\n };\n return {\n handles: new Map(),\n fs: fsOverride ?? defaultFs,\n };\n}\n\n/**\n * Idempotent open-or-cache. Ensures the parent directory exists and\n * appends to the file via flag 'a' (creates the file if missing).\n * Used by both init() (eager static open) and push() (lazy per-event open).\n */\nexport async function ensureHandle(\n state: State,\n filename: string,\n): Promise<FileWriteStream> {\n const cached = state.handles.get(filename);\n if (cached) return cached;\n await state.fs.mkdir(dirname(filename) || '.', { recursive: true });\n const handle = state.fs.createWriteStream(filename, {\n flags: 'a',\n }) as FileWriteStream | WriteStream;\n state.handles.set(filename, handle);\n return handle;\n}\n\n/**\n * Format an event into a single output line (with trailing newline).\n * One implementation, used by push() for all three formats.\n */\nexport function serialize(\n event: WalkerOS.Event,\n format: Format,\n fields: string[] | undefined,\n): string {\n if (format === 'jsonl') {\n return JSON.stringify(event) + '\\n';\n }\n if (!fields || fields.length === 0) {\n throw new Error(\n `file destination: format '${format}' requires non-empty 'fields' setting`,\n );\n }\n const cells = fields.map((path) =>\n formatCell(getByPath(event as unknown as WalkerOS.Properties, path)),\n );\n if (format === 'tsv') {\n return cells.join('\\t') + '\\n';\n }\n // csv\n return cells.map(csvEscape).join(',') + '\\n';\n}\n\nfunction formatCell(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (isObject(value) || Array.isArray(value)) return JSON.stringify(value);\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean')\n return String(value);\n return JSON.stringify(value);\n}\n\n/**\n * RFC 4180 CSV escaping: wrap in quotes if the value contains\n * comma, quote, CR, or LF; double up internal quotes.\n */\nfunction csvEscape(value: string): string {\n if (/[\",\\r\\n]/.test(value)) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\n/**\n * Module-level state map keyed by destination instance id. Plan Task 5\n * documents this as the fallback to config-attached symbols. Each\n * destination instance has a unique id; destroy() removes its entry.\n */\nconst states = new Map<string, State>();\n\nexport function registerState(id: string, state: State): void {\n states.set(id, state);\n}\n\nexport function getState(id: string): State | undefined {\n return states.get(id);\n}\n\nexport function removeState(id: string): void {\n states.delete(id);\n}\n","import type {\n Destination as CoreDestination,\n Mapping as CoreMapping,\n} from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\nimport type { WriteStream } from 'node:fs';\n\nexport type Format = 'jsonl' | 'tsv' | 'csv';\n\nexport interface Settings {\n /**\n * Output filename. Either a static string or a Mapping.Value resolved\n * per event (e.g. tenant sharding via `key`, daily rotation via `$code:` fn).\n * Static filenames are validated and opened at flow startup;\n * dynamic ones at first matching event.\n */\n filename: string | CoreMapping.Value;\n\n /** Serialisation format. Defaults to 'jsonl'. */\n format?: Format;\n\n /**\n * Event paths used as columns for tsv/csv formats. Order preserved.\n * Object values are JSON-stringified into a single cell.\n * Required when format is 'tsv' or 'csv'.\n */\n fields?: string[];\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {}\n\n/**\n * Minimal write-stream interface used by the destination. Matches the\n * subset of node:fs WriteStream needed for append-only writes. Tests\n * inject a fake implementation via env.fs.\n */\nexport interface FileWriteStream {\n write: (chunk: string) => boolean;\n end: () => void;\n}\n\nexport interface Env extends DestinationServer.Env {\n /**\n * Override the file system primitives. Tests inject a fake here so\n * disk writes are captured in memory.\n */\n fs?: {\n createWriteStream: (\n path: string,\n options: { flags: string },\n ) => FileWriteStream | WriteStream;\n mkdir: (path: string, options: { recursive: boolean }) => Promise<void>;\n };\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\n"],"mappings":"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","names":["isObject","isObject"]}
@@ -0,0 +1,508 @@
1
+ {
2
+ "$meta": {
3
+ "package": "@walkeros/server-destination-file",
4
+ "version": "3.4.0-next-1776749829492",
5
+ "type": "destination",
6
+ "platform": [
7
+ "server"
8
+ ],
9
+ "docs": "https://www.walkeros.io/docs/destinations/server/file",
10
+ "source": "https://github.com/elbwalker/walkerOS/tree/main/packages/server/destinations/file/src"
11
+ },
12
+ "schemas": {
13
+ "mapping": {
14
+ "$schema": "http://json-schema.org/draft-07/schema#",
15
+ "type": "object",
16
+ "properties": {},
17
+ "additionalProperties": false,
18
+ "description": "No per-rule overrides for the file destination."
19
+ },
20
+ "settings": {
21
+ "$schema": "http://json-schema.org/draft-07/schema#",
22
+ "type": "object",
23
+ "properties": {
24
+ "filename": {
25
+ "anyOf": [
26
+ {
27
+ "type": "string",
28
+ "description": "Static output filename."
29
+ },
30
+ {
31
+ "type": "object",
32
+ "propertyNames": {
33
+ "type": "string"
34
+ },
35
+ "additionalProperties": {},
36
+ "description": "Mapping.Value resolved per event (e.g. { key: \"data.tenant\" } or { fn: \"$code:...\" })."
37
+ }
38
+ ],
39
+ "description": "Output filename. Static string or Mapping.Value (e.g. { fn: \"$code:...\" } for daily rotation, { key: \"data.tenant\" } for sharding)."
40
+ },
41
+ "format": {
42
+ "description": "Serialisation format. Defaults to jsonl.",
43
+ "type": "string",
44
+ "enum": [
45
+ "jsonl",
46
+ "tsv",
47
+ "csv"
48
+ ]
49
+ },
50
+ "fields": {
51
+ "description": "Event paths used as columns for tsv/csv formats. Object values are JSON-stringified. Required when format is tsv or csv.",
52
+ "type": "array",
53
+ "items": {
54
+ "type": "string"
55
+ }
56
+ }
57
+ },
58
+ "required": [
59
+ "filename"
60
+ ],
61
+ "additionalProperties": false
62
+ }
63
+ },
64
+ "examples": {
65
+ "env": {
66
+ "init": {
67
+ "_spy": {
68
+ "captured": {},
69
+ "mkdirCalls": []
70
+ },
71
+ "fs": {
72
+ "createWriteStream": {
73
+ "$code": "t=>{const s=e.captured.get(t),a=null!=s?s:{filename:t,lines:[],ended:!1};s||e.captured.set(t,a);return{write:e=>(a.lines.push(e),!0),end(){a.ended=!0}}}"
74
+ },
75
+ "mkdir": {
76
+ "$code": "async t=>{e.mkdirCalls.push(t)}"
77
+ }
78
+ }
79
+ },
80
+ "push": {
81
+ "_spy": {
82
+ "captured": {},
83
+ "mkdirCalls": []
84
+ },
85
+ "fs": {
86
+ "createWriteStream": {
87
+ "$code": "t=>{const s=e.captured.get(t),a=null!=s?s:{filename:t,lines:[],ended:!1};s||e.captured.set(t,a);return{write:e=>(a.lines.push(e),!0),end(){a.ended=!0}}}"
88
+ },
89
+ "mkdir": {
90
+ "$code": "async t=>{e.mkdirCalls.push(t)}"
91
+ }
92
+ }
93
+ }
94
+ },
95
+ "step": {
96
+ "csvObjectCell": {
97
+ "in": {
98
+ "name": "page view",
99
+ "data": {
100
+ "title": "Hello, \"World\"",
101
+ "count": 3
102
+ },
103
+ "context": {
104
+ "dev": [
105
+ "test",
106
+ 1
107
+ ]
108
+ },
109
+ "globals": {
110
+ "pagegroup": "docs"
111
+ },
112
+ "custom": {
113
+ "completely": "random"
114
+ },
115
+ "user": {
116
+ "id": "us3r",
117
+ "device": "c00k13",
118
+ "session": "s3ss10n"
119
+ },
120
+ "nested": [
121
+ {
122
+ "entity": "child",
123
+ "data": {
124
+ "is": "subordinated"
125
+ },
126
+ "nested": [],
127
+ "context": {
128
+ "element": [
129
+ "child",
130
+ 0
131
+ ]
132
+ }
133
+ }
134
+ ],
135
+ "consent": {
136
+ "functional": true
137
+ },
138
+ "id": "1700000000000-gr0up-1",
139
+ "trigger": "load",
140
+ "entity": "page",
141
+ "action": "view",
142
+ "timestamp": 1700000000000,
143
+ "timing": 3.14,
144
+ "group": "gr0up",
145
+ "count": 1,
146
+ "version": {
147
+ "source": "3.4.0-next-1776749829492",
148
+ "tagging": 1
149
+ },
150
+ "source": {
151
+ "type": "web",
152
+ "id": "https://localhost:80",
153
+ "previous_id": "http://remotehost:9001"
154
+ }
155
+ },
156
+ "settings": {
157
+ "filename": "events.csv",
158
+ "format": "csv",
159
+ "fields": [
160
+ "timestamp",
161
+ "name",
162
+ "data"
163
+ ]
164
+ },
165
+ "out": [
166
+ [
167
+ "fs.writeFile",
168
+ "events.csv",
169
+ "1700000000000,page view,\"{\"\"title\"\":\"\"Hello, \\\"\"World\\\"\"\"\",\"\"count\"\":3}\"\n"
170
+ ]
171
+ ]
172
+ },
173
+ "jsonlDailyRotation": {
174
+ "in": {
175
+ "name": "order complete",
176
+ "data": {
177
+ "id": "ORD-1"
178
+ },
179
+ "context": {
180
+ "shopping": [
181
+ "complete",
182
+ 0
183
+ ]
184
+ },
185
+ "globals": {
186
+ "pagegroup": "shop"
187
+ },
188
+ "custom": {
189
+ "completely": "random"
190
+ },
191
+ "user": {
192
+ "id": "us3r",
193
+ "device": "c00k13",
194
+ "session": "s3ss10n"
195
+ },
196
+ "nested": [
197
+ {
198
+ "entity": "product",
199
+ "data": {
200
+ "id": "ers",
201
+ "name": "Everyday Ruck Snack",
202
+ "color": "black",
203
+ "size": "l",
204
+ "price": 420
205
+ },
206
+ "context": {
207
+ "shopping": [
208
+ "complete",
209
+ 0
210
+ ]
211
+ },
212
+ "nested": []
213
+ },
214
+ {
215
+ "entity": "product",
216
+ "data": {
217
+ "id": "cc",
218
+ "name": "Cool Cap",
219
+ "size": "one size",
220
+ "price": 42
221
+ },
222
+ "context": {
223
+ "shopping": [
224
+ "complete",
225
+ 0
226
+ ]
227
+ },
228
+ "nested": []
229
+ },
230
+ {
231
+ "entity": "gift",
232
+ "data": {
233
+ "name": "Surprise"
234
+ },
235
+ "context": {
236
+ "shopping": [
237
+ "complete",
238
+ 0
239
+ ]
240
+ },
241
+ "nested": []
242
+ }
243
+ ],
244
+ "consent": {
245
+ "functional": true
246
+ },
247
+ "id": "1776256496000-gr0up-1",
248
+ "trigger": "load",
249
+ "entity": "order",
250
+ "action": "complete",
251
+ "timestamp": 1776256496000,
252
+ "timing": 3.14,
253
+ "group": "gr0up",
254
+ "count": 1,
255
+ "version": {
256
+ "source": "3.4.0-next-1776749829492",
257
+ "tagging": 1
258
+ },
259
+ "source": {
260
+ "type": "web",
261
+ "id": "https://localhost:80",
262
+ "previous_id": "http://remotehost:9001"
263
+ }
264
+ },
265
+ "settings": {
266
+ "filename": {
267
+ "fn": {
268
+ "$code": "e=>{var t;const s=null!=(t=e.timestamp)?t:0;return`events-${new Date(s).toISOString().slice(0,10)}.jsonl`}"
269
+ }
270
+ },
271
+ "format": "jsonl"
272
+ },
273
+ "out": [
274
+ [
275
+ "fs.writeFile",
276
+ "events-2026-04-15.jsonl",
277
+ "jsonl:event"
278
+ ]
279
+ ]
280
+ },
281
+ "jsonlDefault": {
282
+ "in": {
283
+ "name": "page view",
284
+ "data": {
285
+ "domain": "www.example.com",
286
+ "title": "walkerOS documentation",
287
+ "referrer": "https://www.walkeros.io/",
288
+ "search": "?foo=bar",
289
+ "hash": "#hash",
290
+ "id": "/docs/"
291
+ },
292
+ "context": {
293
+ "dev": [
294
+ "test",
295
+ 1
296
+ ]
297
+ },
298
+ "globals": {
299
+ "pagegroup": "docs"
300
+ },
301
+ "custom": {
302
+ "completely": "random"
303
+ },
304
+ "user": {
305
+ "id": "us3r",
306
+ "device": "c00k13",
307
+ "session": "s3ss10n"
308
+ },
309
+ "nested": [
310
+ {
311
+ "entity": "child",
312
+ "data": {
313
+ "is": "subordinated"
314
+ },
315
+ "nested": [],
316
+ "context": {
317
+ "element": [
318
+ "child",
319
+ 0
320
+ ]
321
+ }
322
+ }
323
+ ],
324
+ "consent": {
325
+ "functional": true
326
+ },
327
+ "id": "1700000000000-gr0up-1",
328
+ "trigger": "load",
329
+ "entity": "page",
330
+ "action": "view",
331
+ "timestamp": 1700000000000,
332
+ "timing": 3.14,
333
+ "group": "gr0up",
334
+ "count": 1,
335
+ "version": {
336
+ "source": "3.4.0-next-1776749829492",
337
+ "tagging": 1
338
+ },
339
+ "source": {
340
+ "type": "web",
341
+ "id": "https://localhost:80",
342
+ "previous_id": "http://remotehost:9001"
343
+ }
344
+ },
345
+ "settings": {
346
+ "filename": "events.jsonl"
347
+ },
348
+ "out": [
349
+ [
350
+ "fs.writeFile",
351
+ "events.jsonl",
352
+ "jsonl:event"
353
+ ]
354
+ ]
355
+ },
356
+ "jsonlTenantShardKey": {
357
+ "in": {
358
+ "name": "custom event",
359
+ "data": {
360
+ "tenant": "acme"
361
+ },
362
+ "context": {
363
+ "dev": [
364
+ "test",
365
+ 1
366
+ ]
367
+ },
368
+ "globals": {
369
+ "lang": "elb"
370
+ },
371
+ "custom": {
372
+ "completely": "random"
373
+ },
374
+ "user": {
375
+ "id": "us3r",
376
+ "device": "c00k13",
377
+ "session": "s3ss10n"
378
+ },
379
+ "nested": [
380
+ {
381
+ "entity": "child",
382
+ "data": {
383
+ "is": "subordinated"
384
+ },
385
+ "nested": [],
386
+ "context": {
387
+ "element": [
388
+ "child",
389
+ 0
390
+ ]
391
+ }
392
+ }
393
+ ],
394
+ "consent": {
395
+ "functional": true
396
+ },
397
+ "id": "1700000000000-gr0up-1",
398
+ "trigger": "test",
399
+ "entity": "custom",
400
+ "action": "event",
401
+ "timestamp": 1700000000000,
402
+ "timing": 3.14,
403
+ "group": "gr0up",
404
+ "count": 1,
405
+ "version": {
406
+ "source": "3.4.0-next-1776749829492",
407
+ "tagging": 1
408
+ },
409
+ "source": {
410
+ "type": "web",
411
+ "id": "https://localhost:80",
412
+ "previous_id": "http://remotehost:9001"
413
+ }
414
+ },
415
+ "settings": {
416
+ "filename": {
417
+ "key": "data.tenant"
418
+ },
419
+ "format": "jsonl"
420
+ },
421
+ "out": [
422
+ [
423
+ "fs.writeFile",
424
+ "acme",
425
+ "jsonl:event"
426
+ ]
427
+ ]
428
+ },
429
+ "tsvBaerschLog": {
430
+ "in": {
431
+ "name": "page view",
432
+ "data": {
433
+ "title": "Docs"
434
+ },
435
+ "context": {
436
+ "dev": [
437
+ "test",
438
+ 1
439
+ ]
440
+ },
441
+ "globals": {
442
+ "pagegroup": "docs"
443
+ },
444
+ "custom": {
445
+ "completely": "random"
446
+ },
447
+ "user": {
448
+ "session": "sess-1"
449
+ },
450
+ "nested": [
451
+ {
452
+ "entity": "child",
453
+ "data": {
454
+ "is": "subordinated"
455
+ },
456
+ "nested": [],
457
+ "context": {
458
+ "element": [
459
+ "child",
460
+ 0
461
+ ]
462
+ }
463
+ }
464
+ ],
465
+ "consent": {
466
+ "functional": true
467
+ },
468
+ "id": "1700000000000-gr0up-1",
469
+ "trigger": "load",
470
+ "entity": "page",
471
+ "action": "view",
472
+ "timestamp": 1700000000000,
473
+ "timing": 3.14,
474
+ "group": "gr0up",
475
+ "count": 1,
476
+ "version": {
477
+ "source": "3.4.0-next-1776749829492",
478
+ "tagging": 1
479
+ },
480
+ "source": {
481
+ "id": "https://example.com/docs",
482
+ "type": "server",
483
+ "previous_id": "https://example.com/"
484
+ }
485
+ },
486
+ "settings": {
487
+ "filename": "storage/mblog.txt",
488
+ "format": "tsv",
489
+ "fields": [
490
+ "timestamp",
491
+ "user.session",
492
+ "name",
493
+ "source.id",
494
+ "data.title",
495
+ "source.previous_id"
496
+ ]
497
+ },
498
+ "out": [
499
+ [
500
+ "fs.writeFile",
501
+ "storage/mblog.txt",
502
+ "1700000000000\tsess-1\tpage view\thttps://example.com/docs\tDocs\thttps://example.com/\n"
503
+ ]
504
+ ]
505
+ }
506
+ }
507
+ }
508
+ }
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@walkeros/server-destination-file",
3
+ "description": "Local file sink for walkerOS server flows (JSONL, TSV, CSV)",
4
+ "version": "3.4.0-next-1776749829492",
5
+ "license": "MIT",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.mjs",
10
+ "require": "./dist/index.js"
11
+ },
12
+ "./dev": {
13
+ "types": "./dist/dev.d.ts",
14
+ "import": "./dist/dev.mjs",
15
+ "require": "./dist/dev.js"
16
+ },
17
+ "./walkerOS.json": "./dist/walkerOS.json",
18
+ "./examples": {
19
+ "types": "./dist/examples/index.d.ts",
20
+ "import": "./dist/examples/index.mjs",
21
+ "require": "./dist/examples/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist/**"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsup --silent",
29
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
30
+ "dev": "jest --watchAll --colors",
31
+ "typecheck": "tsc --noEmit",
32
+ "lint": "eslint \"**/*.ts*\"",
33
+ "test": "jest",
34
+ "update": "npx npm-check-updates -u && npm update"
35
+ },
36
+ "dependencies": {
37
+ "@walkeros/core": "3.4.0-next-1776749829492",
38
+ "@walkeros/server-core": "3.4.0-next-1776749829492"
39
+ },
40
+ "devDependencies": {
41
+ "@walkeros/collector": "3.4.0-next-1776749829492"
42
+ },
43
+ "repository": {
44
+ "url": "git+https://github.com/elbwalker/walkerOS.git",
45
+ "directory": "packages/server/destinations/file"
46
+ },
47
+ "author": "elbwalker <hello@elbwalker.com>",
48
+ "homepage": "https://github.com/elbwalker/walkerOS#readme",
49
+ "bugs": {
50
+ "url": "https://github.com/elbwalker/walkerOS/issues"
51
+ },
52
+ "walkerOS": {
53
+ "type": "destination",
54
+ "platform": [
55
+ "server"
56
+ ],
57
+ "docs": "https://www.walkeros.io/docs/destinations/server/file"
58
+ },
59
+ "keywords": [
60
+ "walkerOS",
61
+ "walkerOS-destination",
62
+ "destination",
63
+ "server",
64
+ "file",
65
+ "jsonl",
66
+ "tsv",
67
+ "csv",
68
+ "log"
69
+ ],
70
+ "funding": [
71
+ {
72
+ "type": "GitHub Sponsors",
73
+ "url": "https://github.com/sponsors/elbwalker"
74
+ }
75
+ ]
76
+ }