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/README.md +14 -13
- package/dist/index.cjs +248 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +137 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +68 -75
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +227 -187
- package/dist/index.mjs.map +1 -0
- package/package.json +17 -17
- package/src/index.ts +460 -0
- package/dist/index.d.ts +0 -144
- package/dist/index.js +0 -232
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
197
|
-
return deepDecode(obj, mapping, countRef);
|
|
239
|
+
return deepDecode(obj, mapping, { count: 0 });
|
|
198
240
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "BAML integration for prompt-identifiers. Efficient ID compression for token optimization.",
|
|
5
|
-
"main": "dist/index.
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
|
-
"types": "dist/index.d.
|
|
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.
|
|
16
|
-
"default": "./dist/index.
|
|
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
|
-
"
|
|
43
|
-
"
|
|
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/
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
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": "
|
|
61
|
-
"test": "
|
|
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
|
}
|