@directus/api 11.0.0 → 11.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.
Files changed (57) hide show
  1. package/dist/emitter.d.ts +3 -2
  2. package/dist/emitter.js +12 -4
  3. package/dist/env.js +15 -4
  4. package/dist/messenger.d.ts +3 -3
  5. package/dist/messenger.js +14 -5
  6. package/dist/middleware/authenticate.js +2 -38
  7. package/dist/server.js +10 -0
  8. package/dist/services/graphql/index.d.ts +0 -6
  9. package/dist/services/graphql/index.js +98 -57
  10. package/dist/services/graphql/subscription.d.ts +16 -0
  11. package/dist/services/graphql/subscription.js +77 -0
  12. package/dist/services/server.js +24 -0
  13. package/dist/services/websocket.d.ts +14 -0
  14. package/dist/services/websocket.js +26 -0
  15. package/dist/utils/apply-diff.js +11 -2
  16. package/dist/utils/apply-query.js +5 -6
  17. package/dist/utils/get-accountability-for-token.d.ts +2 -0
  18. package/dist/utils/get-accountability-for-token.js +50 -0
  19. package/dist/utils/get-service.d.ts +7 -0
  20. package/dist/utils/get-service.js +49 -0
  21. package/dist/utils/redact.d.ts +4 -0
  22. package/dist/utils/redact.js +15 -1
  23. package/dist/utils/to-boolean.d.ts +4 -0
  24. package/dist/utils/to-boolean.js +6 -0
  25. package/dist/websocket/authenticate.d.ts +6 -0
  26. package/dist/websocket/authenticate.js +62 -0
  27. package/dist/websocket/controllers/base.d.ts +42 -0
  28. package/dist/websocket/controllers/base.js +276 -0
  29. package/dist/websocket/controllers/graphql.d.ts +12 -0
  30. package/dist/websocket/controllers/graphql.js +102 -0
  31. package/dist/websocket/controllers/hooks.d.ts +1 -0
  32. package/dist/websocket/controllers/hooks.js +122 -0
  33. package/dist/websocket/controllers/index.d.ts +10 -0
  34. package/dist/websocket/controllers/index.js +35 -0
  35. package/dist/websocket/controllers/rest.d.ts +9 -0
  36. package/dist/websocket/controllers/rest.js +47 -0
  37. package/dist/websocket/exceptions.d.ts +16 -0
  38. package/dist/websocket/exceptions.js +55 -0
  39. package/dist/websocket/handlers/heartbeat.d.ts +11 -0
  40. package/dist/websocket/handlers/heartbeat.js +72 -0
  41. package/dist/websocket/handlers/index.d.ts +4 -0
  42. package/dist/websocket/handlers/index.js +11 -0
  43. package/dist/websocket/handlers/items.d.ts +6 -0
  44. package/dist/websocket/handlers/items.js +103 -0
  45. package/dist/websocket/handlers/subscribe.d.ts +43 -0
  46. package/dist/websocket/handlers/subscribe.js +278 -0
  47. package/dist/websocket/messages.d.ts +311 -0
  48. package/dist/websocket/messages.js +96 -0
  49. package/dist/websocket/types.d.ts +34 -0
  50. package/dist/websocket/types.js +1 -0
  51. package/dist/websocket/utils/get-expires-at-for-token.d.ts +1 -0
  52. package/dist/websocket/utils/get-expires-at-for-token.js +8 -0
  53. package/dist/websocket/utils/message.d.ts +4 -0
  54. package/dist/websocket/utils/message.js +27 -0
  55. package/dist/websocket/utils/wait-for-message.d.ts +4 -0
  56. package/dist/websocket/utils/wait-for-message.js +45 -0
  57. package/package.json +19 -14
@@ -0,0 +1,278 @@
1
+ import emitter from '../../emitter.js';
2
+ import { InvalidPayloadException } from '../../index.js';
3
+ import { getMessenger } from '../../messenger.js';
4
+ import { CollectionsService, FieldsService, MetaService } from '../../services/index.js';
5
+ import { getSchema } from '../../utils/get-schema.js';
6
+ import { getService } from '../../utils/get-service.js';
7
+ import { sanitizeQuery } from '../../utils/sanitize-query.js';
8
+ import { refreshAccountability } from '../authenticate.js';
9
+ import { WebSocketException, handleWebSocketException } from '../exceptions.js';
10
+ import { WebSocketSubscribeMessage } from '../messages.js';
11
+ import { fmtMessage, getMessageType } from '../utils/message.js';
12
+ /**
13
+ * Handler responsible for subscriptions
14
+ */
15
+ export class SubscribeHandler {
16
+ // storage of subscriptions per collection
17
+ subscriptions;
18
+ // internal message bus
19
+ messenger;
20
+ /**
21
+ * Initialize the handler
22
+ */
23
+ constructor() {
24
+ this.subscriptions = {};
25
+ this.messenger = getMessenger();
26
+ this.bindWebSocket();
27
+ // listen to the Redis pub/sub and dispatch
28
+ this.messenger.subscribe('websocket.event', (message) => {
29
+ try {
30
+ this.dispatch(message);
31
+ }
32
+ catch {
33
+ // don't error on an invalid event from the messenger
34
+ }
35
+ });
36
+ }
37
+ /**
38
+ * Hook into websocket client lifecycle events
39
+ */
40
+ bindWebSocket() {
41
+ // listen to incoming messages on the connected websockets
42
+ emitter.onAction('websocket.message', ({ client, message }) => {
43
+ if (!['subscribe', 'unsubscribe'].includes(getMessageType(message)))
44
+ return;
45
+ try {
46
+ this.onMessage(client, WebSocketSubscribeMessage.parse(message));
47
+ }
48
+ catch (error) {
49
+ handleWebSocketException(client, error, 'subscribe');
50
+ }
51
+ });
52
+ // unsubscribe when a connection drops
53
+ emitter.onAction('websocket.error', ({ client }) => this.unsubscribe(client));
54
+ emitter.onAction('websocket.close', ({ client }) => this.unsubscribe(client));
55
+ }
56
+ /**
57
+ * Register a subscription
58
+ * @param subscription
59
+ */
60
+ subscribe(subscription) {
61
+ const { collection } = subscription;
62
+ if ('item' in subscription && ['directus_fields', 'directus_relations'].includes(collection)) {
63
+ throw new InvalidPayloadException(`Cannot subscribe to a specific item in the ${collection} collection.`);
64
+ }
65
+ if (!this.subscriptions[collection]) {
66
+ this.subscriptions[collection] = new Set();
67
+ }
68
+ this.subscriptions[collection]?.add(subscription);
69
+ }
70
+ /**
71
+ * Remove a subscription
72
+ * @param subscription
73
+ */
74
+ unsubscribe(client, uid) {
75
+ if (uid !== undefined) {
76
+ const subscription = this.getSubscription(client, String(uid));
77
+ if (subscription) {
78
+ this.subscriptions[subscription.collection]?.delete(subscription);
79
+ }
80
+ }
81
+ else {
82
+ for (const key of Object.keys(this.subscriptions)) {
83
+ const subscriptions = Array.from(this.subscriptions[key] || []);
84
+ for (let i = subscriptions.length - 1; i >= 0; i--) {
85
+ const subscription = subscriptions[i];
86
+ if (!subscription)
87
+ continue;
88
+ if (subscription.client === client && (!uid || subscription.uid === uid)) {
89
+ this.subscriptions[key]?.delete(subscription);
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ /**
96
+ * Dispatch event to subscriptions
97
+ */
98
+ async dispatch(event) {
99
+ const subscriptions = this.subscriptions[event.collection];
100
+ if (!subscriptions || subscriptions.size === 0)
101
+ return;
102
+ const schema = await getSchema();
103
+ for (const subscription of subscriptions) {
104
+ const { client } = subscription;
105
+ if (subscription.event !== undefined && event.action !== subscription.event) {
106
+ continue; // skip filtered events
107
+ }
108
+ try {
109
+ client.accountability = await refreshAccountability(client.accountability);
110
+ const result = 'item' in subscription
111
+ ? await this.getSinglePayload(subscription, client.accountability, schema, event)
112
+ : await this.getMultiPayload(subscription, client.accountability, schema, event);
113
+ if (Array.isArray(result?.['data']) && result?.['data']?.length === 0)
114
+ return;
115
+ client.send(fmtMessage('subscription', result, subscription.uid));
116
+ }
117
+ catch (err) {
118
+ handleWebSocketException(client, err, 'subscribe');
119
+ }
120
+ }
121
+ }
122
+ /**
123
+ * Handle incoming (un)subscribe requests
124
+ */
125
+ async onMessage(client, message) {
126
+ if (getMessageType(message) === 'subscribe') {
127
+ try {
128
+ const collection = String(message.collection);
129
+ const accountability = client.accountability;
130
+ const schema = await getSchema();
131
+ if (!accountability?.admin && !schema.collections[collection]) {
132
+ throw new WebSocketException('subscribe', 'INVALID_COLLECTION', 'The provided collection does not exists or is not accessible.', message.uid);
133
+ }
134
+ const subscription = {
135
+ client,
136
+ collection,
137
+ };
138
+ if ('event' in message) {
139
+ subscription.event = message.event;
140
+ }
141
+ if ('query' in message) {
142
+ subscription.query = sanitizeQuery(message.query, accountability);
143
+ }
144
+ if ('item' in message)
145
+ subscription.item = String(message.item);
146
+ if ('uid' in message) {
147
+ subscription.uid = String(message.uid);
148
+ // remove the subscription if it already exists
149
+ this.unsubscribe(client, subscription.uid);
150
+ }
151
+ let data;
152
+ if (subscription.event === undefined) {
153
+ data =
154
+ 'item' in subscription
155
+ ? await this.getSinglePayload(subscription, accountability, schema)
156
+ : await this.getMultiPayload(subscription, accountability, schema);
157
+ }
158
+ else {
159
+ data = { event: 'init' };
160
+ }
161
+ // if no errors were thrown register the subscription
162
+ this.subscribe(subscription);
163
+ // send an initial response
164
+ client.send(fmtMessage('subscription', data, subscription.uid));
165
+ }
166
+ catch (err) {
167
+ handleWebSocketException(client, err, 'subscribe');
168
+ }
169
+ }
170
+ if (getMessageType(message) === 'unsubscribe') {
171
+ try {
172
+ this.unsubscribe(client, message.uid);
173
+ client.send(fmtMessage('subscription', { event: 'unsubscribe' }, message.uid));
174
+ }
175
+ catch (err) {
176
+ handleWebSocketException(client, err, 'unsubscribe');
177
+ }
178
+ }
179
+ }
180
+ async getSinglePayload(subscription, accountability, schema, event) {
181
+ const metaService = new MetaService({ schema, accountability });
182
+ const query = subscription.query ?? {};
183
+ const id = subscription.item;
184
+ const result = {
185
+ event: event?.action ?? 'init',
186
+ };
187
+ if (subscription.collection === 'directus_collections') {
188
+ const service = new CollectionsService({ schema, accountability });
189
+ result['data'] = await service.readOne(String(id));
190
+ }
191
+ else {
192
+ const service = getService(subscription.collection, { schema, accountability });
193
+ result['data'] = await service.readOne(id, query);
194
+ }
195
+ if ('meta' in query) {
196
+ result['meta'] = await metaService.getMetaForQuery(subscription.collection, query);
197
+ }
198
+ return result;
199
+ }
200
+ async getMultiPayload(subscription, accountability, schema, event) {
201
+ const metaService = new MetaService({ schema, accountability });
202
+ const result = {
203
+ event: event?.action ?? 'init',
204
+ };
205
+ switch (subscription.collection) {
206
+ case 'directus_collections':
207
+ result['data'] = await this.getCollectionPayload(accountability, schema, event);
208
+ break;
209
+ case 'directus_fields':
210
+ result['data'] = await this.getFieldsPayload(accountability, schema, event);
211
+ break;
212
+ case 'directus_relations':
213
+ result['data'] = event?.payload;
214
+ break;
215
+ default:
216
+ result['data'] = await this.getItemsPayload(subscription, accountability, schema, event);
217
+ break;
218
+ }
219
+ const query = subscription.query ?? {};
220
+ if ('meta' in query) {
221
+ result['meta'] = await metaService.getMetaForQuery(subscription.collection, query);
222
+ }
223
+ return result;
224
+ }
225
+ async getCollectionPayload(accountability, schema, event) {
226
+ const service = new CollectionsService({ schema, accountability });
227
+ if (!event?.action) {
228
+ return await service.readByQuery();
229
+ }
230
+ else if (event.action === 'create') {
231
+ return await service.readMany([String(event.key)]);
232
+ }
233
+ else if (event.action === 'delete') {
234
+ return event.keys;
235
+ }
236
+ else {
237
+ return await service.readMany(event.keys.map((key) => String(key)));
238
+ }
239
+ }
240
+ async getFieldsPayload(accountability, schema, event) {
241
+ const service = new FieldsService({ schema, accountability });
242
+ if (!event?.action) {
243
+ return await service.readAll();
244
+ }
245
+ else if (event.action === 'delete') {
246
+ return event.keys;
247
+ }
248
+ else {
249
+ return await service.readOne(event.payload?.['collection'], event.payload?.['field']);
250
+ }
251
+ }
252
+ async getItemsPayload(subscription, accountability, schema, event) {
253
+ const query = subscription.query ?? {};
254
+ const service = getService(subscription.collection, { schema, accountability });
255
+ if (!event?.action) {
256
+ return await service.readByQuery(query);
257
+ }
258
+ else if (event.action === 'create') {
259
+ return await service.readMany([event.key], query);
260
+ }
261
+ else if (event.action === 'delete') {
262
+ return event.keys;
263
+ }
264
+ else {
265
+ return await service.readMany(event.keys, query);
266
+ }
267
+ }
268
+ getSubscription(client, uid) {
269
+ for (const userSubscriptions of Object.values(this.subscriptions)) {
270
+ for (const subscription of userSubscriptions) {
271
+ if (subscription.client === client && subscription.uid === uid) {
272
+ return subscription;
273
+ }
274
+ }
275
+ }
276
+ return undefined;
277
+ }
278
+ }
@@ -0,0 +1,311 @@
1
+ import type { Item, Query } from '@directus/types';
2
+ import { z } from 'zod';
3
+ export declare const WebSocketMessage: z.ZodObject<{
4
+ type: z.ZodString;
5
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
6
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
7
+ type: z.ZodString;
8
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
9
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
10
+ type: z.ZodString;
11
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
12
+ }, z.ZodTypeAny, "passthrough">>;
13
+ export type WebSocketMessage = z.infer<typeof WebSocketMessage>;
14
+ export declare const WebSocketResponse: z.ZodDiscriminatedUnion<"status", [z.ZodObject<{
15
+ type: z.ZodString;
16
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
17
+ status: z.ZodLiteral<"ok">;
18
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
19
+ type: z.ZodString;
20
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
21
+ status: z.ZodLiteral<"ok">;
22
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
23
+ type: z.ZodString;
24
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
25
+ status: z.ZodLiteral<"ok">;
26
+ }, z.ZodTypeAny, "passthrough">>, z.ZodObject<{
27
+ type: z.ZodString;
28
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
29
+ status: z.ZodLiteral<"error">;
30
+ error: z.ZodObject<{
31
+ code: z.ZodString;
32
+ message: z.ZodString;
33
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
34
+ code: z.ZodString;
35
+ message: z.ZodString;
36
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
37
+ code: z.ZodString;
38
+ message: z.ZodString;
39
+ }, z.ZodTypeAny, "passthrough">>;
40
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
41
+ type: z.ZodString;
42
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
43
+ status: z.ZodLiteral<"error">;
44
+ error: z.ZodObject<{
45
+ code: z.ZodString;
46
+ message: z.ZodString;
47
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
48
+ code: z.ZodString;
49
+ message: z.ZodString;
50
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
51
+ code: z.ZodString;
52
+ message: z.ZodString;
53
+ }, z.ZodTypeAny, "passthrough">>;
54
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
55
+ type: z.ZodString;
56
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
57
+ status: z.ZodLiteral<"error">;
58
+ error: z.ZodObject<{
59
+ code: z.ZodString;
60
+ message: z.ZodString;
61
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
62
+ code: z.ZodString;
63
+ message: z.ZodString;
64
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
65
+ code: z.ZodString;
66
+ message: z.ZodString;
67
+ }, z.ZodTypeAny, "passthrough">>;
68
+ }, z.ZodTypeAny, "passthrough">>]>;
69
+ export type WebSocketResponse = z.infer<typeof WebSocketResponse>;
70
+ export declare const ConnectionParams: z.ZodObject<{
71
+ access_token: z.ZodOptional<z.ZodString>;
72
+ }, "strip", z.ZodTypeAny, {
73
+ access_token?: string | undefined;
74
+ }, {
75
+ access_token?: string | undefined;
76
+ }>;
77
+ export type ConnectionParams = z.infer<typeof ConnectionParams>;
78
+ export declare const BasicAuthMessage: z.ZodUnion<[z.ZodObject<{
79
+ email: z.ZodString;
80
+ password: z.ZodString;
81
+ }, "strip", z.ZodTypeAny, {
82
+ email: string;
83
+ password: string;
84
+ }, {
85
+ email: string;
86
+ password: string;
87
+ }>, z.ZodObject<{
88
+ access_token: z.ZodString;
89
+ }, "strip", z.ZodTypeAny, {
90
+ access_token: string;
91
+ }, {
92
+ access_token: string;
93
+ }>, z.ZodObject<{
94
+ refresh_token: z.ZodString;
95
+ }, "strip", z.ZodTypeAny, {
96
+ refresh_token: string;
97
+ }, {
98
+ refresh_token: string;
99
+ }>]>;
100
+ export type BasicAuthMessage = z.infer<typeof BasicAuthMessage>;
101
+ export declare const WebSocketAuthMessage: z.ZodIntersection<z.ZodObject<{
102
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
103
+ type: z.ZodLiteral<"auth">;
104
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
105
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
106
+ type: z.ZodLiteral<"auth">;
107
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
108
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
109
+ type: z.ZodLiteral<"auth">;
110
+ }, z.ZodTypeAny, "passthrough">>, z.ZodUnion<[z.ZodObject<{
111
+ email: z.ZodString;
112
+ password: z.ZodString;
113
+ }, "strip", z.ZodTypeAny, {
114
+ email: string;
115
+ password: string;
116
+ }, {
117
+ email: string;
118
+ password: string;
119
+ }>, z.ZodObject<{
120
+ access_token: z.ZodString;
121
+ }, "strip", z.ZodTypeAny, {
122
+ access_token: string;
123
+ }, {
124
+ access_token: string;
125
+ }>, z.ZodObject<{
126
+ refresh_token: z.ZodString;
127
+ }, "strip", z.ZodTypeAny, {
128
+ refresh_token: string;
129
+ }, {
130
+ refresh_token: string;
131
+ }>]>>;
132
+ export type WebSocketAuthMessage = z.infer<typeof WebSocketAuthMessage>;
133
+ export declare const WebSocketSubscribeMessage: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
134
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
135
+ type: z.ZodLiteral<"subscribe">;
136
+ collection: z.ZodString;
137
+ event: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"create">, z.ZodLiteral<"update">, z.ZodLiteral<"delete">]>>;
138
+ item: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
139
+ query: z.ZodOptional<z.ZodType<Query, z.ZodTypeDef, Query>>;
140
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
141
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
142
+ type: z.ZodLiteral<"subscribe">;
143
+ collection: z.ZodString;
144
+ event: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"create">, z.ZodLiteral<"update">, z.ZodLiteral<"delete">]>>;
145
+ item: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
146
+ query: z.ZodOptional<z.ZodType<Query, z.ZodTypeDef, Query>>;
147
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
148
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
149
+ type: z.ZodLiteral<"subscribe">;
150
+ collection: z.ZodString;
151
+ event: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"create">, z.ZodLiteral<"update">, z.ZodLiteral<"delete">]>>;
152
+ item: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
153
+ query: z.ZodOptional<z.ZodType<Query, z.ZodTypeDef, Query>>;
154
+ }, z.ZodTypeAny, "passthrough">>, z.ZodObject<{
155
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
156
+ type: z.ZodLiteral<"unsubscribe">;
157
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
158
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
159
+ type: z.ZodLiteral<"unsubscribe">;
160
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
161
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
162
+ type: z.ZodLiteral<"unsubscribe">;
163
+ }, z.ZodTypeAny, "passthrough">>]>;
164
+ export type WebSocketSubscribeMessage = z.infer<typeof WebSocketSubscribeMessage>;
165
+ export declare const WebSocketItemsMessage: z.ZodUnion<[z.ZodObject<{
166
+ type: z.ZodLiteral<"items">;
167
+ collection: z.ZodString;
168
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
169
+ action: z.ZodLiteral<"create">;
170
+ data: z.ZodUnion<[z.ZodArray<z.ZodType<Partial<Item>, z.ZodTypeDef, Partial<Item>>, "many">, z.ZodType<Partial<Item>, z.ZodTypeDef, Partial<Item>>]>;
171
+ query: z.ZodOptional<z.ZodType<Query, z.ZodTypeDef, Query>>;
172
+ }, "strip", z.ZodTypeAny, {
173
+ type: "items";
174
+ action: "create";
175
+ data: (Partial<Item> | Partial<Item>[]) & (Partial<Item> | Partial<Item>[] | undefined);
176
+ collection: string;
177
+ uid?: string | number | undefined;
178
+ query?: Query | undefined;
179
+ }, {
180
+ type: "items";
181
+ action: "create";
182
+ data: (Partial<Item> | Partial<Item>[]) & (Partial<Item> | Partial<Item>[] | undefined);
183
+ collection: string;
184
+ uid?: string | number | undefined;
185
+ query?: Query | undefined;
186
+ }>, z.ZodObject<{
187
+ type: z.ZodLiteral<"items">;
188
+ collection: z.ZodString;
189
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
190
+ action: z.ZodLiteral<"read">;
191
+ ids: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">>;
192
+ id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
193
+ query: z.ZodOptional<z.ZodType<Query, z.ZodTypeDef, Query>>;
194
+ }, "strip", z.ZodTypeAny, {
195
+ type: "items";
196
+ action: "read";
197
+ collection: string;
198
+ uid?: string | number | undefined;
199
+ ids?: (string | number)[] | undefined;
200
+ id?: string | number | undefined;
201
+ query?: Query | undefined;
202
+ }, {
203
+ type: "items";
204
+ action: "read";
205
+ collection: string;
206
+ uid?: string | number | undefined;
207
+ ids?: (string | number)[] | undefined;
208
+ id?: string | number | undefined;
209
+ query?: Query | undefined;
210
+ }>, z.ZodObject<{
211
+ type: z.ZodLiteral<"items">;
212
+ collection: z.ZodString;
213
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
214
+ action: z.ZodLiteral<"update">;
215
+ data: z.ZodType<Partial<Item>, z.ZodTypeDef, Partial<Item>>;
216
+ ids: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">>;
217
+ id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
218
+ query: z.ZodOptional<z.ZodType<Query, z.ZodTypeDef, Query>>;
219
+ }, "strip", z.ZodTypeAny, {
220
+ type: "items";
221
+ action: "update";
222
+ data: Partial<Item>;
223
+ collection: string;
224
+ uid?: string | number | undefined;
225
+ ids?: (string | number)[] | undefined;
226
+ id?: string | number | undefined;
227
+ query?: Query | undefined;
228
+ }, {
229
+ type: "items";
230
+ action: "update";
231
+ data: Partial<Item>;
232
+ collection: string;
233
+ uid?: string | number | undefined;
234
+ ids?: (string | number)[] | undefined;
235
+ id?: string | number | undefined;
236
+ query?: Query | undefined;
237
+ }>, z.ZodObject<{
238
+ type: z.ZodLiteral<"items">;
239
+ collection: z.ZodString;
240
+ uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
241
+ action: z.ZodLiteral<"delete">;
242
+ ids: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">>;
243
+ id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
244
+ query: z.ZodOptional<z.ZodType<Query, z.ZodTypeDef, Query>>;
245
+ }, "strip", z.ZodTypeAny, {
246
+ type: "items";
247
+ action: "delete";
248
+ collection: string;
249
+ uid?: string | number | undefined;
250
+ ids?: (string | number)[] | undefined;
251
+ id?: string | number | undefined;
252
+ query?: Query | undefined;
253
+ }, {
254
+ type: "items";
255
+ action: "delete";
256
+ collection: string;
257
+ uid?: string | number | undefined;
258
+ ids?: (string | number)[] | undefined;
259
+ id?: string | number | undefined;
260
+ query?: Query | undefined;
261
+ }>]>;
262
+ export type WebSocketItemsMessage = z.infer<typeof WebSocketItemsMessage>;
263
+ export declare const WebSocketEvent: z.ZodDiscriminatedUnion<"action", [z.ZodObject<{
264
+ action: z.ZodLiteral<"create">;
265
+ collection: z.ZodString;
266
+ payload: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
267
+ key: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
268
+ }, "strip", z.ZodTypeAny, {
269
+ key: string | number;
270
+ action: "create";
271
+ collection: string;
272
+ payload?: Record<string, any> | undefined;
273
+ }, {
274
+ key: string | number;
275
+ action: "create";
276
+ collection: string;
277
+ payload?: Record<string, any> | undefined;
278
+ }>, z.ZodObject<{
279
+ action: z.ZodLiteral<"update">;
280
+ collection: z.ZodString;
281
+ payload: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
282
+ keys: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">;
283
+ }, "strip", z.ZodTypeAny, {
284
+ action: "update";
285
+ keys: (string | number)[];
286
+ collection: string;
287
+ payload?: Record<string, any> | undefined;
288
+ }, {
289
+ action: "update";
290
+ keys: (string | number)[];
291
+ collection: string;
292
+ payload?: Record<string, any> | undefined;
293
+ }>, z.ZodObject<{
294
+ action: z.ZodLiteral<"delete">;
295
+ collection: z.ZodString;
296
+ payload: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
297
+ keys: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">;
298
+ }, "strip", z.ZodTypeAny, {
299
+ action: "delete";
300
+ keys: (string | number)[];
301
+ collection: string;
302
+ payload?: Record<string, any> | undefined;
303
+ }, {
304
+ action: "delete";
305
+ keys: (string | number)[];
306
+ collection: string;
307
+ payload?: Record<string, any> | undefined;
308
+ }>]>;
309
+ export type WebSocketEvent = z.infer<typeof WebSocketEvent>;
310
+ export declare const AuthMode: z.ZodUnion<[z.ZodLiteral<"public">, z.ZodLiteral<"handshake">, z.ZodLiteral<"strict">]>;
311
+ export type AuthMode = z.infer<typeof AuthMode>;
@@ -0,0 +1,96 @@
1
+ import { z } from 'zod';
2
+ const zodStringOrNumber = z.union([z.string(), z.number()]);
3
+ export const WebSocketMessage = z
4
+ .object({
5
+ type: z.string(),
6
+ uid: zodStringOrNumber.optional(),
7
+ })
8
+ .passthrough();
9
+ export const WebSocketResponse = z.discriminatedUnion('status', [
10
+ WebSocketMessage.extend({
11
+ status: z.literal('ok'),
12
+ }),
13
+ WebSocketMessage.extend({
14
+ status: z.literal('error'),
15
+ error: z
16
+ .object({
17
+ code: z.string(),
18
+ message: z.string(),
19
+ })
20
+ .passthrough(),
21
+ }),
22
+ ]);
23
+ export const ConnectionParams = z.object({ access_token: z.string().optional() });
24
+ export const BasicAuthMessage = z.union([
25
+ z.object({ email: z.string().email(), password: z.string() }),
26
+ z.object({ access_token: z.string() }),
27
+ z.object({ refresh_token: z.string() }),
28
+ ]);
29
+ export const WebSocketAuthMessage = WebSocketMessage.extend({
30
+ type: z.literal('auth'),
31
+ }).and(BasicAuthMessage);
32
+ export const WebSocketSubscribeMessage = z.discriminatedUnion('type', [
33
+ WebSocketMessage.extend({
34
+ type: z.literal('subscribe'),
35
+ collection: z.string(),
36
+ event: z.union([z.literal('create'), z.literal('update'), z.literal('delete')]).optional(),
37
+ item: zodStringOrNumber.optional(),
38
+ query: z.custom().optional(),
39
+ }),
40
+ WebSocketMessage.extend({
41
+ type: z.literal('unsubscribe'),
42
+ }),
43
+ ]);
44
+ const ZodItem = z.custom();
45
+ const PartialItemsMessage = z.object({
46
+ uid: zodStringOrNumber.optional(),
47
+ type: z.literal('items'),
48
+ collection: z.string(),
49
+ });
50
+ export const WebSocketItemsMessage = z.union([
51
+ PartialItemsMessage.extend({
52
+ action: z.literal('create'),
53
+ data: z.union([z.array(ZodItem), ZodItem]),
54
+ query: z.custom().optional(),
55
+ }),
56
+ PartialItemsMessage.extend({
57
+ action: z.literal('read'),
58
+ ids: z.array(zodStringOrNumber).optional(),
59
+ id: zodStringOrNumber.optional(),
60
+ query: z.custom().optional(),
61
+ }),
62
+ PartialItemsMessage.extend({
63
+ action: z.literal('update'),
64
+ data: ZodItem,
65
+ ids: z.array(zodStringOrNumber).optional(),
66
+ id: zodStringOrNumber.optional(),
67
+ query: z.custom().optional(),
68
+ }),
69
+ PartialItemsMessage.extend({
70
+ action: z.literal('delete'),
71
+ ids: z.array(zodStringOrNumber).optional(),
72
+ id: zodStringOrNumber.optional(),
73
+ query: z.custom().optional(),
74
+ }),
75
+ ]);
76
+ export const WebSocketEvent = z.discriminatedUnion('action', [
77
+ z.object({
78
+ action: z.literal('create'),
79
+ collection: z.string(),
80
+ payload: z.record(z.any()).optional(),
81
+ key: zodStringOrNumber,
82
+ }),
83
+ z.object({
84
+ action: z.literal('update'),
85
+ collection: z.string(),
86
+ payload: z.record(z.any()).optional(),
87
+ keys: z.array(zodStringOrNumber),
88
+ }),
89
+ z.object({
90
+ action: z.literal('delete'),
91
+ collection: z.string(),
92
+ payload: z.record(z.any()).optional(),
93
+ keys: z.array(zodStringOrNumber),
94
+ }),
95
+ ]);
96
+ export const AuthMode = z.union([z.literal('public'), z.literal('handshake'), z.literal('strict')]);