svelte-realtime 0.4.17 → 0.4.19
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 +18 -3
- package/package.json +1 -1
- package/server.d.ts +13 -2
- package/server.js +50 -10
package/README.md
CHANGED
|
@@ -574,7 +574,7 @@ Same arguments return the same cached store instance. The cache is cleaned up wh
|
|
|
574
574
|
|
|
575
575
|
## Schema validation
|
|
576
576
|
|
|
577
|
-
Use `live.validated(schema, fn)` to validate the first argument against a
|
|
577
|
+
Use `live.validated(schema, fn)` to validate the first argument against a schema before the function runs. Any [Standard Schema](https://standardschema.dev/)-compatible validator is supported, including Zod, ArkType, Valibot, and others.
|
|
578
578
|
|
|
579
579
|
```js
|
|
580
580
|
import { z } from 'zod';
|
|
@@ -592,7 +592,22 @@ export const addTodo = live.validated(CreateTodo, async (ctx, input) => {
|
|
|
592
592
|
});
|
|
593
593
|
```
|
|
594
594
|
|
|
595
|
-
|
|
595
|
+
Because `live.validated()` uses the [Standard Schema](https://standardschema.dev/) interface, you can swap in any compatible validator:
|
|
596
|
+
|
|
597
|
+
```js
|
|
598
|
+
import { type } from 'arktype';
|
|
599
|
+
import { live } from 'svelte-realtime/server';
|
|
600
|
+
|
|
601
|
+
const CreateTodo = type({ text: 'string>0', priority: '"low"|"medium"|"high"|undefined' });
|
|
602
|
+
|
|
603
|
+
export const addTodo = live.validated(CreateTodo, async (ctx, input) => {
|
|
604
|
+
const todo = await db.todos.insert({ ...input, userId: ctx.user.id });
|
|
605
|
+
ctx.publish('todos', 'created', todo);
|
|
606
|
+
return todo;
|
|
607
|
+
});
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
On the client, validated exports work like regular `live()` calls. Validation errors are thrown as `RpcError` with `code: 'VALIDATION'` and an `issues` array.
|
|
596
611
|
|
|
597
612
|
---
|
|
598
613
|
|
|
@@ -1948,7 +1963,7 @@ Import from `svelte-realtime/server`.
|
|
|
1948
1963
|
| `live.stream(topic, initFn, options?)` | Create a reactive stream |
|
|
1949
1964
|
| `live.channel(topic, options?)` | Create an ephemeral pub/sub channel |
|
|
1950
1965
|
| `live.binary(fn, options?)` | Mark a function as a binary RPC handler (`maxSize` limits payload, default 10MB) |
|
|
1951
|
-
| `live.validated(schema, fn)` | RPC with
|
|
1966
|
+
| `live.validated(schema, fn)` | RPC with [Standard Schema](https://standardschema.dev/) input validation (Zod, ArkType, Valibot, etc.) |
|
|
1952
1967
|
| `live.cron(schedule, topic, fn)` | Server-side scheduled function |
|
|
1953
1968
|
| `live.derived(sources, fn, options?)` | Server-side computed stream (static or dynamic sources) |
|
|
1954
1969
|
| `live.effect(sources, fn, options?)` | Server-side reactive side effect |
|
package/package.json
CHANGED
package/server.d.ts
CHANGED
|
@@ -370,18 +370,29 @@ export namespace live {
|
|
|
370
370
|
/**
|
|
371
371
|
* Mark a function as RPC-callable with schema validation.
|
|
372
372
|
* Validates args[0] against the schema before calling fn.
|
|
373
|
-
* Supports
|
|
373
|
+
* Supports any [Standard Schema](https://standardschema.dev/)-compatible schema,
|
|
374
|
+
* including Zod, ArkType, Valibot v1+, and others.
|
|
374
375
|
*
|
|
375
|
-
* @param schema - Zod
|
|
376
|
+
* @param schema - Zod, ArkType, Valibot, or any Standard Schema-compatible schema
|
|
376
377
|
* @param fn - Handler function (ctx, validatedInput, ...rest)
|
|
377
378
|
*
|
|
378
379
|
* @example
|
|
379
380
|
* ```js
|
|
381
|
+
* import { z } from 'zod';
|
|
380
382
|
* const SendSchema = z.object({ text: z.string().min(1) });
|
|
381
383
|
* export const send = live.validated(SendSchema, async (ctx, input) => {
|
|
382
384
|
* // input is validated and typed
|
|
383
385
|
* });
|
|
384
386
|
* ```
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```js
|
|
390
|
+
* import { type } from 'arktype';
|
|
391
|
+
* const SendSchema = type({ text: 'string>0' });
|
|
392
|
+
* export const send = live.validated(SendSchema, async (ctx, input) => {
|
|
393
|
+
* // works with any Standard Schema-compatible validator
|
|
394
|
+
* });
|
|
395
|
+
* ```
|
|
385
396
|
*/
|
|
386
397
|
function validated<S, T extends (ctx: LiveContext<any>, input: any, ...args: any[]) => any>(
|
|
387
398
|
schema: S,
|
package/server.js
CHANGED
|
@@ -600,9 +600,10 @@ live.rateLimit = function rateLimit(config, fn) {
|
|
|
600
600
|
/**
|
|
601
601
|
* Mark a function as RPC-callable with schema validation.
|
|
602
602
|
* Validates args[0] against the schema before calling fn.
|
|
603
|
-
* Supports
|
|
603
|
+
* Supports any Standard Schema-compatible schema (https://standardschema.dev/),
|
|
604
|
+
* including Zod, ArkType, Valibot v1+, and others.
|
|
604
605
|
*
|
|
605
|
-
* @param {any} schema - Zod
|
|
606
|
+
* @param {any} schema - Zod, ArkType, Valibot, or any Standard Schema-compatible schema
|
|
606
607
|
* @param {Function} fn - Handler function (ctx, validatedInput, ...rest)
|
|
607
608
|
* @returns {Function}
|
|
608
609
|
*/
|
|
@@ -626,13 +627,36 @@ live.validated = function validated(schema, fn) {
|
|
|
626
627
|
};
|
|
627
628
|
|
|
628
629
|
/**
|
|
629
|
-
* Validate input against a
|
|
630
|
+
* Validate input against a Standard Schema-compatible schema, with legacy Zod/Valibot fallbacks.
|
|
630
631
|
* @param {any} schema
|
|
631
632
|
* @param {any} input
|
|
632
633
|
* @returns {{ ok: true, data: any } | { ok: false, message: string, issues: Array<{ path: string[], message: string }> }}
|
|
633
634
|
*/
|
|
634
635
|
function _validate(schema, input) {
|
|
635
|
-
//
|
|
636
|
+
// Standard Schema: schema exposes `~standard.validate` (https://standardschema.dev/)
|
|
637
|
+
if (schema?.['~standard'] && typeof schema['~standard'].validate === 'function') {
|
|
638
|
+
const result = schema['~standard'].validate(input);
|
|
639
|
+
if (result instanceof Promise) {
|
|
640
|
+
return {
|
|
641
|
+
ok: false,
|
|
642
|
+
message: 'Async schemas are not supported in live.validated(). Use a synchronous schema.',
|
|
643
|
+
issues: [{ path: [], message: 'Async schema not supported' }]
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
if (result.issues == null) {
|
|
647
|
+
return { ok: true, data: result.value };
|
|
648
|
+
}
|
|
649
|
+
const issues = result.issues.map((/** @type {any} */ i) => ({
|
|
650
|
+
path: (i.path || []).map((/** @type {any} */ p) => {
|
|
651
|
+
const key = typeof p === 'object' && p !== null && 'key' in p ? p.key : p;
|
|
652
|
+
return key != null ? String(key) : '';
|
|
653
|
+
}).filter((k) => k !== ''),
|
|
654
|
+
message: i.message || 'Validation failed'
|
|
655
|
+
}));
|
|
656
|
+
return { ok: false, message: 'Validation failed', issues };
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Zod legacy fallback: schema has .safeParse method
|
|
636
660
|
if (typeof schema?.safeParse === 'function') {
|
|
637
661
|
const result = schema.safeParse(input);
|
|
638
662
|
if (result.success) {
|
|
@@ -649,7 +673,7 @@ function _validate(schema, input) {
|
|
|
649
673
|
};
|
|
650
674
|
}
|
|
651
675
|
|
|
652
|
-
// Valibot
|
|
676
|
+
// Valibot legacy fallback: schema is passed to a standalone safeParse
|
|
653
677
|
// In Valibot v1, schemas have a ._run or .pipe method
|
|
654
678
|
// Try to use the schema directly as a Valibot schema
|
|
655
679
|
if (schema?._run || schema?.pipe || schema?.type) {
|
|
@@ -675,7 +699,7 @@ function _validate(schema, input) {
|
|
|
675
699
|
// Unknown schema type -- reject. Passing unvalidated input through is a security risk.
|
|
676
700
|
return {
|
|
677
701
|
ok: false,
|
|
678
|
-
message: 'Unrecognized schema type passed to live.validated(). Supported: Zod (.safeParse), Valibot (._run).',
|
|
702
|
+
message: 'Unrecognized schema type passed to live.validated(). Supported: Standard Schema (https://standardschema.dev/), Zod (.safeParse), Valibot (._run).',
|
|
679
703
|
issues: [{ path: [], message: 'Unrecognized schema type' }]
|
|
680
704
|
};
|
|
681
705
|
}
|
|
@@ -1369,8 +1393,24 @@ export function __registerDerived(path, fn) {
|
|
|
1369
1393
|
if (/** @type {any} */ (fn).__derivedDynamic) {
|
|
1370
1394
|
const sourceFactory = /** @type {any} */ (fn).__derivedSourceFactory;
|
|
1371
1395
|
const debounce = /** @type {any} */ (fn).__derivedDebounce || 0;
|
|
1396
|
+
|
|
1397
|
+
/** @type {Map<string, any[]>} */
|
|
1398
|
+
const topicArgs = new Map();
|
|
1399
|
+
const topicFn = (...args) => {
|
|
1400
|
+
const t = path + '\x00' + args.map(a => String(a).replace(/\x00/g, '')).join('\x00');
|
|
1401
|
+
topicArgs.set(t, args);
|
|
1402
|
+
if (topicArgs.size > 10000) {
|
|
1403
|
+
const iter = topicArgs.keys();
|
|
1404
|
+
topicArgs.delete(iter.next().value);
|
|
1405
|
+
}
|
|
1406
|
+
return t;
|
|
1407
|
+
};
|
|
1408
|
+
/** @type {any} */ (topicFn).__topicUsesCtx = false;
|
|
1409
|
+
/** @type {any} */ (fn).__streamTopic = topicFn;
|
|
1410
|
+
/** @type {any} */ (fn).__derivedTopicArgs = topicArgs;
|
|
1411
|
+
|
|
1372
1412
|
const entry = {
|
|
1373
|
-
sources: null, sourceFactory, fn, topic:
|
|
1413
|
+
sources: null, sourceFactory, fn, topic: topicFn,
|
|
1374
1414
|
debounce, timer: null, dynamic: true, instances: new Map()
|
|
1375
1415
|
};
|
|
1376
1416
|
derivedRegistry.set(path, entry);
|
|
@@ -1379,11 +1419,11 @@ export function __registerDerived(path, fn) {
|
|
|
1379
1419
|
return;
|
|
1380
1420
|
}
|
|
1381
1421
|
|
|
1422
|
+
/** @type {any} */ (fn).__streamTopic = path;
|
|
1382
1423
|
const sources = /** @type {any} */ (fn).__derivedSources;
|
|
1383
|
-
const topic = /** @type {any} */ (fn).__streamTopic;
|
|
1384
1424
|
const debounce = /** @type {any} */ (fn).__derivedDebounce || 0;
|
|
1385
|
-
if (!sources
|
|
1386
|
-
derivedRegistry.set(path, { sources, fn, topic, debounce, timer: null });
|
|
1425
|
+
if (!sources) return;
|
|
1426
|
+
derivedRegistry.set(path, { sources, fn, topic: path, debounce, timer: null });
|
|
1387
1427
|
for (const src of sources) {
|
|
1388
1428
|
let set = _derivedBySource.get(src);
|
|
1389
1429
|
if (!set) { set = new Set(); _derivedBySource.set(src, set); }
|