Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Before proceeding, ensure you have TypeScript 5 installed and configured appropr
To use River, install the required packages using npm:

```bash
npm i @replit/river @sinclair/typebox
npm i @replit/river typebox
```

## Writing services
Expand All @@ -72,7 +72,7 @@ First, we create a service:

```ts
import { createServiceSchema, Procedure, Ok } from '@replit/river';
import { Type } from '@sinclair/typebox';
import { Type } from 'typebox';

const ServiceSchema = createServiceSchema();
export const ExampleService = ServiceSchema.define(
Expand Down
2 changes: 1 addition & 1 deletion __tests__/cancellation.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TNever, TObject, Type } from '@sinclair/typebox';
import { TNever, TObject, Type } from 'typebox';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import {
Err,
Expand Down
2 changes: 1 addition & 1 deletion __tests__/cleanup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
import { testMatrix } from '../testUtil/fixtures/matrix';
import { TestSetupHelpers } from '../testUtil/fixtures/transports';
import { ControlFlags } from '../transport/message';
import { Type } from '@sinclair/typebox';
import { Type } from 'typebox';
import { nanoid } from 'nanoid';

describe.each(testMatrix())(
Expand Down
2 changes: 1 addition & 1 deletion __tests__/context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
import { testMatrix } from '../testUtil/fixtures/matrix';
import { TestSetupHelpers } from '../testUtil/fixtures/transports';
import { Ok, Procedure, createClient, createServer } from '../router';
import { Type } from '@sinclair/typebox';
import { Type } from 'typebox';
import { createServiceSchema } from '../router/services';

describe('should handle incompatabilities', async () => {
Expand Down
2 changes: 1 addition & 1 deletion __tests__/deferCleanup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
test,
vi,
} from 'vitest';
import { Type } from '@sinclair/typebox';
import { Type } from 'typebox';
import {
createClient,
createServer,
Expand Down
2 changes: 1 addition & 1 deletion __tests__/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
waitFor,
} from '../testUtil/fixtures/cleanup';
import { testMatrix } from '../testUtil/fixtures/matrix';
import { Type } from '@sinclair/typebox';
import { Type } from 'typebox';
import {
Procedure,
createServiceSchema,
Expand Down
22 changes: 13 additions & 9 deletions __tests__/invalid-request.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Type } from '@sinclair/typebox';
import { Type } from 'typebox';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import {
Err,
Expand Down Expand Up @@ -395,14 +395,13 @@ describe('cancels invalid request', () => {
code: INVALID_REQUEST_CODE,
message: 'message in requestData position did not match schema',
extras: {
totalErrors: 2,
totalErrors: 1,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
firstValidationErrors: expect.arrayContaining([
{
path: '/mustSendThings',
message: 'Expected required property',
message: 'must have required properties mustSendThings',
},
{ path: '/mustSendThings', message: 'Expected string' },
]),
},
}),
Expand Down Expand Up @@ -463,10 +462,16 @@ describe('cancels invalid request', () => {
code: INVALID_REQUEST_CODE,
message: 'message in control payload position did not match schema',
extras: {
totalErrors: 1,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
totalErrors: expect.any(Number),
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
firstValidationErrors: expect.arrayContaining([
{ path: '', message: 'Expected union value' },
expect.objectContaining({
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
path: expect.any(String),
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
message: expect.any(String),
}),
]),
},
}),
Expand Down Expand Up @@ -597,14 +602,13 @@ describe('cancels invalid request', () => {
'message in requestData position did not match schema',
),
extras: {
totalErrors: 2,
totalErrors: 1,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
firstValidationErrors: expect.arrayContaining([
{
path: '/newRequiredField',
message: 'Expected required property',
message: 'must have required properties newRequiredField',
},
{ path: '/newRequiredField', message: 'Expected string' },
]),
},
}),
Expand Down
2 changes: 1 addition & 1 deletion __tests__/middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
Middleware,
} from '../router';
import { createMockTransportNetwork } from '../testUtil/fixtures/mockTransport';
import { Type } from '@sinclair/typebox';
import { Type } from 'typebox';

describe('middleware test', () => {
let mockTransportNetwork: ReturnType<typeof createMockTransportNetwork>;
Expand Down
2 changes: 1 addition & 1 deletion __tests__/negative.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
handshakeRequestMessage,
} from '../transport/message';
import { NaiveJsonCodec } from '../codec';
import { Static } from '@sinclair/typebox';
import { Static } from 'typebox';
import { WebSocketClientTransport } from '../transport/impls/ws/client';
import { ProtocolError } from '../transport/events';
import NodeWs from 'ws';
Expand Down
2 changes: 1 addition & 1 deletion __tests__/serialize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
TestServiceSchema,
} from '../testUtil/fixtures/services';
import { serializeSchema } from '../router';
import { Type } from '@sinclair/typebox';
import { Type } from 'typebox';

describe('serialize server to jsonschema', () => {
test('serialize entire service schema', () => {
Expand Down
2 changes: 1 addition & 1 deletion __tests__/typescript-stress.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { assert, describe, expect, test } from 'vitest';
import { Procedure } from '../router/procedures';
import { createServiceSchema } from '../router/services';
import { Type } from '@sinclair/typebox';
import { Type } from 'typebox';
import { createServer } from '../router/server';
import { createClient } from '../router/client';
import {
Expand Down
2 changes: 1 addition & 1 deletion __tests__/unserializable.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, test } from 'vitest';
import { Type } from '@sinclair/typebox';
import { Type } from 'typebox';
import {
Procedure,
createServiceSchema,
Expand Down
2 changes: 1 addition & 1 deletion codec/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Value } from '@sinclair/typebox/value';
import { Value } from 'typebox/value';
import {
OpaqueTransportMessage,
OpaqueTransportMessageSchema,
Expand Down
94 changes: 94 additions & 0 deletions customSchemas/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Type } from 'typebox';

export type TUint8Array = Type.TUnsafe<Uint8Array>;
const uint8ArrayCache = new Map<string, TUint8Array>();

/**
* Creates a TypeBox schema for `Uint8Array` values with optional byte length constraints.
* This replaces the removed `Type.Uint8Array()` from TypeBox 0.34.x.
*
* The schema serializes with `{ type: 'Uint8Array' }` for backwards compatibility
* with older River clients/servers that used the built-in `Type.Uint8Array()`.
*
* @param options - Optional constraints for minimum and maximum byte length.
* @returns A TypeBox schema that validates `Uint8Array` instances.
*/
export function Uint8ArrayType(
options: {
minByteLength?: number;
maxByteLength?: number;
} = {},
) {
const min = options.minByteLength;
const max = options.maxByteLength;

const key = `${min ?? ''}:${max ?? ''}`;
const existing = uint8ArrayCache.get(key);
if (existing) return existing;

const schema = Type.Refine(
Type.Unsafe<Uint8Array>({
type: 'Uint8Array',
...(min !== undefined ? { minByteLength: min } : {}),
...(max !== undefined ? { maxByteLength: max } : {}),
}),
(value): value is Uint8Array => {
if (!(value instanceof Uint8Array)) return false;
if (min !== undefined && value.byteLength < min) return false;
if (max !== undefined && value.byteLength > max) return false;

return true;
},
);

uint8ArrayCache.set(key, schema);

return schema;
}

export type TDate = Type.TUnsafe<Date>;
const dateCache = new Map<string, TDate>();

/**
* Creates a TypeBox schema for `Date` values.
* This replaces the removed `Type.Date()` from TypeBox 0.34.x.
*
* The schema serializes with `{ type: 'Date' }` for backwards compatibility
* with older River clients/servers that used the built-in `Type.Date()`.
*
* @param options - Optional constraints for minimum and maximum date values.
* @returns A TypeBox schema that validates `Date` instances (rejects invalid dates).
*/
export function DateType(
options: {
minimumTimestamp?: number;
maximumTimestamp?: number;
} = {},
): TDate {
const min = options.minimumTimestamp;
const max = options.maximumTimestamp;

const key = `${min ?? ''}:${max ?? ''}`;
const existing = dateCache.get(key);
if (existing) return existing;

const schema = Type.Refine(
Type.Unsafe<Date>({
type: 'Date',
...(min !== undefined ? { minimumTimestamp: min } : {}),
...(max !== undefined ? { maximumTimestamp: max } : {}),
}),
(value): value is Date => {
if (!(value instanceof Date)) return false;
if (isNaN(value.getTime())) return false;
if (typeof min === 'number' && value.getTime() < min) return false;
if (typeof max === 'number' && value.getTime() > max) return false;

return true;
},
);

dateCache.set(key, schema);

return schema;
}
29 changes: 15 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading