prompt-identifiers-baml 0.1.2 → 0.1.3

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/index.mjs CHANGED
@@ -1,204 +1,244 @@
1
- // src/index.ts
2
1
  import { createEncodeState, decode, encode } from "prompt-identifiers";
2
+ //#region src/index.ts
3
+ /**
4
+ * prompt-identifiers-baml - BAML wrapper for automatic ID encoding/decoding
5
+ *
6
+ * Wraps BAML-generated TypeScript functions to automatically encode IDs
7
+ * in inputs and decode them in outputs.
8
+ */
9
+ /**
10
+ * Parse a field path into segments.
11
+ * 'items[].id' -> ['items', '[]', 'id']
12
+ */
3
13
  function parseFieldPath(path) {
4
- const segments = [];
5
- let current = "";
6
- for (let i = 0; i < path.length; i++) {
7
- const char = path[i];
8
- if (char === ".") {
9
- if (current) {
10
- segments.push(current);
11
- current = "";
12
- }
13
- } else if (char === "[" && path[i + 1] === "]") {
14
- if (current) {
15
- segments.push(current);
16
- current = "";
17
- }
18
- segments.push("[]");
19
- i++;
20
- } else {
21
- current += char;
22
- }
23
- }
24
- if (current) {
25
- segments.push(current);
26
- }
27
- return segments;
14
+ const segments = [];
15
+ let current = "";
16
+ for (let i = 0; i < path.length; i++) {
17
+ const char = path[i];
18
+ if (char === ".") {
19
+ if (current) {
20
+ segments.push(current);
21
+ current = "";
22
+ }
23
+ } else if (char === "[" && path[i + 1] === "]") {
24
+ if (current) {
25
+ segments.push(current);
26
+ current = "";
27
+ }
28
+ segments.push("[]");
29
+ i++;
30
+ } else current += char;
31
+ }
32
+ if (current) segments.push(current);
33
+ return segments;
28
34
  }
35
+ /**
36
+ * Check if a value at the given path should be encoded.
37
+ */
29
38
  function matchesFieldPath(currentPath, targetSegments) {
30
- if (currentPath.length !== targetSegments.length) {
31
- return false;
32
- }
33
- for (let i = 0; i < targetSegments.length; i++) {
34
- const target = targetSegments[i];
35
- const current = currentPath[i];
36
- if (target === "[]") {
37
- if (!/^\d+$/.test(current)) {
38
- return false;
39
- }
40
- } else if (target !== current) {
41
- return false;
42
- }
43
- }
44
- return true;
39
+ if (currentPath.length !== targetSegments.length) return false;
40
+ for (let i = 0; i < targetSegments.length; i++) {
41
+ const target = targetSegments[i];
42
+ const current = currentPath[i];
43
+ if (target === "[]") {
44
+ if (!/^\d+$/.test(current)) return false;
45
+ } else if (target !== current) return false;
46
+ }
47
+ return true;
45
48
  }
49
+ /**
50
+ * Deep traverse and encode IDs in an object.
51
+ * Returns a new object with IDs replaced by placeholders.
52
+ */
46
53
  function deepEncode(value, ctx, path = []) {
47
- if (value === null || value === void 0) {
48
- return value;
49
- }
50
- if (typeof value === "string") {
51
- const shouldEncode = ctx.fieldPaths === null || ctx.fieldPaths.some((fp) => matchesFieldPath(path, fp));
52
- if (!shouldEncode) {
53
- return value;
54
- }
55
- return encode(value, ctx.config, ctx.state).encoded;
56
- }
57
- if (Array.isArray(value)) {
58
- return value.map((item, index) => deepEncode(item, ctx, [...path, String(index)]));
59
- }
60
- if (typeof value === "object") {
61
- const result = {};
62
- for (const [key, val] of Object.entries(value)) {
63
- result[key] = deepEncode(val, ctx, [...path, key]);
64
- }
65
- return result;
66
- }
67
- return value;
54
+ if (value === null || value === void 0) return value;
55
+ if (typeof value === "string") {
56
+ if (!(ctx.fieldPaths === null || ctx.fieldPaths.some((fp) => matchesFieldPath(path, fp)))) return value;
57
+ return encode(value, ctx.config, ctx.state).encoded;
58
+ }
59
+ if (Array.isArray(value)) return value.map((item, index) => deepEncode(item, ctx, [...path, String(index)]));
60
+ if (typeof value === "object") {
61
+ const result = {};
62
+ for (const [key, val] of Object.entries(value)) result[key] = deepEncode(val, ctx, [...path, key]);
63
+ return result;
64
+ }
65
+ return value;
68
66
  }
67
+ /**
68
+ * Deep traverse and decode placeholders in an object.
69
+ * Returns a new object with placeholders replaced by original IDs.
70
+ */
69
71
  function deepDecode(value, mapping, countRef) {
70
- if (value === null || value === void 0) {
71
- return value;
72
- }
73
- if (typeof value === "string") {
74
- const decoded = decode(value, mapping);
75
- if (decoded !== value) {
76
- countRef.count++;
77
- }
78
- return decoded;
79
- }
80
- if (Array.isArray(value)) {
81
- return value.map((item) => deepDecode(item, mapping, countRef));
82
- }
83
- if (typeof value === "object") {
84
- const result = {};
85
- for (const [key, val] of Object.entries(value)) {
86
- result[key] = deepDecode(val, mapping, countRef);
87
- }
88
- return result;
89
- }
90
- return value;
72
+ if (value === null || value === void 0) return value;
73
+ if (typeof value === "string") {
74
+ const decoded = decode(value, mapping);
75
+ if (decoded !== value) countRef.count++;
76
+ return decoded;
77
+ }
78
+ if (Array.isArray(value)) return value.map((item) => deepDecode(item, mapping, countRef));
79
+ if (typeof value === "object") {
80
+ const result = {};
81
+ for (const [key, val] of Object.entries(value)) result[key] = deepDecode(val, mapping, countRef);
82
+ return result;
83
+ }
84
+ return value;
91
85
  }
86
+ /**
87
+ * Wrap a BAML function to automatically encode IDs in inputs and decode them in outputs.
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * import { wrapBamlFunction } from 'prompt-identifiers-baml';
92
+ * import { b } from './baml_client';
93
+ *
94
+ * const analyzeUser = wrapBamlFunction(b.AnalyzeUser, {
95
+ * config: { inputFormat: 'UUID', outputFormat: 'SafeNumeric' },
96
+ * encodeFields: ['user_id', 'items[].id'], // Optional: specific fields
97
+ * });
98
+ *
99
+ * // Use normally - IDs are auto-encoded/decoded
100
+ * const result = await analyzeUser({
101
+ * user_id: '123e4567-e89b-42d3-a456-426655440000',
102
+ * items: [{ id: '987fcdeb-51a2-43f7-8d9c-0123456789ab', name: 'test' }]
103
+ * });
104
+ * ```
105
+ */
92
106
  function wrapBamlFunction(fn, options) {
93
- const { config, encodeFields, onEncode, onDecode, debug } = options;
94
- const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;
95
- return async (input) => {
96
- const ctx = {
97
- config,
98
- fieldPaths,
99
- state: createEncodeState()
100
- };
101
- const startEncode = debug ? performance.now() : 0;
102
- const encodedInput = deepEncode(input, ctx);
103
- const encodeDurationMs = debug ? performance.now() - startEncode : 0;
104
- onEncode?.({
105
- mapping: ctx.state.mapping,
106
- ...debug && {
107
- debugData: {
108
- encodedCount: Object.keys(ctx.state.mapping).length,
109
- input,
110
- output: encodedInput,
111
- durationMs: encodeDurationMs
112
- }
113
- }
114
- });
115
- const output = await fn(encodedInput);
116
- const countRef = { count: 0 };
117
- const startDecode = debug ? performance.now() : 0;
118
- const decodedOutput = deepDecode(output, ctx.state.mapping, countRef);
119
- const decodeDurationMs = debug ? performance.now() - startDecode : 0;
120
- onDecode?.({
121
- ...debug && {
122
- debugData: {
123
- decodedCount: countRef.count,
124
- input: output,
125
- output: decodedOutput,
126
- durationMs: decodeDurationMs
127
- }
128
- }
129
- });
130
- return decodedOutput;
131
- };
107
+ const { config, encodeFields, onEncode, onDecode, debug } = options;
108
+ const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;
109
+ return async (input) => {
110
+ const ctx = {
111
+ config,
112
+ fieldPaths,
113
+ state: createEncodeState()
114
+ };
115
+ const startEncode = debug ? performance.now() : 0;
116
+ const encodedInput = deepEncode(input, ctx);
117
+ const encodeDurationMs = debug ? performance.now() - startEncode : 0;
118
+ onEncode?.({
119
+ mapping: ctx.state.mapping,
120
+ ...debug && { debugData: {
121
+ encodedCount: Object.keys(ctx.state.mapping).length,
122
+ input,
123
+ output: encodedInput,
124
+ durationMs: encodeDurationMs
125
+ } }
126
+ });
127
+ const output = await fn(encodedInput);
128
+ const countRef = { count: 0 };
129
+ const startDecode = debug ? performance.now() : 0;
130
+ const decodedOutput = deepDecode(output, ctx.state.mapping, countRef);
131
+ const decodeDurationMs = debug ? performance.now() - startDecode : 0;
132
+ onDecode?.({ ...debug && { debugData: {
133
+ decodedCount: countRef.count,
134
+ input: output,
135
+ output: decodedOutput,
136
+ durationMs: decodeDurationMs
137
+ } } });
138
+ return decodedOutput;
139
+ };
132
140
  }
141
+ /**
142
+ * Wrap a BAML streaming function to automatically encode IDs in inputs
143
+ * and decode them in outputs (both partial and final).
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * import { wrapBamlStreamingFunction } from 'prompt-identifiers-baml';
148
+ * import { b } from './baml_client';
149
+ *
150
+ * const streamAnalysis = wrapBamlStreamingFunction(b.stream.AnalyzeUser, {
151
+ * config: { inputFormat: 'UUID', outputFormat: 'SafeNumeric' },
152
+ * });
153
+ *
154
+ * for await (const partial of streamAnalysis({ user_id: 'uuid-here' })) {
155
+ * console.log(partial); // IDs decoded in real-time
156
+ * }
157
+ * ```
158
+ */
133
159
  function wrapBamlStreamingFunction(fn, options) {
134
- const { config, encodeFields, onEncode, onDecode, debug } = options;
135
- const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;
136
- return async function* (input) {
137
- const ctx = {
138
- config,
139
- fieldPaths,
140
- state: createEncodeState()
141
- };
142
- const startEncode = debug ? performance.now() : 0;
143
- const encodedInput = deepEncode(input, ctx);
144
- const encodeDurationMs = debug ? performance.now() - startEncode : 0;
145
- onEncode?.({
146
- mapping: ctx.state.mapping,
147
- ...debug && {
148
- debugData: {
149
- encodedCount: Object.keys(ctx.state.mapping).length,
150
- input,
151
- output: encodedInput,
152
- durationMs: encodeDurationMs
153
- }
154
- }
155
- });
156
- const generator = fn(encodedInput);
157
- let totalDecoded = 0;
158
- const startDecode = debug ? performance.now() : 0;
159
- while (true) {
160
- const { value, done } = await generator.next();
161
- if (done) {
162
- const countRef2 = { count: 0 };
163
- const decodedValue2 = deepDecode(value, ctx.state.mapping, countRef2);
164
- totalDecoded += countRef2.count;
165
- const decodeDurationMs = debug ? performance.now() - startDecode : 0;
166
- onDecode?.({
167
- ...debug && {
168
- debugData: {
169
- decodedCount: totalDecoded,
170
- input: value,
171
- output: decodedValue2,
172
- durationMs: decodeDurationMs
173
- }
174
- }
175
- });
176
- return decodedValue2;
177
- }
178
- const countRef = { count: 0 };
179
- const decodedValue = deepDecode(value, ctx.state.mapping, countRef);
180
- totalDecoded += countRef.count;
181
- yield decodedValue;
182
- }
183
- };
160
+ const { config, encodeFields, onEncode, onDecode, debug } = options;
161
+ const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;
162
+ return async function* (input) {
163
+ const ctx = {
164
+ config,
165
+ fieldPaths,
166
+ state: createEncodeState()
167
+ };
168
+ const startEncode = debug ? performance.now() : 0;
169
+ const encodedInput = deepEncode(input, ctx);
170
+ const encodeDurationMs = debug ? performance.now() - startEncode : 0;
171
+ onEncode?.({
172
+ mapping: ctx.state.mapping,
173
+ ...debug && { debugData: {
174
+ encodedCount: Object.keys(ctx.state.mapping).length,
175
+ input,
176
+ output: encodedInput,
177
+ durationMs: encodeDurationMs
178
+ } }
179
+ });
180
+ const generator = fn(encodedInput);
181
+ let totalDecoded = 0;
182
+ const startDecode = debug ? performance.now() : 0;
183
+ while (true) {
184
+ const { value, done } = await generator.next();
185
+ if (done) {
186
+ const countRef = { count: 0 };
187
+ const decodedValue = deepDecode(value, ctx.state.mapping, countRef);
188
+ totalDecoded += countRef.count;
189
+ const decodeDurationMs = debug ? performance.now() - startDecode : 0;
190
+ onDecode?.({ ...debug && { debugData: {
191
+ decodedCount: totalDecoded,
192
+ input: value,
193
+ output: decodedValue,
194
+ durationMs: decodeDurationMs
195
+ } } });
196
+ return decodedValue;
197
+ }
198
+ const countRef = { count: 0 };
199
+ const decodedValue = deepDecode(value, ctx.state.mapping, countRef);
200
+ totalDecoded += countRef.count;
201
+ yield decodedValue;
202
+ }
203
+ };
184
204
  }
205
+ /**
206
+ * Utility function to encode a plain object (useful for manual encoding).
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * const { encoded, mapping } = encodeObject(
211
+ * { user_id: 'uuid-here', data: { owner: 'other-uuid' } },
212
+ * { inputFormat: 'UUID', outputFormat: 'SafeNumeric' }
213
+ * );
214
+ * ```
215
+ */
185
216
  function encodeObject(obj, config, encodeFields) {
186
- const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;
187
- const ctx = {
188
- config,
189
- fieldPaths,
190
- state: createEncodeState()
191
- };
192
- const encoded = deepEncode(obj, ctx);
193
- return { encoded, mapping: ctx.state.mapping };
217
+ const ctx = {
218
+ config,
219
+ fieldPaths: encodeFields ? encodeFields.map(parseFieldPath) : null,
220
+ state: createEncodeState()
221
+ };
222
+ return {
223
+ encoded: deepEncode(obj, ctx),
224
+ mapping: ctx.state.mapping
225
+ };
194
226
  }
227
+ /**
228
+ * Utility function to decode a plain object (useful for manual decoding).
229
+ *
230
+ * @example
231
+ * ```typescript
232
+ * const decoded = decodeObject(
233
+ * { user_id: '«000»', summary: 'User «000» is active' },
234
+ * { '«000»': 'uuid-here' }
235
+ * );
236
+ * ```
237
+ */
195
238
  function decodeObject(obj, mapping) {
196
- const countRef = { count: 0 };
197
- return deepDecode(obj, mapping, countRef);
239
+ return deepDecode(obj, mapping, { count: 0 });
198
240
  }
199
- export {
200
- decodeObject,
201
- encodeObject,
202
- wrapBamlFunction,
203
- wrapBamlStreamingFunction
204
- };
241
+ //#endregion
242
+ export { decodeObject, encodeObject, wrapBamlFunction, wrapBamlStreamingFunction };
243
+
244
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * prompt-identifiers-baml - BAML wrapper for automatic ID encoding/decoding\n *\n * Wraps BAML-generated TypeScript functions to automatically encode IDs\n * in inputs and decode them in outputs.\n */\n\nimport { createEncodeState, decode, encode, EncodeConfig, EncodeState } from \"prompt-identifiers\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Debug data included in onEncode callback when debug is true */\nexport interface EncodeDebugData {\n /** Number of unique IDs encoded */\n encodedCount: number;\n /** Original input object before encoding */\n input: unknown;\n /** Encoded input object */\n output: unknown;\n /** Time spent encoding in milliseconds */\n durationMs: number;\n}\n\n/** Debug data included in onDecode callback when debug is true */\nexport interface DecodeDebugData {\n /** Number of fields containing decoded placeholders */\n decodedCount: number;\n /** Raw output from LLM (encoded) */\n input: unknown;\n /** Decoded output with original IDs restored */\n output: unknown;\n /** Time spent decoding in milliseconds */\n durationMs: number;\n}\n\n/** Configuration options for the BAML wrapper */\nexport interface WrapBamlFunctionOptions {\n /** Encoding configuration (inputFormat and outputFormat) */\n config: EncodeConfig;\n\n /**\n * Optional: specific field paths to encode.\n * If not provided, all string fields matching the input pattern are encoded.\n *\n * Supports dot notation and array wildcards:\n * - 'user_id' - top-level field\n * - 'data.user_id' - nested field\n * - 'items[].id' - all 'id' fields in 'items' array\n * - 'data.users[].profile.id' - deeply nested array field\n *\n * @example\n * encodeFields: ['user_id', 'items[].id', 'metadata.owner_id']\n */\n encodeFields?: string[];\n\n /**\n * Enable debug mode to populate debugData in callbacks with\n * input/output snapshots, counts, and timing information.\n */\n debug?: boolean;\n\n /**\n * Optional callback fired after encoding IDs in the input.\n * Receives the placeholder→ID mapping. When debug is true,\n * also receives debugData with input, output, counts, and timing.\n */\n onEncode?: (result: { mapping: Record<string, string>; debugData?: EncodeDebugData }) => void;\n\n /**\n * Optional callback fired after decoding IDs in the output.\n * When debug is true, receives debugData with input, output, counts, and timing.\n */\n onDecode?: (result: { debugData?: DecodeDebugData }) => void;\n}\n\n/** A BAML function type (sync or async) */\nexport type BamlFunction<TInput, TOutput> = (input: TInput) => Promise<TOutput>;\n\n/** A BAML streaming function type */\nexport type BamlStreamingFunction<TInput, TPartial, TFinal> = (\n input: TInput\n) => AsyncGenerator<TPartial, TFinal, unknown>;\n\n// =============================================================================\n// Field Path Matching\n// =============================================================================\n\n/**\n * Parse a field path into segments.\n * 'items[].id' -> ['items', '[]', 'id']\n */\nfunction parseFieldPath(path: string): string[] {\n const segments: string[] = [];\n let current = \"\";\n\n for (let i = 0; i < path.length; i++) {\n const char = path[i];\n\n if (char === \".\") {\n if (current) {\n segments.push(current);\n current = \"\";\n }\n } else if (char === \"[\" && path[i + 1] === \"]\") {\n if (current) {\n segments.push(current);\n current = \"\";\n }\n segments.push(\"[]\");\n i++; // skip ']'\n } else {\n current += char;\n }\n }\n\n if (current) {\n segments.push(current);\n }\n\n return segments;\n}\n\n/**\n * Check if a value at the given path should be encoded.\n */\nfunction matchesFieldPath(currentPath: string[], targetSegments: string[]): boolean {\n if (currentPath.length !== targetSegments.length) {\n return false;\n }\n\n for (let i = 0; i < targetSegments.length; i++) {\n const target = targetSegments[i];\n const current = currentPath[i];\n\n // '[]' matches any array index\n if (target === \"[]\") {\n if (!/^\\d+$/.test(current)) {\n return false;\n }\n } else if (target !== current) {\n return false;\n }\n }\n\n return true;\n}\n\n// =============================================================================\n// Deep Object Traversal\n// =============================================================================\n\n/**\n * Context for encoding operations - tracks state across recursive calls.\n * Delegates to core's encode() with shared EncodeState for consistent placeholder assignment.\n */\ninterface EncodeContext {\n config: EncodeConfig;\n fieldPaths: string[][] | null; // null means auto-detect mode\n state: EncodeState;\n}\n\n/**\n * Deep traverse and encode IDs in an object.\n * Returns a new object with IDs replaced by placeholders.\n */\nfunction deepEncode<T>(value: T, ctx: EncodeContext, path: string[] = []): T {\n // Handle null/undefined\n if (value === null || value === undefined) {\n return value;\n }\n\n // Handle strings - the primary encoding target\n if (typeof value === \"string\") {\n // Check if this field should be encoded\n const shouldEncode =\n ctx.fieldPaths === null || ctx.fieldPaths.some((fp) => matchesFieldPath(path, fp));\n\n if (!shouldEncode) {\n return value;\n }\n\n // Encode the string using core's encode() with shared state\n return encode(value, ctx.config, ctx.state).encoded as T;\n }\n\n // Handle arrays\n if (Array.isArray(value)) {\n return value.map((item, index) => deepEncode(item, ctx, [...path, String(index)])) as T;\n }\n\n // Handle objects\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n\n for (const [key, val] of Object.entries(value)) {\n result[key] = deepEncode(val, ctx, [...path, key]);\n }\n\n return result as T;\n }\n\n // Primitives (numbers, booleans) - return as-is\n return value;\n}\n\n/**\n * Deep traverse and decode placeholders in an object.\n * Returns a new object with placeholders replaced by original IDs.\n */\nfunction deepDecode<T>(value: T, mapping: Record<string, string>, countRef: { count: number }): T {\n // Handle null/undefined\n if (value === null || value === undefined) {\n return value;\n }\n\n // Handle strings\n if (typeof value === \"string\") {\n const decoded = decode(value, mapping);\n // Count replacements\n if (decoded !== value) {\n countRef.count++;\n }\n return decoded as T;\n }\n\n // Handle arrays\n if (Array.isArray(value)) {\n return value.map((item) => deepDecode(item, mapping, countRef)) as T;\n }\n\n // Handle objects\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n\n for (const [key, val] of Object.entries(value)) {\n result[key] = deepDecode(val, mapping, countRef);\n }\n\n return result as T;\n }\n\n // Primitives - return as-is\n return value;\n}\n\n// =============================================================================\n// Public API\n// =============================================================================\n\n/**\n * Wrap a BAML function to automatically encode IDs in inputs and decode them in outputs.\n *\n * @example\n * ```typescript\n * import { wrapBamlFunction } from 'prompt-identifiers-baml';\n * import { b } from './baml_client';\n *\n * const analyzeUser = wrapBamlFunction(b.AnalyzeUser, {\n * config: { inputFormat: 'UUID', outputFormat: 'SafeNumeric' },\n * encodeFields: ['user_id', 'items[].id'], // Optional: specific fields\n * });\n *\n * // Use normally - IDs are auto-encoded/decoded\n * const result = await analyzeUser({\n * user_id: '123e4567-e89b-42d3-a456-426655440000',\n * items: [{ id: '987fcdeb-51a2-43f7-8d9c-0123456789ab', name: 'test' }]\n * });\n * ```\n */\nexport function wrapBamlFunction<TInput, TOutput>(\n fn: BamlFunction<TInput, TOutput>,\n options: WrapBamlFunctionOptions\n): BamlFunction<TInput, TOutput> {\n const { config, encodeFields, onEncode, onDecode, debug } = options;\n\n // Pre-parse field paths if provided\n const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;\n\n return async (input: TInput): Promise<TOutput> => {\n // Encode input\n const ctx: EncodeContext = {\n config,\n fieldPaths,\n state: createEncodeState(),\n };\n\n const startEncode = debug ? performance.now() : 0;\n const encodedInput = deepEncode(input, ctx);\n const encodeDurationMs = debug ? performance.now() - startEncode : 0;\n\n onEncode?.({\n mapping: ctx.state.mapping,\n ...(debug && {\n debugData: {\n encodedCount: Object.keys(ctx.state.mapping).length,\n input,\n output: encodedInput,\n durationMs: encodeDurationMs,\n },\n }),\n });\n\n // Call the original function\n const output = await fn(encodedInput);\n\n // Decode output\n const countRef = { count: 0 };\n const startDecode = debug ? performance.now() : 0;\n const decodedOutput = deepDecode(output, ctx.state.mapping, countRef);\n const decodeDurationMs = debug ? performance.now() - startDecode : 0;\n\n onDecode?.({\n ...(debug && {\n debugData: {\n decodedCount: countRef.count,\n input: output,\n output: decodedOutput,\n durationMs: decodeDurationMs,\n },\n }),\n });\n\n return decodedOutput;\n };\n}\n\n/**\n * Wrap a BAML streaming function to automatically encode IDs in inputs\n * and decode them in outputs (both partial and final).\n *\n * @example\n * ```typescript\n * import { wrapBamlStreamingFunction } from 'prompt-identifiers-baml';\n * import { b } from './baml_client';\n *\n * const streamAnalysis = wrapBamlStreamingFunction(b.stream.AnalyzeUser, {\n * config: { inputFormat: 'UUID', outputFormat: 'SafeNumeric' },\n * });\n *\n * for await (const partial of streamAnalysis({ user_id: 'uuid-here' })) {\n * console.log(partial); // IDs decoded in real-time\n * }\n * ```\n */\nexport function wrapBamlStreamingFunction<TInput, TPartial, TFinal>(\n fn: BamlStreamingFunction<TInput, TPartial, TFinal>,\n options: WrapBamlFunctionOptions\n): BamlStreamingFunction<TInput, TPartial, TFinal> {\n const { config, encodeFields, onEncode, onDecode, debug } = options;\n\n // Pre-parse field paths if provided\n const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;\n\n return async function* (input: TInput): AsyncGenerator<TPartial, TFinal, unknown> {\n // Encode input\n const ctx: EncodeContext = {\n config,\n fieldPaths,\n state: createEncodeState(),\n };\n\n const startEncode = debug ? performance.now() : 0;\n const encodedInput = deepEncode(input, ctx);\n const encodeDurationMs = debug ? performance.now() - startEncode : 0;\n\n onEncode?.({\n mapping: ctx.state.mapping,\n ...(debug && {\n debugData: {\n encodedCount: Object.keys(ctx.state.mapping).length,\n input,\n output: encodedInput,\n durationMs: encodeDurationMs,\n },\n }),\n });\n\n // Call the original streaming function\n const generator = fn(encodedInput);\n let totalDecoded = 0;\n const startDecode = debug ? performance.now() : 0;\n\n while (true) {\n const { value, done } = await generator.next();\n\n if (done) {\n // Final value\n const countRef = { count: 0 };\n const decodedValue = deepDecode(value, ctx.state.mapping, countRef);\n totalDecoded += countRef.count;\n const decodeDurationMs = debug ? performance.now() - startDecode : 0;\n\n onDecode?.({\n ...(debug && {\n debugData: {\n decodedCount: totalDecoded,\n input: value,\n output: decodedValue,\n durationMs: decodeDurationMs,\n },\n }),\n });\n return decodedValue;\n }\n\n // Partial value\n const countRef = { count: 0 };\n const decodedValue = deepDecode(value, ctx.state.mapping, countRef);\n totalDecoded += countRef.count;\n yield decodedValue;\n }\n };\n}\n\n/**\n * Utility function to encode a plain object (useful for manual encoding).\n *\n * @example\n * ```typescript\n * const { encoded, mapping } = encodeObject(\n * { user_id: 'uuid-here', data: { owner: 'other-uuid' } },\n * { inputFormat: 'UUID', outputFormat: 'SafeNumeric' }\n * );\n * ```\n */\nexport function encodeObject<T>(\n obj: T,\n config: EncodeConfig,\n encodeFields?: string[]\n): { encoded: T; mapping: Record<string, string> } {\n const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;\n\n const ctx: EncodeContext = {\n config,\n fieldPaths,\n state: createEncodeState(),\n };\n\n const encoded = deepEncode(obj, ctx);\n\n return { encoded, mapping: ctx.state.mapping };\n}\n\n/**\n * Utility function to decode a plain object (useful for manual decoding).\n *\n * @example\n * ```typescript\n * const decoded = decodeObject(\n * { user_id: '«000»', summary: 'User «000» is active' },\n * { '«000»': 'uuid-here' }\n * );\n * ```\n */\nexport function decodeObject<T>(obj: T, mapping: Record<string, string>): T {\n const countRef = { count: 0 };\n return deepDecode(obj, mapping, countRef);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "prompt-identifiers-baml",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "BAML integration for prompt-identifiers. Efficient ID compression for token optimization.",
5
- "main": "dist/index.js",
5
+ "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
7
- "types": "dist/index.d.ts",
7
+ "types": "dist/index.d.cts",
8
8
  "exports": {
9
9
  ".": {
10
10
  "import": {
@@ -12,14 +12,15 @@
12
12
  "default": "./dist/index.mjs"
13
13
  },
14
14
  "require": {
15
- "types": "./dist/index.d.ts",
16
- "default": "./dist/index.js"
15
+ "types": "./dist/index.d.cts",
16
+ "default": "./dist/index.cjs"
17
17
  }
18
18
  }
19
19
  },
20
20
  "sideEffects": false,
21
21
  "files": [
22
- "dist"
22
+ "dist",
23
+ "src"
23
24
  ],
24
25
  "keywords": [
25
26
  "llm",
@@ -39,8 +40,8 @@
39
40
  "url": "https://github.com/fogx/prompt-identifiers"
40
41
  },
41
42
  "peerDependencies": {
42
- "prompt-identifiers": ">=0.1.2",
43
- "@boundaryml/baml": ">=0.70.0"
43
+ "@boundaryml/baml": ">=0.70.0",
44
+ "prompt-identifiers": ">=0.1.3"
44
45
  },
45
46
  "peerDependenciesMeta": {
46
47
  "@boundaryml/baml": {
@@ -48,16 +49,15 @@
48
49
  }
49
50
  },
50
51
  "devDependencies": {
51
- "@types/jest": "^29.0.0",
52
- "@types/node": "^20.0.0",
53
- "jest": "^29.0.0",
54
- "ts-jest": "^29.0.0",
55
- "tsup": "^8.5.1",
56
- "typescript": "^5.0.0",
57
- "prompt-identifiers": "0.1.2"
52
+ "@types/node": "^22.20.2",
53
+ "prompt-identifiers": "0.1.4",
54
+ "tsdown": "^0.23.0",
55
+ "typescript": "^7.0.2",
56
+ "vitest": "^5.0.0"
58
57
  },
59
58
  "scripts": {
60
- "build": "tsup src/index.ts --format cjs,esm --dts --clean",
61
- "test": "jest"
59
+ "build": "tsdown src/index.ts --format esm --format cjs --dts --sourcemap",
60
+ "test": "vitest run",
61
+ "typecheck": "tsc --noEmit -p tsconfig.typecheck.json"
62
62
  }
63
63
  }