progressive-zod 1.1.2 → 1.2.0
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/cli/index.js +24 -15
- package/dist/client.cjs +645 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +125 -0
- package/dist/client.d.ts +125 -0
- package/dist/client.js +609 -0
- package/dist/client.js.map +1 -0
- package/dist/index.cjs +25 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -125
- package/dist/index.d.ts +2 -125
- package/dist/index.js +25 -16
- package/dist/index.js.map +1 -1
- package/package.json +12 -2
package/dist/cli/index.js
CHANGED
|
@@ -88,7 +88,8 @@ var init_redis = __esm({
|
|
|
88
88
|
if (!this.redis) {
|
|
89
89
|
let Redis;
|
|
90
90
|
try {
|
|
91
|
-
|
|
91
|
+
const moduleName = "ioredis";
|
|
92
|
+
Redis = (await import(moduleName)).default;
|
|
92
93
|
} catch {
|
|
93
94
|
throw new Error(
|
|
94
95
|
"ioredis is required for Redis storage. Install it with: npm install ioredis"
|
|
@@ -286,9 +287,13 @@ var MemoryStorage = class {
|
|
|
286
287
|
}
|
|
287
288
|
};
|
|
288
289
|
|
|
289
|
-
// src/storage/
|
|
290
|
+
// src/storage/resolve.ts
|
|
290
291
|
var currentConfig = {};
|
|
291
292
|
var currentStorage = null;
|
|
293
|
+
var storageFactory = async (config) => new MemoryStorage(config);
|
|
294
|
+
function _setStorageFactory(factory) {
|
|
295
|
+
storageFactory = factory;
|
|
296
|
+
}
|
|
292
297
|
function getConfig() {
|
|
293
298
|
return {
|
|
294
299
|
storage: currentConfig.storage ?? process.env.PROGRESSIVE_ZOD_STORAGE ?? "memory",
|
|
@@ -302,28 +307,32 @@ function getConfig() {
|
|
|
302
307
|
async function getStorage() {
|
|
303
308
|
if (currentStorage) return currentStorage;
|
|
304
309
|
const config = getConfig();
|
|
310
|
+
currentStorage = await storageFactory(config, currentConfig);
|
|
311
|
+
return currentStorage;
|
|
312
|
+
}
|
|
313
|
+
async function disconnectStorage() {
|
|
314
|
+
if (currentStorage) {
|
|
315
|
+
await currentStorage.disconnect();
|
|
316
|
+
currentStorage = null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/storage/index.ts
|
|
321
|
+
_setStorageFactory(async (config, userConfig) => {
|
|
305
322
|
if (config.storage === "amplitude") {
|
|
306
|
-
if (!
|
|
323
|
+
if (!userConfig.amplitudeClient) {
|
|
307
324
|
throw new Error(
|
|
308
325
|
"progressive-zod: storage is set to 'amplitude' but no amplitudeClient was provided. Pass an Amplitude client instance via configure({ amplitudeClient })."
|
|
309
326
|
);
|
|
310
327
|
}
|
|
311
328
|
const { AmplitudeStorage: AmplitudeStorage2 } = await Promise.resolve().then(() => (init_amplitude(), amplitude_exports));
|
|
312
|
-
|
|
329
|
+
return new AmplitudeStorage2(userConfig.amplitudeClient, config);
|
|
313
330
|
} else if (config.storage === "redis") {
|
|
314
331
|
const { RedisStorage: RedisStorage2 } = await Promise.resolve().then(() => (init_redis(), redis_exports));
|
|
315
|
-
|
|
316
|
-
} else {
|
|
317
|
-
currentStorage = new MemoryStorage(config);
|
|
332
|
+
return new RedisStorage2(config);
|
|
318
333
|
}
|
|
319
|
-
return
|
|
320
|
-
}
|
|
321
|
-
async function disconnectStorage() {
|
|
322
|
-
if (currentStorage) {
|
|
323
|
-
await currentStorage.disconnect();
|
|
324
|
-
currentStorage = null;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
334
|
+
return new MemoryStorage(config);
|
|
335
|
+
});
|
|
327
336
|
|
|
328
337
|
// src/cli/commands/list.ts
|
|
329
338
|
async function listCommand() {
|
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
|
|
33
|
+
// src/storage/amplitude.ts
|
|
34
|
+
var amplitude_exports = {};
|
|
35
|
+
__export(amplitude_exports, {
|
|
36
|
+
AmplitudeStorage: () => AmplitudeStorage
|
|
37
|
+
});
|
|
38
|
+
var AmplitudeStorage;
|
|
39
|
+
var init_amplitude = __esm({
|
|
40
|
+
"src/storage/amplitude.ts"() {
|
|
41
|
+
"use strict";
|
|
42
|
+
AmplitudeStorage = class {
|
|
43
|
+
client;
|
|
44
|
+
deviceId;
|
|
45
|
+
constructor(client, config) {
|
|
46
|
+
this.client = client;
|
|
47
|
+
this.deviceId = `pzod:${config.keyPrefix ?? "default"}`;
|
|
48
|
+
}
|
|
49
|
+
addName(_name) {
|
|
50
|
+
}
|
|
51
|
+
addSample(_name, _sample) {
|
|
52
|
+
}
|
|
53
|
+
addViolation(_name, _violation) {
|
|
54
|
+
}
|
|
55
|
+
incrConform(name) {
|
|
56
|
+
this.client.track(
|
|
57
|
+
"pzod:type_checked",
|
|
58
|
+
{ type_name: name, result: "conform" },
|
|
59
|
+
{ device_id: this.deviceId }
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
incrViolate(name) {
|
|
63
|
+
this.client.track(
|
|
64
|
+
"pzod:type_checked",
|
|
65
|
+
{ type_name: name, result: "violate" },
|
|
66
|
+
{ device_id: this.deviceId }
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
// --- Read methods: no-ops (use Amplitude dashboard) ---
|
|
70
|
+
async getNames() {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
async getSamples(_name) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
async getViolations(_name, _limit) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
async getStats(_name) {
|
|
80
|
+
return { conform: 0, violate: 0 };
|
|
81
|
+
}
|
|
82
|
+
disconnect() {
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// src/client.ts
|
|
89
|
+
var client_exports = {};
|
|
90
|
+
__export(client_exports, {
|
|
91
|
+
configure: () => configure,
|
|
92
|
+
configureBatch: () => configureBatch,
|
|
93
|
+
forceFlush: () => forceFlush,
|
|
94
|
+
inferSchema: () => inferSchema,
|
|
95
|
+
progressive: () => progressive,
|
|
96
|
+
schemaToCode: () => schemaToCode,
|
|
97
|
+
shutdown: () => shutdown
|
|
98
|
+
});
|
|
99
|
+
module.exports = __toCommonJS(client_exports);
|
|
100
|
+
|
|
101
|
+
// src/storage/memory.ts
|
|
102
|
+
var fs = __toESM(require("fs"), 1);
|
|
103
|
+
var path = __toESM(require("path"), 1);
|
|
104
|
+
var MemoryStorage = class {
|
|
105
|
+
names = /* @__PURE__ */ new Set();
|
|
106
|
+
data = /* @__PURE__ */ new Map();
|
|
107
|
+
maxSamples;
|
|
108
|
+
maxViolations;
|
|
109
|
+
dataDir;
|
|
110
|
+
flushTimer;
|
|
111
|
+
constructor(config = {}) {
|
|
112
|
+
this.maxSamples = config.maxSamples ?? 1e3;
|
|
113
|
+
this.maxViolations = config.maxViolations ?? 1e3;
|
|
114
|
+
this.dataDir = config.dataDir;
|
|
115
|
+
if (this.dataDir) {
|
|
116
|
+
this.loadFromDisk();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
getOrCreate(name) {
|
|
120
|
+
let entry = this.data.get(name);
|
|
121
|
+
if (!entry) {
|
|
122
|
+
entry = { samples: [], violations: [], conform: 0, violate: 0 };
|
|
123
|
+
this.data.set(name, entry);
|
|
124
|
+
}
|
|
125
|
+
return entry;
|
|
126
|
+
}
|
|
127
|
+
addName(name) {
|
|
128
|
+
this.names.add(name);
|
|
129
|
+
this.schedulePersist();
|
|
130
|
+
}
|
|
131
|
+
addSample(name, sample) {
|
|
132
|
+
const entry = this.getOrCreate(name);
|
|
133
|
+
entry.samples.unshift(sample);
|
|
134
|
+
if (entry.samples.length > this.maxSamples) {
|
|
135
|
+
entry.samples.length = this.maxSamples;
|
|
136
|
+
}
|
|
137
|
+
this.schedulePersist();
|
|
138
|
+
}
|
|
139
|
+
addViolation(name, violation) {
|
|
140
|
+
const entry = this.getOrCreate(name);
|
|
141
|
+
entry.violations.unshift(violation);
|
|
142
|
+
if (entry.violations.length > this.maxViolations) {
|
|
143
|
+
entry.violations.length = this.maxViolations;
|
|
144
|
+
}
|
|
145
|
+
this.schedulePersist();
|
|
146
|
+
}
|
|
147
|
+
incrConform(name) {
|
|
148
|
+
this.getOrCreate(name).conform++;
|
|
149
|
+
this.schedulePersist();
|
|
150
|
+
}
|
|
151
|
+
incrViolate(name) {
|
|
152
|
+
this.getOrCreate(name).violate++;
|
|
153
|
+
this.schedulePersist();
|
|
154
|
+
}
|
|
155
|
+
getNames() {
|
|
156
|
+
return [...this.names].sort();
|
|
157
|
+
}
|
|
158
|
+
getSamples(name) {
|
|
159
|
+
return this.getOrCreate(name).samples;
|
|
160
|
+
}
|
|
161
|
+
getViolations(name, limit) {
|
|
162
|
+
return this.getOrCreate(name).violations.slice(0, limit);
|
|
163
|
+
}
|
|
164
|
+
async getStats(name) {
|
|
165
|
+
const entry = this.getOrCreate(name);
|
|
166
|
+
return { conform: entry.conform, violate: entry.violate };
|
|
167
|
+
}
|
|
168
|
+
disconnect() {
|
|
169
|
+
if (this.flushTimer) {
|
|
170
|
+
clearTimeout(this.flushTimer);
|
|
171
|
+
this.flushTimer = void 0;
|
|
172
|
+
}
|
|
173
|
+
if (this.dataDir) {
|
|
174
|
+
this.persistToDisk();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
schedulePersist() {
|
|
178
|
+
if (!this.dataDir || this.flushTimer) return;
|
|
179
|
+
this.flushTimer = setTimeout(() => {
|
|
180
|
+
this.flushTimer = void 0;
|
|
181
|
+
this.persistToDisk();
|
|
182
|
+
}, 500);
|
|
183
|
+
}
|
|
184
|
+
persistToDisk() {
|
|
185
|
+
if (!this.dataDir) return;
|
|
186
|
+
try {
|
|
187
|
+
fs.mkdirSync(this.dataDir, { recursive: true });
|
|
188
|
+
const snapshot = {
|
|
189
|
+
names: [...this.names],
|
|
190
|
+
types: Object.fromEntries(this.data)
|
|
191
|
+
};
|
|
192
|
+
fs.writeFileSync(
|
|
193
|
+
path.join(this.dataDir, "data.json"),
|
|
194
|
+
JSON.stringify(snapshot, null, 2)
|
|
195
|
+
);
|
|
196
|
+
} catch {
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
loadFromDisk() {
|
|
200
|
+
if (!this.dataDir) return;
|
|
201
|
+
try {
|
|
202
|
+
const raw = fs.readFileSync(
|
|
203
|
+
path.join(this.dataDir, "data.json"),
|
|
204
|
+
"utf-8"
|
|
205
|
+
);
|
|
206
|
+
const snapshot = JSON.parse(raw);
|
|
207
|
+
if (Array.isArray(snapshot.names)) {
|
|
208
|
+
for (const n of snapshot.names) this.names.add(n);
|
|
209
|
+
}
|
|
210
|
+
if (snapshot.types && typeof snapshot.types === "object") {
|
|
211
|
+
for (const [key, val] of Object.entries(snapshot.types)) {
|
|
212
|
+
this.data.set(key, val);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} catch {
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// src/storage/resolve.ts
|
|
221
|
+
var currentConfig = {};
|
|
222
|
+
var currentStorage = null;
|
|
223
|
+
var storageFactory = async (config) => new MemoryStorage(config);
|
|
224
|
+
function _setStorageFactory(factory) {
|
|
225
|
+
storageFactory = factory;
|
|
226
|
+
}
|
|
227
|
+
function configure(config) {
|
|
228
|
+
currentConfig = { ...currentConfig, ...config };
|
|
229
|
+
if (currentStorage) {
|
|
230
|
+
currentStorage.disconnect();
|
|
231
|
+
currentStorage = null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function getConfig() {
|
|
235
|
+
return {
|
|
236
|
+
storage: currentConfig.storage ?? process.env.PROGRESSIVE_ZOD_STORAGE ?? "memory",
|
|
237
|
+
redisUrl: currentConfig.redisUrl ?? process.env.PROGRESSIVE_ZOD_REDIS_URL,
|
|
238
|
+
keyPrefix: currentConfig.keyPrefix ?? process.env.PROGRESSIVE_ZOD_KEY_PREFIX ?? "pzod:",
|
|
239
|
+
maxViolations: currentConfig.maxViolations ?? parseInt(process.env.PROGRESSIVE_ZOD_MAX_VIOLATIONS ?? "1000", 10),
|
|
240
|
+
maxSamples: currentConfig.maxSamples ?? parseInt(process.env.PROGRESSIVE_ZOD_MAX_SAMPLES ?? "1000", 10),
|
|
241
|
+
dataDir: currentConfig.dataDir ?? process.env.PROGRESSIVE_ZOD_DATA_DIR ?? ".progressive-zod"
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
async function getStorage() {
|
|
245
|
+
if (currentStorage) return currentStorage;
|
|
246
|
+
const config = getConfig();
|
|
247
|
+
currentStorage = await storageFactory(config, currentConfig);
|
|
248
|
+
return currentStorage;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/batch-processor.ts
|
|
252
|
+
var DEFAULT_MAX_QUEUE_SIZE = 2048;
|
|
253
|
+
var DEFAULT_FLUSH_INTERVAL_MS = 5e3;
|
|
254
|
+
var DEFAULT_MAX_EXPORT_BATCH_SIZE = 512;
|
|
255
|
+
var BatchProcessor = class {
|
|
256
|
+
queue = [];
|
|
257
|
+
timer = null;
|
|
258
|
+
flushing = false;
|
|
259
|
+
droppedCount = 0;
|
|
260
|
+
maxQueueSize;
|
|
261
|
+
flushIntervalMs;
|
|
262
|
+
maxExportBatchSize;
|
|
263
|
+
constructor(config = {}) {
|
|
264
|
+
this.maxQueueSize = config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE;
|
|
265
|
+
this.flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
|
|
266
|
+
this.maxExportBatchSize = config.maxExportBatchSize ?? DEFAULT_MAX_EXPORT_BATCH_SIZE;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Enqueue an observation. This is synchronous and fast — no I/O happens here.
|
|
270
|
+
* If the queue is full, the oldest observation is dropped (bounded memory).
|
|
271
|
+
*/
|
|
272
|
+
enqueue(name, input, schema) {
|
|
273
|
+
this.ensureTimer();
|
|
274
|
+
let serialized;
|
|
275
|
+
try {
|
|
276
|
+
serialized = JSON.stringify(input);
|
|
277
|
+
} catch {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (this.queue.length >= this.maxQueueSize) {
|
|
281
|
+
this.queue.shift();
|
|
282
|
+
this.droppedCount++;
|
|
283
|
+
}
|
|
284
|
+
this.queue.push({ name, serialized, schema });
|
|
285
|
+
if (this.queue.length >= this.maxExportBatchSize) {
|
|
286
|
+
this.flushAsync();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Force flush all pending observations. Call during graceful shutdown.
|
|
291
|
+
*/
|
|
292
|
+
async forceFlush() {
|
|
293
|
+
this.clearTimer();
|
|
294
|
+
await this.flush();
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Shut down the processor: flush remaining data and stop the timer.
|
|
298
|
+
*/
|
|
299
|
+
async shutdown() {
|
|
300
|
+
await this.forceFlush();
|
|
301
|
+
}
|
|
302
|
+
/** Number of observations currently in the queue */
|
|
303
|
+
get pendingCount() {
|
|
304
|
+
return this.queue.length;
|
|
305
|
+
}
|
|
306
|
+
/** Number of observations dropped due to queue overflow */
|
|
307
|
+
get dropped() {
|
|
308
|
+
return this.droppedCount;
|
|
309
|
+
}
|
|
310
|
+
ensureTimer() {
|
|
311
|
+
if (this.timer) return;
|
|
312
|
+
this.timer = setInterval(() => {
|
|
313
|
+
this.flushAsync();
|
|
314
|
+
}, this.flushIntervalMs);
|
|
315
|
+
if (this.timer && typeof this.timer === "object" && "unref" in this.timer) {
|
|
316
|
+
this.timer.unref();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
clearTimer() {
|
|
320
|
+
if (this.timer) {
|
|
321
|
+
clearInterval(this.timer);
|
|
322
|
+
this.timer = null;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/** Non-blocking flush — errors are swallowed */
|
|
326
|
+
flushAsync() {
|
|
327
|
+
this.flush().catch(() => {
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
/** Drain the queue and send observations to storage in batch */
|
|
331
|
+
async flush() {
|
|
332
|
+
if (this.flushing || this.queue.length === 0) return;
|
|
333
|
+
this.flushing = true;
|
|
334
|
+
const batch = this.queue.splice(0, this.maxExportBatchSize);
|
|
335
|
+
try {
|
|
336
|
+
const storage = await getStorage();
|
|
337
|
+
await this.exportBatch(storage, batch);
|
|
338
|
+
} catch {
|
|
339
|
+
} finally {
|
|
340
|
+
this.flushing = false;
|
|
341
|
+
if (this.queue.length >= this.maxExportBatchSize) {
|
|
342
|
+
this.flushAsync();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
async exportBatch(storage, batch) {
|
|
347
|
+
const names = /* @__PURE__ */ new Set();
|
|
348
|
+
for (const obs of batch) {
|
|
349
|
+
names.add(obs.name);
|
|
350
|
+
}
|
|
351
|
+
await Promise.all([...names].map((n) => storage.addName(n)));
|
|
352
|
+
const promises = [];
|
|
353
|
+
for (const obs of batch) {
|
|
354
|
+
promises.push(this.exportOne(storage, obs));
|
|
355
|
+
}
|
|
356
|
+
await Promise.all(promises);
|
|
357
|
+
}
|
|
358
|
+
async exportOne(storage, obs) {
|
|
359
|
+
await storage.addSample(obs.name, obs.serialized);
|
|
360
|
+
if (obs.schema) {
|
|
361
|
+
const result = obs.schema.safeParse(JSON.parse(obs.serialized));
|
|
362
|
+
if (result.success) {
|
|
363
|
+
await storage.incrConform(obs.name);
|
|
364
|
+
} else {
|
|
365
|
+
await storage.incrViolate(obs.name);
|
|
366
|
+
await storage.addViolation(obs.name, obs.serialized);
|
|
367
|
+
}
|
|
368
|
+
} else {
|
|
369
|
+
await storage.incrViolate(obs.name);
|
|
370
|
+
await storage.addViolation(obs.name, obs.serialized);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// src/progressive.ts
|
|
376
|
+
var processor = null;
|
|
377
|
+
function getProcessor() {
|
|
378
|
+
if (!processor) {
|
|
379
|
+
processor = new BatchProcessor();
|
|
380
|
+
}
|
|
381
|
+
return processor;
|
|
382
|
+
}
|
|
383
|
+
function configureBatch(config) {
|
|
384
|
+
if (processor) {
|
|
385
|
+
processor.forceFlush().catch(() => {
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
processor = new BatchProcessor(config);
|
|
389
|
+
}
|
|
390
|
+
async function forceFlush() {
|
|
391
|
+
if (processor) {
|
|
392
|
+
await processor.forceFlush();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
async function shutdown() {
|
|
396
|
+
if (processor) {
|
|
397
|
+
await processor.shutdown();
|
|
398
|
+
processor = null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function progressive(name, schema) {
|
|
402
|
+
return {
|
|
403
|
+
parse(input) {
|
|
404
|
+
getProcessor().enqueue(name, input, schema);
|
|
405
|
+
return input;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// src/storage/client.ts
|
|
411
|
+
_setStorageFactory(async (config, userConfig) => {
|
|
412
|
+
if (config.storage === "amplitude") {
|
|
413
|
+
if (!userConfig.amplitudeClient) {
|
|
414
|
+
throw new Error(
|
|
415
|
+
"progressive-zod: storage is set to 'amplitude' but no amplitudeClient was provided. Pass an Amplitude client instance via configure({ amplitudeClient })."
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
const { AmplitudeStorage: AmplitudeStorage2 } = await Promise.resolve().then(() => (init_amplitude(), amplitude_exports));
|
|
419
|
+
return new AmplitudeStorage2(userConfig.amplitudeClient, config);
|
|
420
|
+
} else if (config.storage === "redis") {
|
|
421
|
+
throw new Error(
|
|
422
|
+
'progressive-zod: Redis storage is not available in the client bundle. Import from "progressive-zod" instead of "progressive-zod/client".'
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return new MemoryStorage(config);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// src/infer-schema.ts
|
|
429
|
+
var import_zod = require("zod");
|
|
430
|
+
function inferSchema(samples) {
|
|
431
|
+
const objects = samples.filter(
|
|
432
|
+
(s) => typeof s === "object" && s !== null && !Array.isArray(s)
|
|
433
|
+
);
|
|
434
|
+
if (objects.length === 0) {
|
|
435
|
+
return inferPrimitiveUnion(samples);
|
|
436
|
+
}
|
|
437
|
+
const groups = groupByShape(objects);
|
|
438
|
+
if (groups.length === 1) {
|
|
439
|
+
return buildObjectSchema(groups[0]);
|
|
440
|
+
}
|
|
441
|
+
const discriminator = findDiscriminator(groups);
|
|
442
|
+
if (discriminator) {
|
|
443
|
+
return import_zod.z.discriminatedUnion(
|
|
444
|
+
discriminator,
|
|
445
|
+
groups.map(
|
|
446
|
+
(group) => buildObjectSchema(group)
|
|
447
|
+
)
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
return import_zod.z.union(
|
|
451
|
+
groups.map((group) => buildObjectSchema(group))
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
function shapeKey(obj) {
|
|
455
|
+
return Object.keys(obj).sort().join(",");
|
|
456
|
+
}
|
|
457
|
+
function groupByShape(objects) {
|
|
458
|
+
const map = /* @__PURE__ */ new Map();
|
|
459
|
+
for (const obj of objects) {
|
|
460
|
+
const key = shapeKey(obj);
|
|
461
|
+
let group = map.get(key);
|
|
462
|
+
if (!group) {
|
|
463
|
+
group = [];
|
|
464
|
+
map.set(key, group);
|
|
465
|
+
}
|
|
466
|
+
group.push(obj);
|
|
467
|
+
}
|
|
468
|
+
return [...map.values()];
|
|
469
|
+
}
|
|
470
|
+
function findDiscriminator(groups) {
|
|
471
|
+
if (groups.length < 2) return null;
|
|
472
|
+
const keySets = groups.map(
|
|
473
|
+
(g) => new Set(Object.keys(g[0]))
|
|
474
|
+
);
|
|
475
|
+
const commonKeys = [...keySets[0]].filter(
|
|
476
|
+
(k) => keySets.every((s) => s.has(k))
|
|
477
|
+
);
|
|
478
|
+
for (const key of commonKeys) {
|
|
479
|
+
const values = /* @__PURE__ */ new Set();
|
|
480
|
+
let valid = true;
|
|
481
|
+
for (const group of groups) {
|
|
482
|
+
const groupValues = new Set(
|
|
483
|
+
group.map((obj) => obj[key]).filter((v) => typeof v === "string")
|
|
484
|
+
);
|
|
485
|
+
if (groupValues.size !== 1) {
|
|
486
|
+
valid = false;
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
const val = [...groupValues][0];
|
|
490
|
+
if (values.has(val)) {
|
|
491
|
+
valid = false;
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
values.add(val);
|
|
495
|
+
}
|
|
496
|
+
if (valid && values.size === groups.length) {
|
|
497
|
+
return key;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
function buildObjectSchema(samples) {
|
|
503
|
+
const keys = Object.keys(samples[0]);
|
|
504
|
+
const shape = {};
|
|
505
|
+
for (const key of keys) {
|
|
506
|
+
const values = samples.map((s) => s[key]);
|
|
507
|
+
const unique = new Set(values);
|
|
508
|
+
if (unique.size === 1 && typeof values[0] === "string") {
|
|
509
|
+
shape[key] = import_zod.z.literal(values[0]);
|
|
510
|
+
} else {
|
|
511
|
+
shape[key] = inferFieldType(values);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return import_zod.z.object(shape).strict();
|
|
515
|
+
}
|
|
516
|
+
function inferFieldType(values) {
|
|
517
|
+
const types = new Set(values.map(classifyValue));
|
|
518
|
+
if (types.size === 1) {
|
|
519
|
+
const type = [...types][0];
|
|
520
|
+
if (type === "string") return import_zod.z.string();
|
|
521
|
+
if (type === "number") return import_zod.z.number();
|
|
522
|
+
if (type === "boolean") return import_zod.z.boolean();
|
|
523
|
+
if (type === "null") return import_zod.z.null();
|
|
524
|
+
if (type === "object") {
|
|
525
|
+
return inferSchema(values);
|
|
526
|
+
}
|
|
527
|
+
if (type === "array") {
|
|
528
|
+
const allItems = values.flatMap(
|
|
529
|
+
(v) => Array.isArray(v) ? v : []
|
|
530
|
+
);
|
|
531
|
+
if (allItems.length === 0) return import_zod.z.array(import_zod.z.unknown());
|
|
532
|
+
return import_zod.z.array(inferFieldType(allItems));
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (types.size === 1 && types.has("string")) {
|
|
536
|
+
const unique = new Set(values);
|
|
537
|
+
if (unique.size === 1) {
|
|
538
|
+
return import_zod.z.literal(values[0]);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const members = [];
|
|
542
|
+
if (types.has("string")) members.push(import_zod.z.string());
|
|
543
|
+
if (types.has("number")) members.push(import_zod.z.number());
|
|
544
|
+
if (types.has("boolean")) members.push(import_zod.z.boolean());
|
|
545
|
+
if (types.has("null")) members.push(import_zod.z.null());
|
|
546
|
+
if (types.has("object")) {
|
|
547
|
+
const objs = values.filter(
|
|
548
|
+
(v) => typeof v === "object" && v !== null && !Array.isArray(v)
|
|
549
|
+
);
|
|
550
|
+
members.push(inferSchema(objs));
|
|
551
|
+
}
|
|
552
|
+
if (types.has("array")) {
|
|
553
|
+
const arrs = values.filter(Array.isArray);
|
|
554
|
+
const allItems = arrs.flat();
|
|
555
|
+
members.push(
|
|
556
|
+
import_zod.z.array(allItems.length > 0 ? inferFieldType(allItems) : import_zod.z.unknown())
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
if (members.length === 1) return members[0];
|
|
560
|
+
if (members.length === 2) return import_zod.z.union([members[0], members[1]]);
|
|
561
|
+
return import_zod.z.union(members);
|
|
562
|
+
}
|
|
563
|
+
function classifyValue(v) {
|
|
564
|
+
if (v === null) return "null";
|
|
565
|
+
if (Array.isArray(v)) return "array";
|
|
566
|
+
return typeof v;
|
|
567
|
+
}
|
|
568
|
+
function inferPrimitiveUnion(samples) {
|
|
569
|
+
const types = new Set(samples.map(classifyValue));
|
|
570
|
+
const members = [];
|
|
571
|
+
if (types.has("string")) members.push(import_zod.z.string());
|
|
572
|
+
if (types.has("number")) members.push(import_zod.z.number());
|
|
573
|
+
if (types.has("boolean")) members.push(import_zod.z.boolean());
|
|
574
|
+
if (types.has("null")) members.push(import_zod.z.null());
|
|
575
|
+
if (members.length === 0) return import_zod.z.unknown();
|
|
576
|
+
if (members.length === 1) return members[0];
|
|
577
|
+
return import_zod.z.union(members);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// src/schema-to-code.ts
|
|
581
|
+
var import_zod2 = require("zod");
|
|
582
|
+
function schemaToCode(schema, indent = 0) {
|
|
583
|
+
const pad = " ".repeat(indent);
|
|
584
|
+
if (schema instanceof import_zod2.z.ZodString) return "z.string()";
|
|
585
|
+
if (schema instanceof import_zod2.z.ZodNumber) return "z.number()";
|
|
586
|
+
if (schema instanceof import_zod2.z.ZodBoolean) return "z.boolean()";
|
|
587
|
+
if (schema instanceof import_zod2.z.ZodNull) return "z.null()";
|
|
588
|
+
if (schema instanceof import_zod2.z.ZodUnknown) return "z.unknown()";
|
|
589
|
+
if (schema instanceof import_zod2.z.ZodLiteral) {
|
|
590
|
+
const val = schema.value;
|
|
591
|
+
return typeof val === "string" ? `z.literal("${val}")` : `z.literal(${val})`;
|
|
592
|
+
}
|
|
593
|
+
if (schema instanceof import_zod2.z.ZodArray) {
|
|
594
|
+
const inner = schemaToCode(schema.element, indent);
|
|
595
|
+
return `z.array(${inner})`;
|
|
596
|
+
}
|
|
597
|
+
if (schema instanceof import_zod2.z.ZodObject) {
|
|
598
|
+
const shape = schema.shape;
|
|
599
|
+
const keys = Object.keys(shape);
|
|
600
|
+
if (keys.length === 0) return "z.object({})";
|
|
601
|
+
const inner = pad + " ";
|
|
602
|
+
const fields = keys.map(
|
|
603
|
+
(k) => `${inner}${safeProp(k)}: ${schemaToCode(shape[k], indent + 1)},`
|
|
604
|
+
);
|
|
605
|
+
const strict = schema._def && schema._def.unknownKeys === "strict" ? ".strict()" : "";
|
|
606
|
+
return `z.object({
|
|
607
|
+
${fields.join("\n")}
|
|
608
|
+
${pad}})${strict}`;
|
|
609
|
+
}
|
|
610
|
+
if (schema instanceof import_zod2.z.ZodUnion) {
|
|
611
|
+
const opts = schema.options;
|
|
612
|
+
const members = opts.map((o) => schemaToCode(o, indent));
|
|
613
|
+
if (members.every((m) => !m.includes("\n"))) {
|
|
614
|
+
return `z.union([${members.join(", ")}])`;
|
|
615
|
+
}
|
|
616
|
+
const inner = pad + " ";
|
|
617
|
+
return `z.union([
|
|
618
|
+
${members.map((m) => `${inner}${m},`).join("\n")}
|
|
619
|
+
${pad}])`;
|
|
620
|
+
}
|
|
621
|
+
if (schema instanceof import_zod2.z.ZodDiscriminatedUnion) {
|
|
622
|
+
const disc = schema._def.discriminator;
|
|
623
|
+
const opts = schema._def.options;
|
|
624
|
+
const members = opts.map((o) => schemaToCode(o, indent + 1));
|
|
625
|
+
const inner = pad + " ";
|
|
626
|
+
return `z.discriminatedUnion("${disc}", [
|
|
627
|
+
${members.map((m) => `${inner}${m},`).join("\n")}
|
|
628
|
+
${pad}])`;
|
|
629
|
+
}
|
|
630
|
+
return "z.unknown()";
|
|
631
|
+
}
|
|
632
|
+
function safeProp(key) {
|
|
633
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `"${key}"`;
|
|
634
|
+
}
|
|
635
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
636
|
+
0 && (module.exports = {
|
|
637
|
+
configure,
|
|
638
|
+
configureBatch,
|
|
639
|
+
forceFlush,
|
|
640
|
+
inferSchema,
|
|
641
|
+
progressive,
|
|
642
|
+
schemaToCode,
|
|
643
|
+
shutdown
|
|
644
|
+
});
|
|
645
|
+
//# sourceMappingURL=client.cjs.map
|