shardwire 0.0.1 → 0.1.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/README.md +251 -55
- package/dist/index.d.mts +75 -7
- package/dist/index.d.ts +75 -7
- package/dist/index.js +267 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +264 -23
- package/dist/index.mjs.map +1 -1
- package/package.json +27 -5
package/dist/index.mjs
CHANGED
|
@@ -64,6 +64,14 @@ function stringifyEnvelope(envelope) {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// src/runtime/validation.ts
|
|
67
|
+
var PayloadValidationError = class extends Error {
|
|
68
|
+
constructor(message, details) {
|
|
69
|
+
super(message);
|
|
70
|
+
this.details = details;
|
|
71
|
+
this.name = "PayloadValidationError";
|
|
72
|
+
}
|
|
73
|
+
details;
|
|
74
|
+
};
|
|
67
75
|
function isNonEmptyString(value) {
|
|
68
76
|
return typeof value === "string" && value.trim().length > 0;
|
|
69
77
|
}
|
|
@@ -77,8 +85,16 @@ function assertHostOptions(options) {
|
|
|
77
85
|
throw new Error("Host mode requires a server configuration.");
|
|
78
86
|
}
|
|
79
87
|
assertPositiveNumber("server.port", options.server.port);
|
|
80
|
-
if (!
|
|
81
|
-
throw new Error("server.
|
|
88
|
+
if (!Array.isArray(options.server.secrets) || options.server.secrets.length === 0) {
|
|
89
|
+
throw new Error("server.secrets must contain at least one secret.");
|
|
90
|
+
}
|
|
91
|
+
for (const [index, secret] of options.server.secrets.entries()) {
|
|
92
|
+
if (!isNonEmptyString(secret)) {
|
|
93
|
+
throw new Error(`server.secrets[${index}] must be a non-empty string.`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (options.server.primarySecretId !== void 0 && !options.server.secrets.some((_, index) => options.server.primarySecretId === `s${index}`)) {
|
|
97
|
+
throw new Error("server.primarySecretId must reference an existing secret id.");
|
|
82
98
|
}
|
|
83
99
|
if (options.server.heartbeatMs !== void 0) {
|
|
84
100
|
assertPositiveNumber("server.heartbeatMs", options.server.heartbeatMs);
|
|
@@ -97,6 +113,12 @@ function assertConsumerOptions(options) {
|
|
|
97
113
|
if (!isNonEmptyString(options.secret)) {
|
|
98
114
|
throw new Error("Consumer mode requires `secret`.");
|
|
99
115
|
}
|
|
116
|
+
if (options.secretId !== void 0 && !isNonEmptyString(options.secretId)) {
|
|
117
|
+
throw new Error("Consumer option `secretId` must be a non-empty string.");
|
|
118
|
+
}
|
|
119
|
+
if (options.clientName !== void 0 && !isNonEmptyString(options.clientName)) {
|
|
120
|
+
throw new Error("Consumer option `clientName` must be a non-empty string.");
|
|
121
|
+
}
|
|
100
122
|
if (options.requestTimeoutMs !== void 0) {
|
|
101
123
|
assertPositiveNumber("requestTimeoutMs", options.requestTimeoutMs);
|
|
102
124
|
}
|
|
@@ -119,9 +141,54 @@ function assertJsonPayload(kind, name, payload) {
|
|
|
119
141
|
throw new Error(`${kind} "${name}" payload must be JSON-serializable.`);
|
|
120
142
|
}
|
|
121
143
|
}
|
|
144
|
+
function normalizeSchemaIssues(error) {
|
|
145
|
+
if (!error || typeof error !== "object") {
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
const issues = error.issues;
|
|
149
|
+
if (!Array.isArray(issues)) {
|
|
150
|
+
return void 0;
|
|
151
|
+
}
|
|
152
|
+
const normalized = issues.map((issue) => {
|
|
153
|
+
if (!issue || typeof issue !== "object") {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const message = issue.message;
|
|
157
|
+
const rawPath = issue.path;
|
|
158
|
+
if (typeof message !== "string") {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const path = Array.isArray(rawPath) && rawPath.length > 0 ? rawPath.map((segment) => typeof segment === "string" || typeof segment === "number" ? String(segment) : "").filter(Boolean).join(".") : "";
|
|
162
|
+
return { path, message };
|
|
163
|
+
}).filter((issue) => Boolean(issue));
|
|
164
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
165
|
+
}
|
|
166
|
+
function parsePayloadWithSchema(schema, payload, context) {
|
|
167
|
+
if (!schema) {
|
|
168
|
+
return payload;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
return schema.parse(payload);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
const message = error instanceof Error && error.message.trim().length > 0 ? error.message : `Payload validation failed for ${context.stage} "${context.name}".`;
|
|
174
|
+
const issues = normalizeSchemaIssues(error);
|
|
175
|
+
throw new PayloadValidationError(message, {
|
|
176
|
+
...context,
|
|
177
|
+
...issues ? { issues } : {}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
122
181
|
|
|
123
182
|
// src/consumer/index.ts
|
|
124
183
|
var DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
|
|
184
|
+
var CommandRequestError = class extends Error {
|
|
185
|
+
constructor(code, message) {
|
|
186
|
+
super(message);
|
|
187
|
+
this.code = code;
|
|
188
|
+
this.name = "CommandRequestError";
|
|
189
|
+
}
|
|
190
|
+
code;
|
|
191
|
+
};
|
|
125
192
|
function createConsumerShardwire(options) {
|
|
126
193
|
const logger = withLogger(options.logger);
|
|
127
194
|
const reconnectEnabled = options.reconnect?.enabled ?? true;
|
|
@@ -138,8 +205,12 @@ function createConsumerShardwire(options) {
|
|
|
138
205
|
let connectResolve = null;
|
|
139
206
|
let connectReject = null;
|
|
140
207
|
let authTimeoutTimer = null;
|
|
208
|
+
let currentConnectionId = null;
|
|
141
209
|
const pendingRequests = /* @__PURE__ */ new Map();
|
|
142
210
|
const eventHandlers = /* @__PURE__ */ new Map();
|
|
211
|
+
const connectedHandlers = /* @__PURE__ */ new Set();
|
|
212
|
+
const disconnectedHandlers = /* @__PURE__ */ new Set();
|
|
213
|
+
const reconnectingHandlers = /* @__PURE__ */ new Set();
|
|
143
214
|
function clearAuthTimeout() {
|
|
144
215
|
if (authTimeoutTimer) {
|
|
145
216
|
clearTimeout(authTimeoutTimer);
|
|
@@ -168,10 +239,10 @@ function createConsumerShardwire(options) {
|
|
|
168
239
|
}
|
|
169
240
|
socket.send(data);
|
|
170
241
|
}
|
|
171
|
-
function rejectAllPending(reason) {
|
|
242
|
+
function rejectAllPending(code, reason) {
|
|
172
243
|
for (const [requestId, pending] of pendingRequests.entries()) {
|
|
173
244
|
clearTimeout(pending.timer);
|
|
174
|
-
pending.reject(new
|
|
245
|
+
pending.reject(new CommandRequestError(code, reason));
|
|
175
246
|
pendingRequests.delete(requestId);
|
|
176
247
|
}
|
|
177
248
|
}
|
|
@@ -181,6 +252,13 @@ function createConsumerShardwire(options) {
|
|
|
181
252
|
}
|
|
182
253
|
const delay = getBackoffDelay(reconnectAttempts, { initialDelayMs, maxDelayMs, jitter });
|
|
183
254
|
reconnectAttempts += 1;
|
|
255
|
+
for (const handler of reconnectingHandlers) {
|
|
256
|
+
try {
|
|
257
|
+
handler({ attempt: reconnectAttempts, delayMs: delay, at: Date.now() });
|
|
258
|
+
} catch (error) {
|
|
259
|
+
logger.warn("Reconnect handler threw an error.", { error: String(error) });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
184
262
|
reconnectTimer = setTimeout(() => {
|
|
185
263
|
reconnectTimer = null;
|
|
186
264
|
void connect().catch((error) => {
|
|
@@ -219,7 +297,12 @@ function createConsumerShardwire(options) {
|
|
|
219
297
|
socket.on("open", () => {
|
|
220
298
|
reconnectAttempts = 0;
|
|
221
299
|
isAuthed = false;
|
|
222
|
-
|
|
300
|
+
currentConnectionId = null;
|
|
301
|
+
const hello = makeEnvelope("auth.hello", {
|
|
302
|
+
secret: options.secret,
|
|
303
|
+
secretId: options.secretId,
|
|
304
|
+
clientName: options.clientName
|
|
305
|
+
});
|
|
223
306
|
socket?.send(stringifyEnvelope(hello));
|
|
224
307
|
authTimeoutTimer = setTimeout(() => {
|
|
225
308
|
if (!isAuthed) {
|
|
@@ -235,6 +318,18 @@ function createConsumerShardwire(options) {
|
|
|
235
318
|
switch (envelope.type) {
|
|
236
319
|
case "auth.ok":
|
|
237
320
|
isAuthed = true;
|
|
321
|
+
if (envelope.payload && typeof envelope.payload === "object" && "connectionId" in envelope.payload && typeof envelope.payload.connectionId === "string") {
|
|
322
|
+
currentConnectionId = envelope.payload.connectionId;
|
|
323
|
+
}
|
|
324
|
+
if (currentConnectionId) {
|
|
325
|
+
for (const handler of connectedHandlers) {
|
|
326
|
+
try {
|
|
327
|
+
handler({ connectionId: currentConnectionId, connectedAt: Date.now() });
|
|
328
|
+
} catch (error) {
|
|
329
|
+
logger.warn("Connected handler threw an error.", { error: String(error) });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
238
333
|
resolveConnect();
|
|
239
334
|
break;
|
|
240
335
|
case "auth.error": {
|
|
@@ -244,7 +339,7 @@ function createConsumerShardwire(options) {
|
|
|
244
339
|
message: payload.message
|
|
245
340
|
});
|
|
246
341
|
rejectConnect(payload.message);
|
|
247
|
-
rejectAllPending("Shardwire authentication failed.");
|
|
342
|
+
rejectAllPending("UNAUTHORIZED", "Shardwire authentication failed.");
|
|
248
343
|
socket?.close();
|
|
249
344
|
break;
|
|
250
345
|
}
|
|
@@ -283,8 +378,22 @@ function createConsumerShardwire(options) {
|
|
|
283
378
|
}
|
|
284
379
|
});
|
|
285
380
|
socket.on("close", () => {
|
|
381
|
+
const willReconnect = !isClosed && reconnectEnabled;
|
|
286
382
|
rejectConnect("Shardwire connection closed.");
|
|
287
383
|
isAuthed = false;
|
|
384
|
+
currentConnectionId = null;
|
|
385
|
+
rejectAllPending("DISCONNECTED", "Shardwire connection closed before command completed.");
|
|
386
|
+
for (const handler of disconnectedHandlers) {
|
|
387
|
+
try {
|
|
388
|
+
handler({
|
|
389
|
+
reason: "Shardwire connection closed.",
|
|
390
|
+
at: Date.now(),
|
|
391
|
+
willReconnect
|
|
392
|
+
});
|
|
393
|
+
} catch (error) {
|
|
394
|
+
logger.warn("Disconnected handler threw an error.", { error: String(error) });
|
|
395
|
+
}
|
|
396
|
+
}
|
|
288
397
|
if (!isClosed) {
|
|
289
398
|
scheduleReconnect();
|
|
290
399
|
}
|
|
@@ -311,7 +420,7 @@ function createConsumerShardwire(options) {
|
|
|
311
420
|
requestId: sendOptions?.requestId ?? "unknown",
|
|
312
421
|
ts: Date.now(),
|
|
313
422
|
error: {
|
|
314
|
-
code: "
|
|
423
|
+
code: "UNAUTHORIZED",
|
|
315
424
|
message: error instanceof Error ? error.message : "Failed to authenticate."
|
|
316
425
|
}
|
|
317
426
|
};
|
|
@@ -323,7 +432,7 @@ function createConsumerShardwire(options) {
|
|
|
323
432
|
requestId: sendOptions?.requestId ?? "unknown",
|
|
324
433
|
ts: Date.now(),
|
|
325
434
|
error: {
|
|
326
|
-
code: "
|
|
435
|
+
code: "DISCONNECTED",
|
|
327
436
|
message: "Not connected to Shardwire host."
|
|
328
437
|
}
|
|
329
438
|
};
|
|
@@ -333,9 +442,13 @@ function createConsumerShardwire(options) {
|
|
|
333
442
|
const promise = new Promise((resolve, reject) => {
|
|
334
443
|
const timer = setTimeout(() => {
|
|
335
444
|
pendingRequests.delete(requestId);
|
|
336
|
-
reject(new
|
|
445
|
+
reject(new CommandRequestError("TIMEOUT", `Command "${name}" timed out after ${timeoutMs}ms.`));
|
|
337
446
|
}, timeoutMs);
|
|
338
|
-
pendingRequests.set(requestId, {
|
|
447
|
+
pendingRequests.set(requestId, {
|
|
448
|
+
resolve,
|
|
449
|
+
reject: (error) => reject(error),
|
|
450
|
+
timer
|
|
451
|
+
});
|
|
339
452
|
});
|
|
340
453
|
sendRaw(
|
|
341
454
|
stringifyEnvelope(
|
|
@@ -352,13 +465,14 @@ function createConsumerShardwire(options) {
|
|
|
352
465
|
try {
|
|
353
466
|
return await promise;
|
|
354
467
|
} catch (error) {
|
|
468
|
+
const failureCode = error instanceof CommandRequestError ? error.code : !socket || socket.readyState !== 1 ? "DISCONNECTED" : "TIMEOUT";
|
|
355
469
|
return {
|
|
356
470
|
ok: false,
|
|
357
471
|
requestId,
|
|
358
472
|
ts: Date.now(),
|
|
359
473
|
error: {
|
|
360
|
-
code:
|
|
361
|
-
message: error instanceof Error ? error.message : "Command request
|
|
474
|
+
code: failureCode,
|
|
475
|
+
message: error instanceof Error ? error.message : "Command request failed."
|
|
362
476
|
}
|
|
363
477
|
};
|
|
364
478
|
}
|
|
@@ -394,18 +508,43 @@ function createConsumerShardwire(options) {
|
|
|
394
508
|
eventHandlers.delete(name);
|
|
395
509
|
}
|
|
396
510
|
},
|
|
511
|
+
onConnected(handler) {
|
|
512
|
+
connectedHandlers.add(handler);
|
|
513
|
+
return () => {
|
|
514
|
+
connectedHandlers.delete(handler);
|
|
515
|
+
};
|
|
516
|
+
},
|
|
517
|
+
onDisconnected(handler) {
|
|
518
|
+
disconnectedHandlers.add(handler);
|
|
519
|
+
return () => {
|
|
520
|
+
disconnectedHandlers.delete(handler);
|
|
521
|
+
};
|
|
522
|
+
},
|
|
523
|
+
onReconnecting(handler) {
|
|
524
|
+
reconnectingHandlers.add(handler);
|
|
525
|
+
return () => {
|
|
526
|
+
reconnectingHandlers.delete(handler);
|
|
527
|
+
};
|
|
528
|
+
},
|
|
529
|
+
ready() {
|
|
530
|
+
return connect();
|
|
531
|
+
},
|
|
397
532
|
connected() {
|
|
398
533
|
return Boolean(socket && socket.readyState === 1 && isAuthed);
|
|
399
534
|
},
|
|
535
|
+
connectionId() {
|
|
536
|
+
return currentConnectionId;
|
|
537
|
+
},
|
|
400
538
|
async close() {
|
|
401
539
|
isClosed = true;
|
|
402
540
|
isAuthed = false;
|
|
541
|
+
currentConnectionId = null;
|
|
403
542
|
rejectConnect("Shardwire consumer has been closed.");
|
|
404
543
|
if (reconnectTimer) {
|
|
405
544
|
clearTimeout(reconnectTimer);
|
|
406
545
|
reconnectTimer = null;
|
|
407
546
|
}
|
|
408
|
-
rejectAllPending("Shardwire consumer has been closed.");
|
|
547
|
+
rejectAllPending("DISCONNECTED", "Shardwire consumer has been closed.");
|
|
409
548
|
if (!socket) {
|
|
410
549
|
return;
|
|
411
550
|
}
|
|
@@ -489,6 +628,9 @@ function isSecretValid(provided, expected) {
|
|
|
489
628
|
}
|
|
490
629
|
return timingSafeEqual(providedBuffer, expectedBuffer);
|
|
491
630
|
}
|
|
631
|
+
function getSecretId(secretIndex) {
|
|
632
|
+
return `s${secretIndex}`;
|
|
633
|
+
}
|
|
492
634
|
|
|
493
635
|
// src/transport/ws/host-server.ts
|
|
494
636
|
var CLOSE_AUTH_REQUIRED = 4001;
|
|
@@ -607,13 +749,33 @@ var HostWebSocketServer = class {
|
|
|
607
749
|
return;
|
|
608
750
|
}
|
|
609
751
|
const payload = envelope.payload;
|
|
610
|
-
|
|
752
|
+
const providedSecret = payload?.secret;
|
|
753
|
+
const knownSecrets = this.config.options.server.secrets;
|
|
754
|
+
const secretId = payload?.secretId;
|
|
755
|
+
let authReason = null;
|
|
756
|
+
if (!providedSecret) {
|
|
757
|
+
authReason = "invalid_secret";
|
|
758
|
+
} else if (secretId) {
|
|
759
|
+
const secretIndex = knownSecrets.findIndex((_, index) => getSecretId(index) === secretId);
|
|
760
|
+
if (secretIndex < 0) {
|
|
761
|
+
authReason = "unknown_secret_id";
|
|
762
|
+
} else {
|
|
763
|
+
const expectedSecret = knownSecrets[secretIndex];
|
|
764
|
+
if (!expectedSecret || !isSecretValid(providedSecret, expectedSecret)) {
|
|
765
|
+
authReason = "invalid_secret";
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
} else if (!knownSecrets.some((secret) => isSecretValid(providedSecret, secret))) {
|
|
769
|
+
authReason = "invalid_secret";
|
|
770
|
+
}
|
|
771
|
+
if (authReason) {
|
|
611
772
|
this.safeSend(
|
|
612
773
|
state.socket,
|
|
613
774
|
stringifyEnvelope(
|
|
614
775
|
makeEnvelope("auth.error", {
|
|
615
|
-
code: "
|
|
616
|
-
|
|
776
|
+
code: "UNAUTHORIZED",
|
|
777
|
+
reason: authReason,
|
|
778
|
+
message: "Authentication failed."
|
|
617
779
|
})
|
|
618
780
|
)
|
|
619
781
|
);
|
|
@@ -722,21 +884,44 @@ function createHostShardwire(options, runtimeHooks) {
|
|
|
722
884
|
context.source = source;
|
|
723
885
|
}
|
|
724
886
|
try {
|
|
725
|
-
const
|
|
887
|
+
const commandValidation = options.validation?.commands?.[payload.name];
|
|
888
|
+
const validatedRequest = parsePayloadWithSchema(commandValidation?.request, payload.data, {
|
|
889
|
+
name: payload.name,
|
|
890
|
+
stage: "command.request"
|
|
891
|
+
});
|
|
892
|
+
const maybePromise = Promise.resolve(handler(validatedRequest, context));
|
|
726
893
|
const value = await withTimeout(
|
|
727
894
|
maybePromise,
|
|
728
895
|
commandTimeoutMs,
|
|
729
896
|
`Command "${payload.name}" timed out after ${commandTimeoutMs}ms.`
|
|
730
897
|
);
|
|
898
|
+
const validatedResponse = parsePayloadWithSchema(commandValidation?.response, value, {
|
|
899
|
+
name: payload.name,
|
|
900
|
+
stage: "command.response"
|
|
901
|
+
});
|
|
731
902
|
const success = {
|
|
732
903
|
ok: true,
|
|
733
904
|
requestId,
|
|
734
905
|
ts: Date.now(),
|
|
735
|
-
data:
|
|
906
|
+
data: validatedResponse
|
|
736
907
|
};
|
|
737
908
|
dedupeCache.set(cacheKey, success);
|
|
738
909
|
return success;
|
|
739
910
|
} catch (error) {
|
|
911
|
+
if (error instanceof PayloadValidationError) {
|
|
912
|
+
const failure2 = {
|
|
913
|
+
ok: false,
|
|
914
|
+
requestId,
|
|
915
|
+
ts: Date.now(),
|
|
916
|
+
error: {
|
|
917
|
+
code: "VALIDATION_ERROR",
|
|
918
|
+
message: error.message,
|
|
919
|
+
details: error.details
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
dedupeCache.set(cacheKey, failure2);
|
|
923
|
+
return failure2;
|
|
924
|
+
}
|
|
740
925
|
const isTimeout = error instanceof Error && /timed out/i.test(error.message);
|
|
741
926
|
const failure = {
|
|
742
927
|
ok: false,
|
|
@@ -763,13 +948,21 @@ function createHostShardwire(options, runtimeHooks) {
|
|
|
763
948
|
},
|
|
764
949
|
emitEvent(name, payload) {
|
|
765
950
|
assertMessageName("event", name);
|
|
766
|
-
|
|
767
|
-
|
|
951
|
+
const validatedPayload = parsePayloadWithSchema(options.validation?.events?.[name], payload, {
|
|
952
|
+
name,
|
|
953
|
+
stage: "event.emit"
|
|
954
|
+
});
|
|
955
|
+
assertJsonPayload("event", name, validatedPayload);
|
|
956
|
+
hostServer.emitEvent(name, validatedPayload, options.name);
|
|
768
957
|
},
|
|
769
958
|
broadcast(name, payload) {
|
|
770
959
|
assertMessageName("event", name);
|
|
771
|
-
|
|
772
|
-
|
|
960
|
+
const validatedPayload = parsePayloadWithSchema(options.validation?.events?.[name], payload, {
|
|
961
|
+
name,
|
|
962
|
+
stage: "event.emit"
|
|
963
|
+
});
|
|
964
|
+
assertJsonPayload("event", name, validatedPayload);
|
|
965
|
+
hostServer.emitEvent(name, validatedPayload, options.name);
|
|
773
966
|
},
|
|
774
967
|
close() {
|
|
775
968
|
return hostServer.close().then(async () => {
|
|
@@ -779,6 +972,52 @@ function createHostShardwire(options, runtimeHooks) {
|
|
|
779
972
|
};
|
|
780
973
|
}
|
|
781
974
|
|
|
975
|
+
// src/schema/index.ts
|
|
976
|
+
function fromSafeParseSchema(schema) {
|
|
977
|
+
return {
|
|
978
|
+
parse(value) {
|
|
979
|
+
const result = schema.safeParse(value);
|
|
980
|
+
if (result.success) {
|
|
981
|
+
return result.data;
|
|
982
|
+
}
|
|
983
|
+
const error = new Error(result.error.message);
|
|
984
|
+
if (result.error.issues) {
|
|
985
|
+
error.issues = result.error.issues;
|
|
986
|
+
}
|
|
987
|
+
throw error;
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// src/schema/zod.ts
|
|
993
|
+
function normalizeZodPath(path) {
|
|
994
|
+
if (path.length === 0) {
|
|
995
|
+
return "";
|
|
996
|
+
}
|
|
997
|
+
return path.filter((segment) => typeof segment === "string" || typeof segment === "number").map((segment) => String(segment)).join(".");
|
|
998
|
+
}
|
|
999
|
+
function fromZodSchema(schema) {
|
|
1000
|
+
return fromSafeParseSchema({
|
|
1001
|
+
safeParse(value) {
|
|
1002
|
+
const result = schema.safeParse(value);
|
|
1003
|
+
if (result.success) {
|
|
1004
|
+
return { success: true, data: result.data };
|
|
1005
|
+
}
|
|
1006
|
+
const issues = result.error.issues.map((issue) => ({
|
|
1007
|
+
path: normalizeZodPath(issue.path),
|
|
1008
|
+
message: issue.message
|
|
1009
|
+
}));
|
|
1010
|
+
return {
|
|
1011
|
+
success: false,
|
|
1012
|
+
error: {
|
|
1013
|
+
message: "Schema validation failed.",
|
|
1014
|
+
issues
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
|
|
782
1021
|
// src/index.ts
|
|
783
1022
|
function isHostOptions(options) {
|
|
784
1023
|
return "server" in options;
|
|
@@ -813,6 +1052,8 @@ function createShardwire(options) {
|
|
|
813
1052
|
});
|
|
814
1053
|
}
|
|
815
1054
|
export {
|
|
816
|
-
createShardwire
|
|
1055
|
+
createShardwire,
|
|
1056
|
+
fromSafeParseSchema,
|
|
1057
|
+
fromZodSchema
|
|
817
1058
|
};
|
|
818
1059
|
//# sourceMappingURL=index.mjs.map
|