@walkeros/server-destination-file 3.4.2 → 4.0.0-next-1777882869103

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dev.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e,t=Object.defineProperty,s=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,n=Object.prototype.hasOwnProperty,a=(e,s)=>{for(var i in s)t(e,i,{get:s[i],enumerable:!0})},o={};a(o,{examples:()=>u,schemas:()=>r}),module.exports=(e=o,((e,a,o,r)=>{if(a&&"object"==typeof a||"function"==typeof a)for(let l of i(a))n.call(e,l)||l===o||t(e,l,{get:()=>a[l],enumerable:!(r=s(a,l))||r.enumerable});return e})(t({},"__esModule",{value:!0}),e));var r={};a(r,{MappingSchema:()=>m,SettingsSchema:()=>d,mapping:()=>f,settings:()=>p});var l=require("@walkeros/core/dev"),c=require("@walkeros/core/dev"),d=c.z.object({filename:c.z.union([c.z.string().describe("Static output filename."),c.z.record(c.z.string(),c.z.unknown()).describe('Mapping.Value resolved per event (e.g. { key: "data.tenant" } or { fn: "$code:..." }).')]).describe('Output filename. Static string or Mapping.Value (e.g. { fn: "$code:..." } for daily rotation, { key: "data.tenant" } for sharding).'),format:c.z.enum(["jsonl","tsv","csv"]).optional().describe("Serialisation format. Defaults to jsonl."),fields:c.z.array(c.z.string()).optional().describe("Event paths used as columns for tsv/csv formats. Object values are JSON-stringified. Required when format is tsv or csv.")}),m=require("@walkeros/core/dev").z.object({}).describe("No per-rule overrides for the file destination."),p=(0,l.zodToSchema)(d),f=(0,l.zodToSchema)(m),u={};a(u,{env:()=>v,step:()=>j});var v={};function g(){const e={captured:new Map,mkdirCalls:[]};return{_spy:e,fs:{createWriteStream:t=>{const s=e.captured.get(t),i=null!=s?s:{filename:t,lines:[],ended:!1};s||e.captured.set(t,i);return{write:e=>(i.lines.push(e),!0),end(){i.ended=!0}}},mkdir:async t=>{e.mkdirCalls.push(t)}}}}a(v,{init:()=>w,push:()=>h});var w=g(),h=g(),j={};a(j,{csvObjectCell:()=>k,jsonlDailyRotation:()=>z,jsonlDefault:()=>S,jsonlTenantShardKey:()=>O,tsvBaerschLog:()=>y});var b=require("@walkeros/core"),S={title:"JSONL append",description:"An event is appended as a single JSON line to a static filename using default JSONL formatting.",in:(0,b.getEvent)("page view",{timestamp:17e11}),settings:{filename:"events.jsonl"},out:[["fs.writeFile","events.jsonl","jsonl:event"]]},y={title:"TSV log",description:"An event is written as a tab-separated line selecting specific fields for a compact access-log style file.",in:(0,b.getEvent)("page view",{timestamp:17e11,data:{title:"Docs"},user:{session:"sess-1"},source:{id:"https://example.com/docs",type:"server",previous_id:"https://example.com/"}}),settings:{filename:"storage/mblog.txt",format:"tsv",fields:["timestamp","user.session","name","source.id","data.title","source.previous_id"]},out:[["fs.writeFile","storage/mblog.txt","1700000000000\tsess-1\tpage view\thttps://example.com/docs\tDocs\thttps://example.com/\n"]]},O={title:"Tenant sharding",description:"The filename is resolved from an event field so events are partitioned into per-tenant JSONL files.",in:(0,b.getEvent)("custom event",{timestamp:17e11,data:{tenant:"acme"}}),settings:{filename:{key:"data.tenant"},format:"jsonl"},out:[["fs.writeFile","acme","jsonl:event"]]},z={title:"Daily rotation",description:"A mapping function derives a date-stamped filename from the event timestamp to rotate JSONL files daily.",in:(0,b.getEvent)("order complete",{timestamp:Date.UTC(2026,3,15,12,34,56),data:{id:"ORD-1"}}),settings:{filename:{fn:e=>{var t;const s=null!=(t=e.timestamp)?t:0;return`events-${new Date(s).toISOString().slice(0,10)}.jsonl`}},format:"jsonl"},out:[["fs.writeFile","events-2026-04-15.jsonl","jsonl:event"]]},k={title:"CSV with object cell",description:"An event is written as a CSV row with the data object JSON-stringified and properly quoted for embedded commas and quotes.",in:(0,b.getEvent)("page view",{timestamp:17e11,data:{title:'Hello, "World"',count:3}}),settings:{filename:"events.csv",format:"csv",fields:["timestamp","name","data"]},out:[["fs.writeFile","events.csv",'1700000000000,page view,"{""title"":""Hello, \\""World\\"""",""count"":3}"\n']]};//# sourceMappingURL=dev.js.map
1
+ "use strict";var e,t=Object.defineProperty,s=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,i=(e,s)=>{for(var n in s)t(e,n,{get:s[n],enumerable:!0})},r={};i(r,{examples:()=>u,schemas:()=>o}),module.exports=(e=r,((e,i,r,o)=>{if(i&&"object"==typeof i||"function"==typeof i)for(let l of n(i))a.call(e,l)||l===r||t(e,l,{get:()=>i[l],enumerable:!(o=s(i,l))||o.enumerable});return e})(t({},"__esModule",{value:!0}),e));var o={};i(o,{MappingSchema:()=>m,SettingsSchema:()=>d,mapping:()=>f,settings:()=>p});var l=require("@walkeros/core/dev"),c=require("@walkeros/core/dev"),d=c.z.object({filename:c.z.union([c.z.string().describe("Static output filename."),c.z.record(c.z.string(),c.z.unknown()).describe('Mapping.Value resolved per event (e.g. { key: "data.tenant" } or { fn: "$code:..." }).')]).describe('Output filename. Static string or Mapping.Value (e.g. { fn: "$code:..." } for daily rotation, { key: "data.tenant" } for sharding).'),format:c.z.enum(["jsonl","tsv","csv"]).optional().describe("Serialisation format. Defaults to jsonl."),fields:c.z.array(c.z.string()).optional().describe("Event paths used as columns for tsv/csv formats. Object values are JSON-stringified. Required when format is tsv or csv.")}),m=require("@walkeros/core/dev").z.object({}).describe("No per-rule overrides for the file destination."),p=(0,l.zodToSchema)(d),f=(0,l.zodToSchema)(m),u={};i(u,{env:()=>v,step:()=>h});var v={};function g(){const e={captured:new Map,mkdirCalls:[]};return{_spy:e,fs:{createWriteStream:t=>{const s=e.captured.get(t),n=s??{filename:t,lines:[],ended:!1};s||e.captured.set(t,n);return{write:e=>(n.lines.push(e),!0),end(){n.ended=!0}}},mkdir:async t=>{e.mkdirCalls.push(t)}}}}i(v,{init:()=>w,push:()=>b});var w=g(),b=g(),h={};i(h,{csvObjectCell:()=>k,jsonlDailyRotation:()=>z,jsonlDefault:()=>S,jsonlTenantShardKey:()=>O,tsvBaerschLog:()=>y});var j=require("@walkeros/core"),S={title:"JSONL append",description:"An event is appended as a single JSON line to a static filename using default JSONL formatting.",in:(0,j.getEvent)("page view",{timestamp:17e11}),settings:{filename:"events.jsonl"},out:[["fs.writeFile","events.jsonl","jsonl:event"]]},y={title:"TSV log",description:"An event is written as a tab-separated line selecting specific fields for a compact access-log style file.",in:(0,j.getEvent)("page view",{timestamp:17e11,data:{title:"Docs"},user:{session:"sess-1"},source:{type:"browser",platform:"web",url:"https://example.com/docs",referrer:"https://example.com/"}}),settings:{filename:"storage/mblog.txt",format:"tsv",fields:["timestamp","user.session","name","source.url","data.title","source.referrer"]},out:[["fs.writeFile","storage/mblog.txt","1700000000000\tsess-1\tpage view\thttps://example.com/docs\tDocs\thttps://example.com/\n"]]},O={title:"Tenant sharding",description:"The filename is resolved from an event field so events are partitioned into per-tenant JSONL files.",in:(0,j.getEvent)("custom event",{timestamp:17e11,data:{tenant:"acme"}}),settings:{filename:{key:"data.tenant"},format:"jsonl"},out:[["fs.writeFile","acme","jsonl:event"]]},z={title:"Daily rotation",description:"A mapping function derives a date-stamped filename from the event timestamp to rotate JSONL files daily.",in:(0,j.getEvent)("order complete",{timestamp:Date.UTC(2026,3,15,12,34,56),data:{id:"ORD-1"}}),settings:{filename:{fn:e=>{const t=e.timestamp??0;return`events-${new Date(t).toISOString().slice(0,10)}.jsonl`}},format:"jsonl"},out:[["fs.writeFile","events-2026-04-15.jsonl","jsonl:event"]]},k={title:"CSV with object cell",description:"An event is written as a CSV row with the data object JSON-stringified and properly quoted for embedded commas and quotes.",in:(0,j.getEvent)("page view",{timestamp:17e11,data:{title:'Hello, "World"',count:3}}),settings:{filename:"events.csv",format:"csv",fields:["timestamp","name","data"]},out:[["fs.writeFile","events.csv",'1700000000000,page view,"{""title"":""Hello, \\""World\\"""",""count"":3}"\n']]};//# sourceMappingURL=dev.js.map
package/dist/dev.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dev.ts","../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["export * as schemas from './schemas';\nexport * as examples from './examples';\n","import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z.object({\n filename: z\n .union([\n z.string().describe('Static output filename.'),\n z\n .record(z.string(), z.unknown())\n .describe(\n 'Mapping.Value resolved per event (e.g. { key: \"data.tenant\" } or { fn: \"$code:...\" }).',\n ),\n ])\n .describe(\n 'Output filename. Static string or Mapping.Value (e.g. { fn: \"$code:...\" } for daily rotation, { key: \"data.tenant\" } for sharding).',\n ),\n format: z\n .enum(['jsonl', 'tsv', 'csv'])\n .optional()\n .describe('Serialisation format. Defaults to jsonl.'),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Event paths used as columns for tsv/csv formats. Object values are JSON-stringified. Required when format is tsv or csv.',\n ),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const MappingSchema = z\n .object({})\n .describe('No per-rule overrides for the file destination.');\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { Env, FileWriteStream } from '../types';\n\n/**\n * Captured file state for assertions in tests.\n */\nexport interface CapturedFile {\n filename: string;\n lines: string[];\n ended: boolean;\n}\n\n// Narrow helper type aliases so the mock fs is typed explicitly without `any`.\ntype CreateWriteStreamFn = (\n path: string,\n options: { flags: string },\n) => FileWriteStream;\ntype MkdirFn = (path: string, options: { recursive: boolean }) => Promise<void>;\n\nexport interface SpyState {\n captured: Map<string, CapturedFile>;\n mkdirCalls: string[];\n}\n\nexport interface SpyEnv extends Env {\n _spy: SpyState;\n}\n\nfunction makeSpyEnv(): SpyEnv {\n const state: SpyState = {\n captured: new Map(),\n mkdirCalls: [],\n };\n\n const createWriteStream: CreateWriteStreamFn = (path) => {\n const existing = state.captured.get(path);\n const file: CapturedFile = existing ?? {\n filename: path,\n lines: [],\n ended: false,\n };\n if (!existing) state.captured.set(path, file);\n const stream: FileWriteStream = {\n write(chunk) {\n file.lines.push(chunk);\n return true;\n },\n end() {\n file.ended = true;\n },\n };\n return stream;\n };\n\n const mkdir: MkdirFn = async (path) => {\n state.mkdirCalls.push(path);\n };\n\n return {\n _spy: state,\n fs: {\n createWriteStream,\n mkdir,\n },\n };\n}\n\nexport const init: SpyEnv = makeSpyEnv();\nexport const push: SpyEnv = makeSpyEnv();\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\nimport type { Format } from '../types';\n\n/**\n * Raw filename-config shape accepted in step examples and flow.json.\n * Mirrors the runtime Settings.filename but types `fn` as string so\n * `$code:` tags (resolved at bundle time by walkerOS) are expressible\n * here without casting to a function type.\n */\nexport interface FileSettingsJson {\n filename:\n | string\n | {\n key?: string;\n fn?: string | ((value: unknown) => unknown);\n value?: unknown;\n };\n format?: Format;\n fields?: string[];\n}\n\n/**\n * Extended step example that carries destination-level settings for the\n * file destination.\n *\n * `out` is modeled at intent level as a single `['fs.writeFile', path, line]`\n * tuple per event (even though the destination internally uses a persistent\n * createWriteStream + write). This matches the design doc's preference for\n * readable intent-level callable tuples over stream-chain fidelity.\n *\n * Dropped / ignored events produce `out: []`.\n */\nexport type FileStepExample = Flow.StepExample & {\n settings: FileSettingsJson;\n};\n\n/** Default JSONL append. Static filename, all defaults. */\nexport const jsonlDefault: FileStepExample = {\n title: 'JSONL append',\n description:\n 'An event is appended as a single JSON line to a static filename using default JSONL formatting.',\n in: getEvent('page view', { timestamp: 1700000000000 }),\n settings: { filename: 'events.jsonl' },\n out: [\n [\n 'fs.writeFile',\n 'events.jsonl',\n // JSONL framework contract: JSON.stringify(event) + '\\n'.\n // The test recomputes this from example.in for the jsonl default to\n // avoid duplicating the full event shape here.\n 'jsonl:event',\n ],\n ],\n};\n\n/** Baersch-style TSV log: PHP parity case. */\nexport const tsvBaerschLog: FileStepExample = {\n title: 'TSV log',\n description:\n 'An event is written as a tab-separated line selecting specific fields for a compact access-log style file.',\n in: getEvent('page view', {\n timestamp: 1700000000000,\n data: { title: 'Docs' },\n user: { session: 'sess-1' },\n source: {\n id: 'https://example.com/docs',\n type: 'server',\n previous_id: 'https://example.com/',\n },\n }),\n settings: {\n filename: 'storage/mblog.txt',\n format: 'tsv',\n fields: [\n 'timestamp',\n 'user.session',\n 'name',\n 'source.id',\n 'data.title',\n 'source.previous_id',\n ],\n },\n out: [\n [\n 'fs.writeFile',\n 'storage/mblog.txt',\n '1700000000000\\tsess-1\\tpage view\\thttps://example.com/docs\\tDocs\\thttps://example.com/\\n',\n ],\n ],\n};\n\n/** Tenant sharding via plain key extraction. */\nexport const jsonlTenantShardKey: FileStepExample = {\n title: 'Tenant sharding',\n description:\n 'The filename is resolved from an event field so events are partitioned into per-tenant JSONL files.',\n in: getEvent('custom event', {\n timestamp: 1700000000000,\n data: { tenant: 'acme' },\n }),\n settings: {\n filename: { key: 'data.tenant' },\n format: 'jsonl',\n },\n out: [['fs.writeFile', 'acme', 'jsonl:event']],\n};\n\n/**\n * Daily rotation via a mapping fn. In flow.json this is authored as a\n * `$code:` string; walkerOS compiles it to a function at bundle time. For\n * in-process tests we pass the already-compiled function directly so the\n * example exercises the same code path without needing the bundler.\n */\nexport const jsonlDailyRotation: FileStepExample = {\n title: 'Daily rotation',\n description:\n 'A mapping function derives a date-stamped filename from the event timestamp to rotate JSONL files daily.',\n in: getEvent('order complete', {\n timestamp: Date.UTC(2026, 3, 15, 12, 34, 56),\n data: { id: 'ORD-1' },\n }),\n settings: {\n filename: {\n fn: (value) => {\n const event = value as { timestamp?: number };\n const ts = event.timestamp ?? 0;\n return `events-${new Date(ts).toISOString().slice(0, 10)}.jsonl`;\n },\n },\n format: 'jsonl',\n },\n out: [['fs.writeFile', 'events-2026-04-15.jsonl', 'jsonl:event']],\n};\n\n/** CSV with an object cell. data is JSON-stringified, properly quoted. */\nexport const csvObjectCell: FileStepExample = {\n title: 'CSV with object cell',\n description:\n 'An event is written as a CSV row with the data object JSON-stringified and properly quoted for embedded commas and quotes.',\n in: getEvent('page view', {\n timestamp: 1700000000000,\n data: { title: 'Hello, \"World\"', count: 3 },\n }),\n settings: {\n filename: 'events.csv',\n format: 'csv',\n fields: ['timestamp', 'name', 'data'],\n },\n out: [\n [\n 'fs.writeFile',\n 'events.csv',\n '1700000000000,page view,\"{\"\"title\"\":\"\"Hello, \\\\\"\"World\\\\\"\"\"\",\"\"count\"\":3}\"\\n',\n ],\n ],\n};\n"],"mappings":"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","names":["import_dev","import_dev"]}
1
+ {"version":3,"sources":["../src/dev.ts","../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["export * as schemas from './schemas';\nexport * as examples from './examples';\n","import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z.object({\n filename: z\n .union([\n z.string().describe('Static output filename.'),\n z\n .record(z.string(), z.unknown())\n .describe(\n 'Mapping.Value resolved per event (e.g. { key: \"data.tenant\" } or { fn: \"$code:...\" }).',\n ),\n ])\n .describe(\n 'Output filename. Static string or Mapping.Value (e.g. { fn: \"$code:...\" } for daily rotation, { key: \"data.tenant\" } for sharding).',\n ),\n format: z\n .enum(['jsonl', 'tsv', 'csv'])\n .optional()\n .describe('Serialisation format. Defaults to jsonl.'),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Event paths used as columns for tsv/csv formats. Object values are JSON-stringified. Required when format is tsv or csv.',\n ),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const MappingSchema = z\n .object({})\n .describe('No per-rule overrides for the file destination.');\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { Env, FileWriteStream } from '../types';\n\n/**\n * Captured file state for assertions in tests.\n */\nexport interface CapturedFile {\n filename: string;\n lines: string[];\n ended: boolean;\n}\n\n// Narrow helper type aliases so the mock fs is typed explicitly without `any`.\ntype CreateWriteStreamFn = (\n path: string,\n options: { flags: string },\n) => FileWriteStream;\ntype MkdirFn = (path: string, options: { recursive: boolean }) => Promise<void>;\n\nexport interface SpyState {\n captured: Map<string, CapturedFile>;\n mkdirCalls: string[];\n}\n\nexport interface SpyEnv extends Env {\n _spy: SpyState;\n}\n\nfunction makeSpyEnv(): SpyEnv {\n const state: SpyState = {\n captured: new Map(),\n mkdirCalls: [],\n };\n\n const createWriteStream: CreateWriteStreamFn = (path) => {\n const existing = state.captured.get(path);\n const file: CapturedFile = existing ?? {\n filename: path,\n lines: [],\n ended: false,\n };\n if (!existing) state.captured.set(path, file);\n const stream: FileWriteStream = {\n write(chunk) {\n file.lines.push(chunk);\n return true;\n },\n end() {\n file.ended = true;\n },\n };\n return stream;\n };\n\n const mkdir: MkdirFn = async (path) => {\n state.mkdirCalls.push(path);\n };\n\n return {\n _spy: state,\n fs: {\n createWriteStream,\n mkdir,\n },\n };\n}\n\nexport const init: SpyEnv = makeSpyEnv();\nexport const push: SpyEnv = makeSpyEnv();\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\nimport type { Format } from '../types';\n\n/**\n * Raw filename-config shape accepted in step examples and flow.json.\n * Mirrors the runtime Settings.filename but types `fn` as string so\n * `$code:` tags (resolved at bundle time by walkerOS) are expressible\n * here without casting to a function type.\n */\nexport interface FileSettingsJson {\n filename:\n | string\n | {\n key?: string;\n fn?: string | ((value: unknown) => unknown);\n value?: unknown;\n };\n format?: Format;\n fields?: string[];\n}\n\n/**\n * Extended step example that carries destination-level settings for the\n * file destination.\n *\n * `out` is modeled at intent level as a single `['fs.writeFile', path, line]`\n * tuple per event (even though the destination internally uses a persistent\n * createWriteStream + write). This matches the design doc's preference for\n * readable intent-level callable tuples over stream-chain fidelity.\n *\n * Dropped / ignored events produce `out: []`.\n */\nexport type FileStepExample = Flow.StepExample & {\n settings: FileSettingsJson;\n};\n\n/** Default JSONL append. Static filename, all defaults. */\nexport const jsonlDefault: FileStepExample = {\n title: 'JSONL append',\n description:\n 'An event is appended as a single JSON line to a static filename using default JSONL formatting.',\n in: getEvent('page view', { timestamp: 1700000000000 }),\n settings: { filename: 'events.jsonl' },\n out: [\n [\n 'fs.writeFile',\n 'events.jsonl',\n // JSONL framework contract: JSON.stringify(event) + '\\n'.\n // The test recomputes this from example.in for the jsonl default to\n // avoid duplicating the full event shape here.\n 'jsonl:event',\n ],\n ],\n};\n\n/** Baersch-style TSV log: PHP parity case. */\nexport const tsvBaerschLog: FileStepExample = {\n title: 'TSV log',\n description:\n 'An event is written as a tab-separated line selecting specific fields for a compact access-log style file.',\n in: getEvent('page view', {\n timestamp: 1700000000000,\n data: { title: 'Docs' },\n user: { session: 'sess-1' },\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/docs',\n referrer: 'https://example.com/',\n },\n }),\n settings: {\n filename: 'storage/mblog.txt',\n format: 'tsv',\n fields: [\n 'timestamp',\n 'user.session',\n 'name',\n 'source.url',\n 'data.title',\n 'source.referrer',\n ],\n },\n out: [\n [\n 'fs.writeFile',\n 'storage/mblog.txt',\n '1700000000000\\tsess-1\\tpage view\\thttps://example.com/docs\\tDocs\\thttps://example.com/\\n',\n ],\n ],\n};\n\n/** Tenant sharding via plain key extraction. */\nexport const jsonlTenantShardKey: FileStepExample = {\n title: 'Tenant sharding',\n description:\n 'The filename is resolved from an event field so events are partitioned into per-tenant JSONL files.',\n in: getEvent('custom event', {\n timestamp: 1700000000000,\n data: { tenant: 'acme' },\n }),\n settings: {\n filename: { key: 'data.tenant' },\n format: 'jsonl',\n },\n out: [['fs.writeFile', 'acme', 'jsonl:event']],\n};\n\n/**\n * Daily rotation via a mapping fn. In flow.json this is authored as a\n * `$code:` string; walkerOS compiles it to a function at bundle time. For\n * in-process tests we pass the already-compiled function directly so the\n * example exercises the same code path without needing the bundler.\n */\nexport const jsonlDailyRotation: FileStepExample = {\n title: 'Daily rotation',\n description:\n 'A mapping function derives a date-stamped filename from the event timestamp to rotate JSONL files daily.',\n in: getEvent('order complete', {\n timestamp: Date.UTC(2026, 3, 15, 12, 34, 56),\n data: { id: 'ORD-1' },\n }),\n settings: {\n filename: {\n fn: (value) => {\n const event = value as { timestamp?: number };\n const ts = event.timestamp ?? 0;\n return `events-${new Date(ts).toISOString().slice(0, 10)}.jsonl`;\n },\n },\n format: 'jsonl',\n },\n out: [['fs.writeFile', 'events-2026-04-15.jsonl', 'jsonl:event']],\n};\n\n/** CSV with an object cell. data is JSON-stringified, properly quoted. */\nexport const csvObjectCell: FileStepExample = {\n title: 'CSV with object cell',\n description:\n 'An event is written as a CSV row with the data object JSON-stringified and properly quoted for embedded commas and quotes.',\n in: getEvent('page view', {\n timestamp: 1700000000000,\n data: { title: 'Hello, \"World\"', count: 3 },\n }),\n settings: {\n filename: 'events.csv',\n format: 'csv',\n fields: ['timestamp', 'name', 'data'],\n },\n out: [\n [\n 'fs.writeFile',\n 'events.csv',\n '1700000000000,page view,\"{\"\"title\"\":\"\"Hello, \\\\\"\"World\\\\\"\"\"\",\"\"count\"\":3}\"\\n',\n ],\n ],\n};\n"],"mappings":"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","names":["import_dev","import_dev"]}
package/dist/dev.mjs CHANGED
@@ -1 +1 @@
1
- var e=Object.defineProperty,t=(t,s)=>{for(var i in s)e(t,i,{get:s[i],enumerable:!0})},s={};t(s,{MappingSchema:()=>r,SettingsSchema:()=>n,mapping:()=>m,settings:()=>l});import{zodToSchema as i}from"@walkeros/core/dev";import{z as a}from"@walkeros/core/dev";var n=a.object({filename:a.union([a.string().describe("Static output filename."),a.record(a.string(),a.unknown()).describe('Mapping.Value resolved per event (e.g. { key: "data.tenant" } or { fn: "$code:..." }).')]).describe('Output filename. Static string or Mapping.Value (e.g. { fn: "$code:..." } for daily rotation, { key: "data.tenant" } for sharding).'),format:a.enum(["jsonl","tsv","csv"]).optional().describe("Serialisation format. Defaults to jsonl."),fields:a.array(a.string()).optional().describe("Event paths used as columns for tsv/csv formats. Object values are JSON-stringified. Required when format is tsv or csv.")});import{z as o}from"@walkeros/core/dev";var r=o.object({}).describe("No per-rule overrides for the file destination."),l=i(n),m=i(r),d={};t(d,{env:()=>c,step:()=>u});var c={};function p(){const e={captured:new Map,mkdirCalls:[]};return{_spy:e,fs:{createWriteStream:t=>{const s=e.captured.get(t),i=null!=s?s:{filename:t,lines:[],ended:!1};s||e.captured.set(t,i);return{write:e=>(i.lines.push(e),!0),end(){i.ended=!0}}},mkdir:async t=>{e.mkdirCalls.push(t)}}}}t(c,{init:()=>f,push:()=>v});var f=p(),v=p(),u={};t(u,{csvObjectCell:()=>b,jsonlDailyRotation:()=>S,jsonlDefault:()=>w,jsonlTenantShardKey:()=>j,tsvBaerschLog:()=>h});import{getEvent as g}from"@walkeros/core";var w={title:"JSONL append",description:"An event is appended as a single JSON line to a static filename using default JSONL formatting.",in:g("page view",{timestamp:17e11}),settings:{filename:"events.jsonl"},out:[["fs.writeFile","events.jsonl","jsonl:event"]]},h={title:"TSV log",description:"An event is written as a tab-separated line selecting specific fields for a compact access-log style file.",in:g("page view",{timestamp:17e11,data:{title:"Docs"},user:{session:"sess-1"},source:{id:"https://example.com/docs",type:"server",previous_id:"https://example.com/"}}),settings:{filename:"storage/mblog.txt",format:"tsv",fields:["timestamp","user.session","name","source.id","data.title","source.previous_id"]},out:[["fs.writeFile","storage/mblog.txt","1700000000000\tsess-1\tpage view\thttps://example.com/docs\tDocs\thttps://example.com/\n"]]},j={title:"Tenant sharding",description:"The filename is resolved from an event field so events are partitioned into per-tenant JSONL files.",in:g("custom event",{timestamp:17e11,data:{tenant:"acme"}}),settings:{filename:{key:"data.tenant"},format:"jsonl"},out:[["fs.writeFile","acme","jsonl:event"]]},S={title:"Daily rotation",description:"A mapping function derives a date-stamped filename from the event timestamp to rotate JSONL files daily.",in:g("order complete",{timestamp:Date.UTC(2026,3,15,12,34,56),data:{id:"ORD-1"}}),settings:{filename:{fn:e=>{var t;const s=null!=(t=e.timestamp)?t:0;return`events-${new Date(s).toISOString().slice(0,10)}.jsonl`}},format:"jsonl"},out:[["fs.writeFile","events-2026-04-15.jsonl","jsonl:event"]]},b={title:"CSV with object cell",description:"An event is written as a CSV row with the data object JSON-stringified and properly quoted for embedded commas and quotes.",in:g("page view",{timestamp:17e11,data:{title:'Hello, "World"',count:3}}),settings:{filename:"events.csv",format:"csv",fields:["timestamp","name","data"]},out:[["fs.writeFile","events.csv",'1700000000000,page view,"{""title"":""Hello, \\""World\\"""",""count"":3}"\n']]};export{d as examples,s as schemas};//# sourceMappingURL=dev.mjs.map
1
+ var e=Object.defineProperty,t=(t,s)=>{for(var a in s)e(t,a,{get:s[a],enumerable:!0})},s={};t(s,{MappingSchema:()=>o,SettingsSchema:()=>n,mapping:()=>m,settings:()=>l});import{zodToSchema as a}from"@walkeros/core/dev";import{z as i}from"@walkeros/core/dev";var n=i.object({filename:i.union([i.string().describe("Static output filename."),i.record(i.string(),i.unknown()).describe('Mapping.Value resolved per event (e.g. { key: "data.tenant" } or { fn: "$code:..." }).')]).describe('Output filename. Static string or Mapping.Value (e.g. { fn: "$code:..." } for daily rotation, { key: "data.tenant" } for sharding).'),format:i.enum(["jsonl","tsv","csv"]).optional().describe("Serialisation format. Defaults to jsonl."),fields:i.array(i.string()).optional().describe("Event paths used as columns for tsv/csv formats. Object values are JSON-stringified. Required when format is tsv or csv.")});import{z as r}from"@walkeros/core/dev";var o=r.object({}).describe("No per-rule overrides for the file destination."),l=a(n),m=a(o),d={};t(d,{env:()=>c,step:()=>u});var c={};function p(){const e={captured:new Map,mkdirCalls:[]};return{_spy:e,fs:{createWriteStream:t=>{const s=e.captured.get(t),a=s??{filename:t,lines:[],ended:!1};s||e.captured.set(t,a);return{write:e=>(a.lines.push(e),!0),end(){a.ended=!0}}},mkdir:async t=>{e.mkdirCalls.push(t)}}}}t(c,{init:()=>f,push:()=>v});var f=p(),v=p(),u={};t(u,{csvObjectCell:()=>S,jsonlDailyRotation:()=>b,jsonlDefault:()=>w,jsonlTenantShardKey:()=>j,tsvBaerschLog:()=>h});import{getEvent as g}from"@walkeros/core";var w={title:"JSONL append",description:"An event is appended as a single JSON line to a static filename using default JSONL formatting.",in:g("page view",{timestamp:17e11}),settings:{filename:"events.jsonl"},out:[["fs.writeFile","events.jsonl","jsonl:event"]]},h={title:"TSV log",description:"An event is written as a tab-separated line selecting specific fields for a compact access-log style file.",in:g("page view",{timestamp:17e11,data:{title:"Docs"},user:{session:"sess-1"},source:{type:"browser",platform:"web",url:"https://example.com/docs",referrer:"https://example.com/"}}),settings:{filename:"storage/mblog.txt",format:"tsv",fields:["timestamp","user.session","name","source.url","data.title","source.referrer"]},out:[["fs.writeFile","storage/mblog.txt","1700000000000\tsess-1\tpage view\thttps://example.com/docs\tDocs\thttps://example.com/\n"]]},j={title:"Tenant sharding",description:"The filename is resolved from an event field so events are partitioned into per-tenant JSONL files.",in:g("custom event",{timestamp:17e11,data:{tenant:"acme"}}),settings:{filename:{key:"data.tenant"},format:"jsonl"},out:[["fs.writeFile","acme","jsonl:event"]]},b={title:"Daily rotation",description:"A mapping function derives a date-stamped filename from the event timestamp to rotate JSONL files daily.",in:g("order complete",{timestamp:Date.UTC(2026,3,15,12,34,56),data:{id:"ORD-1"}}),settings:{filename:{fn:e=>{const t=e.timestamp??0;return`events-${new Date(t).toISOString().slice(0,10)}.jsonl`}},format:"jsonl"},out:[["fs.writeFile","events-2026-04-15.jsonl","jsonl:event"]]},S={title:"CSV with object cell",description:"An event is written as a CSV row with the data object JSON-stringified and properly quoted for embedded commas and quotes.",in:g("page view",{timestamp:17e11,data:{title:'Hello, "World"',count:3}}),settings:{filename:"events.csv",format:"csv",fields:["timestamp","name","data"]},out:[["fs.writeFile","events.csv",'1700000000000,page view,"{""title"":""Hello, \\""World\\"""",""count"":3}"\n']]};export{d as examples,s as schemas};//# sourceMappingURL=dev.mjs.map
package/dist/dev.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z.object({\n filename: z\n .union([\n z.string().describe('Static output filename.'),\n z\n .record(z.string(), z.unknown())\n .describe(\n 'Mapping.Value resolved per event (e.g. { key: \"data.tenant\" } or { fn: \"$code:...\" }).',\n ),\n ])\n .describe(\n 'Output filename. Static string or Mapping.Value (e.g. { fn: \"$code:...\" } for daily rotation, { key: \"data.tenant\" } for sharding).',\n ),\n format: z\n .enum(['jsonl', 'tsv', 'csv'])\n .optional()\n .describe('Serialisation format. Defaults to jsonl.'),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Event paths used as columns for tsv/csv formats. Object values are JSON-stringified. Required when format is tsv or csv.',\n ),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const MappingSchema = z\n .object({})\n .describe('No per-rule overrides for the file destination.');\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { Env, FileWriteStream } from '../types';\n\n/**\n * Captured file state for assertions in tests.\n */\nexport interface CapturedFile {\n filename: string;\n lines: string[];\n ended: boolean;\n}\n\n// Narrow helper type aliases so the mock fs is typed explicitly without `any`.\ntype CreateWriteStreamFn = (\n path: string,\n options: { flags: string },\n) => FileWriteStream;\ntype MkdirFn = (path: string, options: { recursive: boolean }) => Promise<void>;\n\nexport interface SpyState {\n captured: Map<string, CapturedFile>;\n mkdirCalls: string[];\n}\n\nexport interface SpyEnv extends Env {\n _spy: SpyState;\n}\n\nfunction makeSpyEnv(): SpyEnv {\n const state: SpyState = {\n captured: new Map(),\n mkdirCalls: [],\n };\n\n const createWriteStream: CreateWriteStreamFn = (path) => {\n const existing = state.captured.get(path);\n const file: CapturedFile = existing ?? {\n filename: path,\n lines: [],\n ended: false,\n };\n if (!existing) state.captured.set(path, file);\n const stream: FileWriteStream = {\n write(chunk) {\n file.lines.push(chunk);\n return true;\n },\n end() {\n file.ended = true;\n },\n };\n return stream;\n };\n\n const mkdir: MkdirFn = async (path) => {\n state.mkdirCalls.push(path);\n };\n\n return {\n _spy: state,\n fs: {\n createWriteStream,\n mkdir,\n },\n };\n}\n\nexport const init: SpyEnv = makeSpyEnv();\nexport const push: SpyEnv = makeSpyEnv();\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\nimport type { Format } from '../types';\n\n/**\n * Raw filename-config shape accepted in step examples and flow.json.\n * Mirrors the runtime Settings.filename but types `fn` as string so\n * `$code:` tags (resolved at bundle time by walkerOS) are expressible\n * here without casting to a function type.\n */\nexport interface FileSettingsJson {\n filename:\n | string\n | {\n key?: string;\n fn?: string | ((value: unknown) => unknown);\n value?: unknown;\n };\n format?: Format;\n fields?: string[];\n}\n\n/**\n * Extended step example that carries destination-level settings for the\n * file destination.\n *\n * `out` is modeled at intent level as a single `['fs.writeFile', path, line]`\n * tuple per event (even though the destination internally uses a persistent\n * createWriteStream + write). This matches the design doc's preference for\n * readable intent-level callable tuples over stream-chain fidelity.\n *\n * Dropped / ignored events produce `out: []`.\n */\nexport type FileStepExample = Flow.StepExample & {\n settings: FileSettingsJson;\n};\n\n/** Default JSONL append. Static filename, all defaults. */\nexport const jsonlDefault: FileStepExample = {\n title: 'JSONL append',\n description:\n 'An event is appended as a single JSON line to a static filename using default JSONL formatting.',\n in: getEvent('page view', { timestamp: 1700000000000 }),\n settings: { filename: 'events.jsonl' },\n out: [\n [\n 'fs.writeFile',\n 'events.jsonl',\n // JSONL framework contract: JSON.stringify(event) + '\\n'.\n // The test recomputes this from example.in for the jsonl default to\n // avoid duplicating the full event shape here.\n 'jsonl:event',\n ],\n ],\n};\n\n/** Baersch-style TSV log: PHP parity case. */\nexport const tsvBaerschLog: FileStepExample = {\n title: 'TSV log',\n description:\n 'An event is written as a tab-separated line selecting specific fields for a compact access-log style file.',\n in: getEvent('page view', {\n timestamp: 1700000000000,\n data: { title: 'Docs' },\n user: { session: 'sess-1' },\n source: {\n id: 'https://example.com/docs',\n type: 'server',\n previous_id: 'https://example.com/',\n },\n }),\n settings: {\n filename: 'storage/mblog.txt',\n format: 'tsv',\n fields: [\n 'timestamp',\n 'user.session',\n 'name',\n 'source.id',\n 'data.title',\n 'source.previous_id',\n ],\n },\n out: [\n [\n 'fs.writeFile',\n 'storage/mblog.txt',\n '1700000000000\\tsess-1\\tpage view\\thttps://example.com/docs\\tDocs\\thttps://example.com/\\n',\n ],\n ],\n};\n\n/** Tenant sharding via plain key extraction. */\nexport const jsonlTenantShardKey: FileStepExample = {\n title: 'Tenant sharding',\n description:\n 'The filename is resolved from an event field so events are partitioned into per-tenant JSONL files.',\n in: getEvent('custom event', {\n timestamp: 1700000000000,\n data: { tenant: 'acme' },\n }),\n settings: {\n filename: { key: 'data.tenant' },\n format: 'jsonl',\n },\n out: [['fs.writeFile', 'acme', 'jsonl:event']],\n};\n\n/**\n * Daily rotation via a mapping fn. In flow.json this is authored as a\n * `$code:` string; walkerOS compiles it to a function at bundle time. For\n * in-process tests we pass the already-compiled function directly so the\n * example exercises the same code path without needing the bundler.\n */\nexport const jsonlDailyRotation: FileStepExample = {\n title: 'Daily rotation',\n description:\n 'A mapping function derives a date-stamped filename from the event timestamp to rotate JSONL files daily.',\n in: getEvent('order complete', {\n timestamp: Date.UTC(2026, 3, 15, 12, 34, 56),\n data: { id: 'ORD-1' },\n }),\n settings: {\n filename: {\n fn: (value) => {\n const event = value as { timestamp?: number };\n const ts = event.timestamp ?? 0;\n return `events-${new Date(ts).toISOString().slice(0, 10)}.jsonl`;\n },\n },\n format: 'jsonl',\n },\n out: [['fs.writeFile', 'events-2026-04-15.jsonl', 'jsonl:event']],\n};\n\n/** CSV with an object cell. data is JSON-stringified, properly quoted. */\nexport const csvObjectCell: FileStepExample = {\n title: 'CSV with object cell',\n description:\n 'An event is written as a CSV row with the data object JSON-stringified and properly quoted for embedded commas and quotes.',\n in: getEvent('page view', {\n timestamp: 1700000000000,\n data: { title: 'Hello, \"World\"', count: 3 },\n }),\n settings: {\n filename: 'events.csv',\n format: 'csv',\n fields: ['timestamp', 'name', 'data'],\n },\n out: [\n [\n 'fs.writeFile',\n 'events.csv',\n '1700000000000,page view,\"{\"\"title\"\":\"\"Hello, \\\\\"\"World\\\\\"\"\"\",\"\"count\"\":3}\"\\n',\n ],\n ],\n};\n"],"mappings":"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","names":["z"]}
1
+ {"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z.object({\n filename: z\n .union([\n z.string().describe('Static output filename.'),\n z\n .record(z.string(), z.unknown())\n .describe(\n 'Mapping.Value resolved per event (e.g. { key: \"data.tenant\" } or { fn: \"$code:...\" }).',\n ),\n ])\n .describe(\n 'Output filename. Static string or Mapping.Value (e.g. { fn: \"$code:...\" } for daily rotation, { key: \"data.tenant\" } for sharding).',\n ),\n format: z\n .enum(['jsonl', 'tsv', 'csv'])\n .optional()\n .describe('Serialisation format. Defaults to jsonl.'),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Event paths used as columns for tsv/csv formats. Object values are JSON-stringified. Required when format is tsv or csv.',\n ),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const MappingSchema = z\n .object({})\n .describe('No per-rule overrides for the file destination.');\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { Env, FileWriteStream } from '../types';\n\n/**\n * Captured file state for assertions in tests.\n */\nexport interface CapturedFile {\n filename: string;\n lines: string[];\n ended: boolean;\n}\n\n// Narrow helper type aliases so the mock fs is typed explicitly without `any`.\ntype CreateWriteStreamFn = (\n path: string,\n options: { flags: string },\n) => FileWriteStream;\ntype MkdirFn = (path: string, options: { recursive: boolean }) => Promise<void>;\n\nexport interface SpyState {\n captured: Map<string, CapturedFile>;\n mkdirCalls: string[];\n}\n\nexport interface SpyEnv extends Env {\n _spy: SpyState;\n}\n\nfunction makeSpyEnv(): SpyEnv {\n const state: SpyState = {\n captured: new Map(),\n mkdirCalls: [],\n };\n\n const createWriteStream: CreateWriteStreamFn = (path) => {\n const existing = state.captured.get(path);\n const file: CapturedFile = existing ?? {\n filename: path,\n lines: [],\n ended: false,\n };\n if (!existing) state.captured.set(path, file);\n const stream: FileWriteStream = {\n write(chunk) {\n file.lines.push(chunk);\n return true;\n },\n end() {\n file.ended = true;\n },\n };\n return stream;\n };\n\n const mkdir: MkdirFn = async (path) => {\n state.mkdirCalls.push(path);\n };\n\n return {\n _spy: state,\n fs: {\n createWriteStream,\n mkdir,\n },\n };\n}\n\nexport const init: SpyEnv = makeSpyEnv();\nexport const push: SpyEnv = makeSpyEnv();\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\nimport type { Format } from '../types';\n\n/**\n * Raw filename-config shape accepted in step examples and flow.json.\n * Mirrors the runtime Settings.filename but types `fn` as string so\n * `$code:` tags (resolved at bundle time by walkerOS) are expressible\n * here without casting to a function type.\n */\nexport interface FileSettingsJson {\n filename:\n | string\n | {\n key?: string;\n fn?: string | ((value: unknown) => unknown);\n value?: unknown;\n };\n format?: Format;\n fields?: string[];\n}\n\n/**\n * Extended step example that carries destination-level settings for the\n * file destination.\n *\n * `out` is modeled at intent level as a single `['fs.writeFile', path, line]`\n * tuple per event (even though the destination internally uses a persistent\n * createWriteStream + write). This matches the design doc's preference for\n * readable intent-level callable tuples over stream-chain fidelity.\n *\n * Dropped / ignored events produce `out: []`.\n */\nexport type FileStepExample = Flow.StepExample & {\n settings: FileSettingsJson;\n};\n\n/** Default JSONL append. Static filename, all defaults. */\nexport const jsonlDefault: FileStepExample = {\n title: 'JSONL append',\n description:\n 'An event is appended as a single JSON line to a static filename using default JSONL formatting.',\n in: getEvent('page view', { timestamp: 1700000000000 }),\n settings: { filename: 'events.jsonl' },\n out: [\n [\n 'fs.writeFile',\n 'events.jsonl',\n // JSONL framework contract: JSON.stringify(event) + '\\n'.\n // The test recomputes this from example.in for the jsonl default to\n // avoid duplicating the full event shape here.\n 'jsonl:event',\n ],\n ],\n};\n\n/** Baersch-style TSV log: PHP parity case. */\nexport const tsvBaerschLog: FileStepExample = {\n title: 'TSV log',\n description:\n 'An event is written as a tab-separated line selecting specific fields for a compact access-log style file.',\n in: getEvent('page view', {\n timestamp: 1700000000000,\n data: { title: 'Docs' },\n user: { session: 'sess-1' },\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/docs',\n referrer: 'https://example.com/',\n },\n }),\n settings: {\n filename: 'storage/mblog.txt',\n format: 'tsv',\n fields: [\n 'timestamp',\n 'user.session',\n 'name',\n 'source.url',\n 'data.title',\n 'source.referrer',\n ],\n },\n out: [\n [\n 'fs.writeFile',\n 'storage/mblog.txt',\n '1700000000000\\tsess-1\\tpage view\\thttps://example.com/docs\\tDocs\\thttps://example.com/\\n',\n ],\n ],\n};\n\n/** Tenant sharding via plain key extraction. */\nexport const jsonlTenantShardKey: FileStepExample = {\n title: 'Tenant sharding',\n description:\n 'The filename is resolved from an event field so events are partitioned into per-tenant JSONL files.',\n in: getEvent('custom event', {\n timestamp: 1700000000000,\n data: { tenant: 'acme' },\n }),\n settings: {\n filename: { key: 'data.tenant' },\n format: 'jsonl',\n },\n out: [['fs.writeFile', 'acme', 'jsonl:event']],\n};\n\n/**\n * Daily rotation via a mapping fn. In flow.json this is authored as a\n * `$code:` string; walkerOS compiles it to a function at bundle time. For\n * in-process tests we pass the already-compiled function directly so the\n * example exercises the same code path without needing the bundler.\n */\nexport const jsonlDailyRotation: FileStepExample = {\n title: 'Daily rotation',\n description:\n 'A mapping function derives a date-stamped filename from the event timestamp to rotate JSONL files daily.',\n in: getEvent('order complete', {\n timestamp: Date.UTC(2026, 3, 15, 12, 34, 56),\n data: { id: 'ORD-1' },\n }),\n settings: {\n filename: {\n fn: (value) => {\n const event = value as { timestamp?: number };\n const ts = event.timestamp ?? 0;\n return `events-${new Date(ts).toISOString().slice(0, 10)}.jsonl`;\n },\n },\n format: 'jsonl',\n },\n out: [['fs.writeFile', 'events-2026-04-15.jsonl', 'jsonl:event']],\n};\n\n/** CSV with an object cell. data is JSON-stringified, properly quoted. */\nexport const csvObjectCell: FileStepExample = {\n title: 'CSV with object cell',\n description:\n 'An event is written as a CSV row with the data object JSON-stringified and properly quoted for embedded commas and quotes.',\n in: getEvent('page view', {\n timestamp: 1700000000000,\n data: { title: 'Hello, \"World\"', count: 3 },\n }),\n settings: {\n filename: 'events.csv',\n format: 'csv',\n fields: ['timestamp', 'name', 'data'],\n },\n out: [\n [\n 'fs.writeFile',\n 'events.csv',\n '1700000000000,page view,\"{\"\"title\"\":\"\"Hello, \\\\\"\"World\\\\\"\"\"\",\"\"count\"\":3}\"\\n',\n ],\n ],\n};\n"],"mappings":"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","names":["z"]}
@@ -38,7 +38,7 @@ function makeSpyEnv() {
38
38
  };
39
39
  const createWriteStream = (path) => {
40
40
  const existing = state.captured.get(path);
41
- const file = existing != null ? existing : {
41
+ const file = existing ?? {
42
42
  filename: path,
43
43
  lines: [],
44
44
  ended: false
@@ -103,9 +103,10 @@ var tsvBaerschLog = {
103
103
  data: { title: "Docs" },
104
104
  user: { session: "sess-1" },
105
105
  source: {
106
- id: "https://example.com/docs",
107
- type: "server",
108
- previous_id: "https://example.com/"
106
+ type: "browser",
107
+ platform: "web",
108
+ url: "https://example.com/docs",
109
+ referrer: "https://example.com/"
109
110
  }
110
111
  }),
111
112
  settings: {
@@ -115,9 +116,9 @@ var tsvBaerschLog = {
115
116
  "timestamp",
116
117
  "user.session",
117
118
  "name",
118
- "source.id",
119
+ "source.url",
119
120
  "data.title",
120
- "source.previous_id"
121
+ "source.referrer"
121
122
  ]
122
123
  },
123
124
  out: [
@@ -151,9 +152,8 @@ var jsonlDailyRotation = {
151
152
  settings: {
152
153
  filename: {
153
154
  fn: (value) => {
154
- var _a;
155
155
  const event = value;
156
- const ts = (_a = event.timestamp) != null ? _a : 0;
156
+ const ts = event.timestamp ?? 0;
157
157
  return `events-${new Date(ts).toISOString().slice(0, 10)}.jsonl`;
158
158
  }
159
159
  },
@@ -17,7 +17,7 @@ function makeSpyEnv() {
17
17
  };
18
18
  const createWriteStream = (path) => {
19
19
  const existing = state.captured.get(path);
20
- const file = existing != null ? existing : {
20
+ const file = existing ?? {
21
21
  filename: path,
22
22
  lines: [],
23
23
  ended: false
@@ -82,9 +82,10 @@ var tsvBaerschLog = {
82
82
  data: { title: "Docs" },
83
83
  user: { session: "sess-1" },
84
84
  source: {
85
- id: "https://example.com/docs",
86
- type: "server",
87
- previous_id: "https://example.com/"
85
+ type: "browser",
86
+ platform: "web",
87
+ url: "https://example.com/docs",
88
+ referrer: "https://example.com/"
88
89
  }
89
90
  }),
90
91
  settings: {
@@ -94,9 +95,9 @@ var tsvBaerschLog = {
94
95
  "timestamp",
95
96
  "user.session",
96
97
  "name",
97
- "source.id",
98
+ "source.url",
98
99
  "data.title",
99
- "source.previous_id"
100
+ "source.referrer"
100
101
  ]
101
102
  },
102
103
  out: [
@@ -130,9 +131,8 @@ var jsonlDailyRotation = {
130
131
  settings: {
131
132
  filename: {
132
133
  fn: (value) => {
133
- var _a;
134
134
  const event = value;
135
- const ts = (_a = event.timestamp) != null ? _a : 0;
135
+ const ts = event.timestamp ?? 0;
136
136
  return `events-${new Date(ts).toISOString().slice(0, 10)}.jsonl`;
137
137
  }
138
138
  },
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var mod,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{DestinationFile:()=>types_exports,default:()=>index_default,destinationFile:()=>destinationFile}),module.exports=(mod=index_exports,((to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to})(__defProp({},"__esModule",{value:!0}),mod));var import_core2=require("@walkeros/core"),import_node_fs=require("fs"),import_promises=require("fs/promises"),import_node_path=require("path"),import_core=require("@walkeros/core");async function ensureHandle(state,filename){const cached=state.handles.get(filename);if(cached)return cached;await state.fs.mkdir((0,import_node_path.dirname)(filename)||".",{recursive:!0});const handle=state.fs.createWriteStream(filename,{flags:"a"});return state.handles.set(filename,handle),handle}function serialize(event,format,fields){if("jsonl"===format)return JSON.stringify(event)+"\n";if(!fields||0===fields.length)throw new Error(`file destination: format '${format}' requires non-empty 'fields' setting`);const cells=fields.map(path=>{return null==(value=(0,import_core.getByPath)(event,path))?"":(0,import_core.isObject)(value)||Array.isArray(value)?JSON.stringify(value):"string"==typeof value?value:"number"==typeof value||"boolean"==typeof value?String(value):JSON.stringify(value);var value});return"tsv"===format?cells.join("\t")+"\n":cells.map(csvEscape).join(",")+"\n"}function csvEscape(value){return/[",\r\n]/.test(value)?`"${value.replace(/"/g,'""')}"`:value}var states=new Map;function getState(id){return states.get(id)}var types_exports={},destinationFile={type:"file",config:{},init:async({id:id,config:config,env:env,logger:logger})=>{var _a,_b;const partial=null!=(_a=null==config?void 0:config.settings)?_a:{};if(void 0===partial.filename||null===partial.filename)return logger.throw("file destination: 'filename' is required (string or Mapping.Value)"),!1;if(!(0,import_core2.isString)(partial.filename)&&!(0,import_core2.isObject)(partial.filename)&&!Array.isArray(partial.filename))return logger.throw("file destination: 'filename' must be a string or a Mapping.Value"),!1;const format=null!=(_b=partial.format)?_b:"jsonl";if(!("tsv"!==format&&"csv"!==format||partial.fields&&0!==partial.fields.length))return logger.throw(`file destination: format '${format}' requires non-empty 'fields'`),!1;const settings={filename:partial.filename,format:format,fields:partial.fields},state=function(env){const fsOverride=null==env?void 0:env.fs,defaultFs={createWriteStream:(path,options)=>(0,import_node_fs.createWriteStream)(path,options),mkdir:async(path,options)=>{await(0,import_promises.mkdir)(path,options)}};return{handles:new Map,fs:null!=fsOverride?fsOverride:defaultFs}}(env);if((0,import_core2.isString)(settings.filename))try{await ensureHandle(state,settings.filename)}catch(err){return logger.throw(`file destination: failed to open '${settings.filename}': ${err instanceof Error?err.message:String(err)}`),!1}return function(id,state){states.set(id,state)}(id,state),{...config,settings:settings}},push:async(event,{id:id,config:config,collector:collector,logger:logger})=>{var _a;const settings=config.settings;if(!settings)return void logger.warn("file destination: settings missing");const state=getState(id);if(!state)return void logger.warn("file destination: state missing, init may have failed");let filename;if((0,import_core2.isString)(settings.filename))filename=settings.filename;else{const resolved=await(0,import_core2.getMappingValue)(event,settings.filename,{collector:collector});if(!(0,import_core2.isString)(resolved)||0===resolved.length)return void logger.warn("file destination: dynamic filename resolved to empty or non-string, dropping event");filename=resolved}try{(await ensureHandle(state,filename)).write(serialize(event,null!=(_a=settings.format)?_a:"jsonl",settings.fields))}catch(err){logger.warn(`file destination: write failed for ${filename}: ${err instanceof Error?err.message:String(err)}`)}},async destroy({id:id}){const state=getState(id);if(state){for(const handle of state.handles.values())try{handle.end()}catch(e){}state.handles.clear(),function(id){states.delete(id)}(id)}}},index_default=destinationFile;//# sourceMappingURL=index.js.map
1
+ "use strict";var mod,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{DestinationFile:()=>types_exports,default:()=>index_default,destinationFile:()=>destinationFile}),module.exports=(mod=index_exports,((to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to})(__defProp({},"__esModule",{value:!0}),mod));var import_core2=require("@walkeros/core"),import_node_fs=require("fs"),import_promises=require("fs/promises"),import_node_path=require("path"),import_core=require("@walkeros/core");async function ensureHandle(state,filename){const cached=state.handles.get(filename);if(cached)return cached;await state.fs.mkdir((0,import_node_path.dirname)(filename)||".",{recursive:!0});const handle=state.fs.createWriteStream(filename,{flags:"a"});return state.handles.set(filename,handle),handle}function serialize(event,format,fields){if("jsonl"===format)return JSON.stringify(event)+"\n";if(!fields||0===fields.length)throw new Error(`file destination: format '${format}' requires non-empty 'fields' setting`);const cells=fields.map(path=>{return null==(value=(0,import_core.getByPath)(event,path))?"":(0,import_core.isObject)(value)||Array.isArray(value)?JSON.stringify(value):"string"==typeof value?value:"number"==typeof value||"boolean"==typeof value?String(value):JSON.stringify(value);var value});return"tsv"===format?cells.join("\t")+"\n":cells.map(csvEscape).join(",")+"\n"}function csvEscape(value){return/[",\r\n]/.test(value)?`"${value.replace(/"/g,'""')}"`:value}var states=new Map;function getState(id){return states.get(id)}var types_exports={},destinationFile={type:"file",config:{},init:async({id:id,config:config,env:env,logger:logger})=>{const partial=config?.settings??{};if(void 0===partial.filename||null===partial.filename)return logger.throw("file destination: 'filename' is required (string or Mapping.Value)"),!1;if(!(0,import_core2.isString)(partial.filename)&&!(0,import_core2.isObject)(partial.filename)&&!Array.isArray(partial.filename))return logger.throw("file destination: 'filename' must be a string or a Mapping.Value"),!1;const format=partial.format??"jsonl";if(!("tsv"!==format&&"csv"!==format||partial.fields&&0!==partial.fields.length))return logger.throw(`file destination: format '${format}' requires non-empty 'fields'`),!1;const settings={filename:partial.filename,format:format,fields:partial.fields},state=function(env){const fsOverride=env?.fs,defaultFs={createWriteStream:(path,options)=>(0,import_node_fs.createWriteStream)(path,options),mkdir:async(path,options)=>{await(0,import_promises.mkdir)(path,options)}};return{handles:new Map,fs:fsOverride??defaultFs}}(env);if((0,import_core2.isString)(settings.filename))try{await ensureHandle(state,settings.filename)}catch(err){return logger.throw(`file destination: failed to open '${settings.filename}': ${err instanceof Error?err.message:String(err)}`),!1}return function(id,state){states.set(id,state)}(id,state),{...config,settings:settings}},push:async(event,{id:id,config:config,collector:collector,logger:logger})=>{const settings=config.settings;if(!settings)return void logger.warn("file destination: settings missing");const state=getState(id);if(!state)return void logger.warn("file destination: state missing, init may have failed");let filename;if((0,import_core2.isString)(settings.filename))filename=settings.filename;else{const resolved=await(0,import_core2.getMappingValue)(event,settings.filename,{collector:collector});if(!(0,import_core2.isString)(resolved)||0===resolved.length)return void logger.warn("file destination: dynamic filename resolved to empty or non-string, dropping event");filename=resolved}try{(await ensureHandle(state,filename)).write(serialize(event,settings.format??"jsonl",settings.fields))}catch(err){logger.warn(`file destination: write failed for ${filename}: ${err instanceof Error?err.message:String(err)}`)}},async destroy({id:id}){const state=getState(id);if(state){for(const handle of state.handles.values())try{handle.end()}catch{}state.handles.clear(),function(id){states.delete(id)}(id)}}},index_default=destinationFile;//# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +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":["import_core","nodeCreateWriteStream","nodeMkdir"]}
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":["import_core","nodeCreateWriteStream","nodeMkdir"]}
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{getMappingValue,isString,isObject as isObject2}from"@walkeros/core";import{createWriteStream as nodeCreateWriteStream}from"fs";import{mkdir as nodeMkdir}from"fs/promises";import{dirname}from"path";import{getByPath,isObject}from"@walkeros/core";async function ensureHandle(state,filename){const cached=state.handles.get(filename);if(cached)return cached;await state.fs.mkdir(dirname(filename)||".",{recursive:!0});const handle=state.fs.createWriteStream(filename,{flags:"a"});return state.handles.set(filename,handle),handle}function serialize(event,format,fields){if("jsonl"===format)return JSON.stringify(event)+"\n";if(!fields||0===fields.length)throw new Error(`file destination: format '${format}' requires non-empty 'fields' setting`);const cells=fields.map(path=>{return null==(value=getByPath(event,path))?"":isObject(value)||Array.isArray(value)?JSON.stringify(value):"string"==typeof value?value:"number"==typeof value||"boolean"==typeof value?String(value):JSON.stringify(value);var value});return"tsv"===format?cells.join("\t")+"\n":cells.map(csvEscape).join(",")+"\n"}function csvEscape(value){return/[",\r\n]/.test(value)?`"${value.replace(/"/g,'""')}"`:value}var states=new Map;function getState(id){return states.get(id)}var types_exports={},destinationFile={type:"file",config:{},init:async({id:id,config:config,env:env,logger:logger})=>{var _a,_b;const partial=null!=(_a=null==config?void 0:config.settings)?_a:{};if(void 0===partial.filename||null===partial.filename)return logger.throw("file destination: 'filename' is required (string or Mapping.Value)"),!1;if(!isString(partial.filename)&&!isObject2(partial.filename)&&!Array.isArray(partial.filename))return logger.throw("file destination: 'filename' must be a string or a Mapping.Value"),!1;const format=null!=(_b=partial.format)?_b:"jsonl";if(!("tsv"!==format&&"csv"!==format||partial.fields&&0!==partial.fields.length))return logger.throw(`file destination: format '${format}' requires non-empty 'fields'`),!1;const settings={filename:partial.filename,format:format,fields:partial.fields},state=function(env){const fsOverride=null==env?void 0:env.fs,defaultFs={createWriteStream:(path,options)=>nodeCreateWriteStream(path,options),mkdir:async(path,options)=>{await nodeMkdir(path,options)}};return{handles:new Map,fs:null!=fsOverride?fsOverride:defaultFs}}(env);if(isString(settings.filename))try{await ensureHandle(state,settings.filename)}catch(err){return logger.throw(`file destination: failed to open '${settings.filename}': ${err instanceof Error?err.message:String(err)}`),!1}return function(id,state){states.set(id,state)}(id,state),{...config,settings:settings}},push:async(event,{id:id,config:config,collector:collector,logger:logger})=>{var _a;const settings=config.settings;if(!settings)return void logger.warn("file destination: settings missing");const state=getState(id);if(!state)return void logger.warn("file destination: state missing, init may have failed");let filename;if(isString(settings.filename))filename=settings.filename;else{const resolved=await getMappingValue(event,settings.filename,{collector:collector});if(!isString(resolved)||0===resolved.length)return void logger.warn("file destination: dynamic filename resolved to empty or non-string, dropping event");filename=resolved}try{(await ensureHandle(state,filename)).write(serialize(event,null!=(_a=settings.format)?_a:"jsonl",settings.fields))}catch(err){logger.warn(`file destination: write failed for ${filename}: ${err instanceof Error?err.message:String(err)}`)}},async destroy({id:id}){const state=getState(id);if(state){for(const handle of state.handles.values())try{handle.end()}catch(e){}state.handles.clear(),function(id){states.delete(id)}(id)}}},index_default=destinationFile;export{types_exports as DestinationFile,index_default as default,destinationFile};//# sourceMappingURL=index.mjs.map
1
+ import{getMappingValue,isString,isObject as isObject2}from"@walkeros/core";import{createWriteStream as nodeCreateWriteStream}from"fs";import{mkdir as nodeMkdir}from"fs/promises";import{dirname}from"path";import{getByPath,isObject}from"@walkeros/core";async function ensureHandle(state,filename){const cached=state.handles.get(filename);if(cached)return cached;await state.fs.mkdir(dirname(filename)||".",{recursive:!0});const handle=state.fs.createWriteStream(filename,{flags:"a"});return state.handles.set(filename,handle),handle}function serialize(event,format,fields){if("jsonl"===format)return JSON.stringify(event)+"\n";if(!fields||0===fields.length)throw new Error(`file destination: format '${format}' requires non-empty 'fields' setting`);const cells=fields.map(path=>{return null==(value=getByPath(event,path))?"":isObject(value)||Array.isArray(value)?JSON.stringify(value):"string"==typeof value?value:"number"==typeof value||"boolean"==typeof value?String(value):JSON.stringify(value);var value});return"tsv"===format?cells.join("\t")+"\n":cells.map(csvEscape).join(",")+"\n"}function csvEscape(value){return/[",\r\n]/.test(value)?`"${value.replace(/"/g,'""')}"`:value}var states=new Map;function getState(id){return states.get(id)}var types_exports={},destinationFile={type:"file",config:{},init:async({id:id,config:config,env:env,logger:logger})=>{const partial=config?.settings??{};if(void 0===partial.filename||null===partial.filename)return logger.throw("file destination: 'filename' is required (string or Mapping.Value)"),!1;if(!isString(partial.filename)&&!isObject2(partial.filename)&&!Array.isArray(partial.filename))return logger.throw("file destination: 'filename' must be a string or a Mapping.Value"),!1;const format=partial.format??"jsonl";if(!("tsv"!==format&&"csv"!==format||partial.fields&&0!==partial.fields.length))return logger.throw(`file destination: format '${format}' requires non-empty 'fields'`),!1;const settings={filename:partial.filename,format:format,fields:partial.fields},state=function(env){const fsOverride=env?.fs,defaultFs={createWriteStream:(path,options)=>nodeCreateWriteStream(path,options),mkdir:async(path,options)=>{await nodeMkdir(path,options)}};return{handles:new Map,fs:fsOverride??defaultFs}}(env);if(isString(settings.filename))try{await ensureHandle(state,settings.filename)}catch(err){return logger.throw(`file destination: failed to open '${settings.filename}': ${err instanceof Error?err.message:String(err)}`),!1}return function(id,state){states.set(id,state)}(id,state),{...config,settings:settings}},push:async(event,{id:id,config:config,collector:collector,logger:logger})=>{const settings=config.settings;if(!settings)return void logger.warn("file destination: settings missing");const state=getState(id);if(!state)return void logger.warn("file destination: state missing, init may have failed");let filename;if(isString(settings.filename))filename=settings.filename;else{const resolved=await getMappingValue(event,settings.filename,{collector:collector});if(!isString(resolved)||0===resolved.length)return void logger.warn("file destination: dynamic filename resolved to empty or non-string, dropping event");filename=resolved}try{(await ensureHandle(state,filename)).write(serialize(event,settings.format??"jsonl",settings.fields))}catch(err){logger.warn(`file destination: write failed for ${filename}: ${err instanceof Error?err.message:String(err)}`)}},async destroy({id:id}){const state=getState(id);if(state){for(const handle of state.handles.values())try{handle.end()}catch{}state.handles.clear(),function(id){states.delete(id)}(id)}}},index_default=destinationFile;export{types_exports as DestinationFile,index_default as default,destinationFile};//# sourceMappingURL=index.mjs.map
@@ -1 +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"]}
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"]}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$meta": {
3
3
  "package": "@walkeros/server-destination-file",
4
- "version": "3.4.2",
4
+ "version": "4.0.0-next-1777882869103",
5
5
  "type": "destination",
6
6
  "platform": [
7
7
  "server"
@@ -70,7 +70,7 @@
70
70
  },
71
71
  "fs": {
72
72
  "createWriteStream": {
73
- "$code": "t=>{const s=e.captured.get(t),i=null!=s?s:{filename:t,lines:[],ended:!1};s||e.captured.set(t,i);return{write:e=>(i.lines.push(e),!0),end(){i.ended=!0}}}"
73
+ "$code": "t=>{const s=e.captured.get(t),a=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
74
  },
75
75
  "mkdir": {
76
76
  "$code": "async t=>{e.mkdirCalls.push(t)}"
@@ -84,7 +84,7 @@
84
84
  },
85
85
  "fs": {
86
86
  "createWriteStream": {
87
- "$code": "t=>{const s=e.captured.get(t),i=null!=s?s:{filename:t,lines:[],ended:!1};s||e.captured.set(t,i);return{write:e=>(i.lines.push(e),!0),end(){i.ended=!0}}}"
87
+ "$code": "t=>{const s=e.captured.get(t),a=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
88
  },
89
89
  "mkdir": {
90
90
  "$code": "async t=>{e.mkdirCalls.push(t)}"
@@ -124,35 +124,21 @@
124
124
  "entity": "child",
125
125
  "data": {
126
126
  "is": "subordinated"
127
- },
128
- "nested": [],
129
- "context": {
130
- "element": [
131
- "child",
132
- 0
133
- ]
134
127
  }
135
128
  }
136
129
  ],
137
130
  "consent": {
138
131
  "functional": true
139
132
  },
140
- "id": "1700000000000-gr0up-1",
133
+ "id": "272ca3ed6dd46278",
141
134
  "trigger": "load",
142
135
  "entity": "page",
143
136
  "action": "view",
144
137
  "timestamp": 1700000000000,
145
138
  "timing": 3.14,
146
- "group": "gr0up",
147
- "count": 1,
148
- "version": {
149
- "source": "3.4.2",
150
- "tagging": 1
151
- },
152
139
  "source": {
153
- "type": "web",
154
- "id": "https://localhost:80",
155
- "previous_id": "http://remotehost:9001"
140
+ "type": "collector",
141
+ "schema": "4"
156
142
  }
157
143
  },
158
144
  "settings": {
@@ -248,28 +234,21 @@
248
234
  "consent": {
249
235
  "functional": true
250
236
  },
251
- "id": "1776256496000-gr0up-1",
237
+ "id": "039a2aaa5e101df9",
252
238
  "trigger": "load",
253
239
  "entity": "order",
254
240
  "action": "complete",
255
241
  "timestamp": 1776256496000,
256
242
  "timing": 3.14,
257
- "group": "gr0up",
258
- "count": 1,
259
- "version": {
260
- "source": "3.4.2",
261
- "tagging": 1
262
- },
263
243
  "source": {
264
- "type": "web",
265
- "id": "https://localhost:80",
266
- "previous_id": "http://remotehost:9001"
244
+ "type": "collector",
245
+ "schema": "4"
267
246
  }
268
247
  },
269
248
  "settings": {
270
249
  "filename": {
271
250
  "fn": {
272
- "$code": "e=>{var t;const s=null!=(t=e.timestamp)?t:0;return`events-${new Date(s).toISOString().slice(0,10)}.jsonl`}"
251
+ "$code": "e=>{const t=e.timestamp??0;return`events-${new Date(t).toISOString().slice(0,10)}.jsonl`}"
273
252
  }
274
253
  },
275
254
  "format": "jsonl"
@@ -317,35 +296,21 @@
317
296
  "entity": "child",
318
297
  "data": {
319
298
  "is": "subordinated"
320
- },
321
- "nested": [],
322
- "context": {
323
- "element": [
324
- "child",
325
- 0
326
- ]
327
299
  }
328
300
  }
329
301
  ],
330
302
  "consent": {
331
303
  "functional": true
332
304
  },
333
- "id": "1700000000000-gr0up-1",
305
+ "id": "0786bf747959526a",
334
306
  "trigger": "load",
335
307
  "entity": "page",
336
308
  "action": "view",
337
309
  "timestamp": 1700000000000,
338
310
  "timing": 3.14,
339
- "group": "gr0up",
340
- "count": 1,
341
- "version": {
342
- "source": "3.4.2",
343
- "tagging": 1
344
- },
345
311
  "source": {
346
- "type": "web",
347
- "id": "https://localhost:80",
348
- "previous_id": "http://remotehost:9001"
312
+ "type": "collector",
313
+ "schema": "4"
349
314
  }
350
315
  },
351
316
  "settings": {
@@ -389,35 +354,21 @@
389
354
  "entity": "child",
390
355
  "data": {
391
356
  "is": "subordinated"
392
- },
393
- "nested": [],
394
- "context": {
395
- "element": [
396
- "child",
397
- 0
398
- ]
399
357
  }
400
358
  }
401
359
  ],
402
360
  "consent": {
403
361
  "functional": true
404
362
  },
405
- "id": "1700000000000-gr0up-1",
363
+ "id": "c944567f5aa51c10",
406
364
  "trigger": "test",
407
365
  "entity": "custom",
408
366
  "action": "event",
409
367
  "timestamp": 1700000000000,
410
368
  "timing": 3.14,
411
- "group": "gr0up",
412
- "count": 1,
413
- "version": {
414
- "source": "3.4.2",
415
- "tagging": 1
416
- },
417
369
  "source": {
418
- "type": "web",
419
- "id": "https://localhost:80",
420
- "previous_id": "http://remotehost:9001"
370
+ "type": "collector",
371
+ "schema": "4"
421
372
  }
422
373
  },
423
374
  "settings": {
@@ -462,35 +413,23 @@
462
413
  "entity": "child",
463
414
  "data": {
464
415
  "is": "subordinated"
465
- },
466
- "nested": [],
467
- "context": {
468
- "element": [
469
- "child",
470
- 0
471
- ]
472
416
  }
473
417
  }
474
418
  ],
475
419
  "consent": {
476
420
  "functional": true
477
421
  },
478
- "id": "1700000000000-gr0up-1",
422
+ "id": "b4e45b7e92d63477",
479
423
  "trigger": "load",
480
424
  "entity": "page",
481
425
  "action": "view",
482
426
  "timestamp": 1700000000000,
483
427
  "timing": 3.14,
484
- "group": "gr0up",
485
- "count": 1,
486
- "version": {
487
- "source": "3.4.2",
488
- "tagging": 1
489
- },
490
428
  "source": {
491
- "id": "https://example.com/docs",
492
- "type": "server",
493
- "previous_id": "https://example.com/"
429
+ "type": "browser",
430
+ "platform": "web",
431
+ "url": "https://example.com/docs",
432
+ "referrer": "https://example.com/"
494
433
  }
495
434
  },
496
435
  "settings": {
@@ -500,9 +439,9 @@
500
439
  "timestamp",
501
440
  "user.session",
502
441
  "name",
503
- "source.id",
442
+ "source.url",
504
443
  "data.title",
505
- "source.previous_id"
444
+ "source.referrer"
506
445
  ]
507
446
  },
508
447
  "out": [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@walkeros/server-destination-file",
3
3
  "description": "Local file sink for walkerOS server flows (JSONL, TSV, CSV)",
4
- "version": "3.4.2",
4
+ "version": "4.0.0-next-1777882869103",
5
5
  "license": "MIT",
6
6
  "exports": {
7
7
  ".": {
@@ -34,11 +34,11 @@
34
34
  "update": "npx npm-check-updates -u && npm update"
35
35
  },
36
36
  "dependencies": {
37
- "@walkeros/core": "3.4.2",
38
- "@walkeros/server-core": "3.4.2"
37
+ "@walkeros/core": "4.0.0-next-1777882869103",
38
+ "@walkeros/server-core": "4.0.0-next-1777882869103"
39
39
  },
40
40
  "devDependencies": {
41
- "@walkeros/collector": "3.4.2"
41
+ "@walkeros/collector": "4.0.0-next-1777882869103"
42
42
  },
43
43
  "repository": {
44
44
  "url": "git+https://github.com/elbwalker/walkerOS.git",