@livestore/sync-cf 0.4.0-dev.9 → 0.5.0-dev.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 (113) hide show
  1. package/README.md +7 -8
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/cf-worker/do/durable-object.d.ts +1 -1
  4. package/dist/cf-worker/do/durable-object.d.ts.map +1 -1
  5. package/dist/cf-worker/do/durable-object.js +36 -18
  6. package/dist/cf-worker/do/durable-object.js.map +1 -1
  7. package/dist/cf-worker/do/layer.d.ts +24 -20
  8. package/dist/cf-worker/do/layer.d.ts.map +1 -1
  9. package/dist/cf-worker/do/layer.js +57 -24
  10. package/dist/cf-worker/do/layer.js.map +1 -1
  11. package/dist/cf-worker/do/pull.d.ts +8 -3
  12. package/dist/cf-worker/do/pull.d.ts.map +1 -1
  13. package/dist/cf-worker/do/pull.js +26 -21
  14. package/dist/cf-worker/do/pull.js.map +1 -1
  15. package/dist/cf-worker/do/push.d.ts +20 -6
  16. package/dist/cf-worker/do/push.d.ts.map +1 -1
  17. package/dist/cf-worker/do/push.js +84 -56
  18. package/dist/cf-worker/do/push.js.map +1 -1
  19. package/dist/cf-worker/do/push.test.d.ts +2 -0
  20. package/dist/cf-worker/do/push.test.d.ts.map +1 -0
  21. package/dist/cf-worker/do/push.test.js +30 -0
  22. package/dist/cf-worker/do/push.test.js.map +1 -0
  23. package/dist/cf-worker/do/sqlite.d.ts +49 -52
  24. package/dist/cf-worker/do/sqlite.d.ts.map +1 -1
  25. package/dist/cf-worker/do/sqlite.js +14 -5
  26. package/dist/cf-worker/do/sqlite.js.map +1 -1
  27. package/dist/cf-worker/do/sync-storage.d.ts +15 -9
  28. package/dist/cf-worker/do/sync-storage.d.ts.map +1 -1
  29. package/dist/cf-worker/do/sync-storage.js +111 -25
  30. package/dist/cf-worker/do/sync-storage.js.map +1 -1
  31. package/dist/cf-worker/do/transport/do-rpc-server.d.ts +2 -2
  32. package/dist/cf-worker/do/transport/do-rpc-server.d.ts.map +1 -1
  33. package/dist/cf-worker/do/transport/do-rpc-server.js +31 -23
  34. package/dist/cf-worker/do/transport/do-rpc-server.js.map +1 -1
  35. package/dist/cf-worker/do/transport/http-rpc-server.d.ts +5 -3
  36. package/dist/cf-worker/do/transport/http-rpc-server.d.ts.map +1 -1
  37. package/dist/cf-worker/do/transport/http-rpc-server.js +28 -19
  38. package/dist/cf-worker/do/transport/http-rpc-server.js.map +1 -1
  39. package/dist/cf-worker/do/transport/ws-rpc-server.d.ts +3 -2
  40. package/dist/cf-worker/do/transport/ws-rpc-server.d.ts.map +1 -1
  41. package/dist/cf-worker/do/transport/ws-rpc-server.js +32 -10
  42. package/dist/cf-worker/do/transport/ws-rpc-server.js.map +1 -1
  43. package/dist/cf-worker/shared.d.ts +124 -34
  44. package/dist/cf-worker/shared.d.ts.map +1 -1
  45. package/dist/cf-worker/shared.js +52 -17
  46. package/dist/cf-worker/shared.js.map +1 -1
  47. package/dist/cf-worker/worker.d.ts +47 -39
  48. package/dist/cf-worker/worker.d.ts.map +1 -1
  49. package/dist/cf-worker/worker.js +54 -35
  50. package/dist/cf-worker/worker.js.map +1 -1
  51. package/dist/client/transport/do-rpc-client.d.ts +13 -4
  52. package/dist/client/transport/do-rpc-client.d.ts.map +1 -1
  53. package/dist/client/transport/do-rpc-client.js +74 -28
  54. package/dist/client/transport/do-rpc-client.js.map +1 -1
  55. package/dist/client/transport/http-rpc-client.d.ts +3 -3
  56. package/dist/client/transport/http-rpc-client.d.ts.map +1 -1
  57. package/dist/client/transport/http-rpc-client.js +51 -22
  58. package/dist/client/transport/http-rpc-client.js.map +1 -1
  59. package/dist/client/transport/ws-rpc-client.d.ts +4 -3
  60. package/dist/client/transport/ws-rpc-client.d.ts.map +1 -1
  61. package/dist/client/transport/ws-rpc-client.js +40 -22
  62. package/dist/client/transport/ws-rpc-client.js.map +1 -1
  63. package/dist/common/constants.d.ts +7 -0
  64. package/dist/common/constants.d.ts.map +1 -0
  65. package/dist/common/constants.js +17 -0
  66. package/dist/common/constants.js.map +1 -0
  67. package/dist/common/do-rpc-schema.d.ts +42 -43
  68. package/dist/common/do-rpc-schema.d.ts.map +1 -1
  69. package/dist/common/do-rpc-schema.js +5 -5
  70. package/dist/common/do-rpc-schema.js.map +1 -1
  71. package/dist/common/http-rpc-schema.d.ts +37 -38
  72. package/dist/common/http-rpc-schema.d.ts.map +1 -1
  73. package/dist/common/http-rpc-schema.js +7 -7
  74. package/dist/common/http-rpc-schema.js.map +1 -1
  75. package/dist/common/mod.d.ts +5 -3
  76. package/dist/common/mod.d.ts.map +1 -1
  77. package/dist/common/mod.js +4 -2
  78. package/dist/common/mod.js.map +1 -1
  79. package/dist/common/sync-message-types.d.ts +113 -130
  80. package/dist/common/sync-message-types.d.ts.map +1 -1
  81. package/dist/common/sync-message-types.js +29 -17
  82. package/dist/common/sync-message-types.js.map +1 -1
  83. package/dist/common/ws-rpc-schema.d.ts +34 -35
  84. package/dist/common/ws-rpc-schema.d.ts.map +1 -1
  85. package/dist/common/ws-rpc-schema.js +5 -5
  86. package/dist/common/ws-rpc-schema.js.map +1 -1
  87. package/package.json +57 -15
  88. package/src/cf-worker/do/durable-object.ts +59 -41
  89. package/src/cf-worker/do/layer.ts +153 -88
  90. package/src/cf-worker/do/pull.ts +48 -31
  91. package/src/cf-worker/do/push.test.ts +40 -0
  92. package/src/cf-worker/do/push.ts +163 -125
  93. package/src/cf-worker/do/sqlite.ts +15 -5
  94. package/src/cf-worker/do/sync-storage.ts +171 -38
  95. package/src/cf-worker/do/transport/do-rpc-server.ts +40 -40
  96. package/src/cf-worker/do/transport/http-rpc-server.ts +37 -20
  97. package/src/cf-worker/do/transport/ws-rpc-server.ts +44 -16
  98. package/src/cf-worker/shared.ts +152 -36
  99. package/src/cf-worker/worker.ts +110 -58
  100. package/src/client/transport/do-rpc-client.ts +106 -40
  101. package/src/client/transport/http-rpc-client.ts +74 -38
  102. package/src/client/transport/ws-rpc-client.ts +55 -29
  103. package/src/common/constants.ts +18 -0
  104. package/src/common/do-rpc-schema.ts +6 -5
  105. package/src/common/http-rpc-schema.ts +8 -7
  106. package/src/common/mod.ts +4 -3
  107. package/src/common/sync-message-types.ts +19 -19
  108. package/src/common/ws-rpc-schema.ts +6 -5
  109. package/dist/cf-worker/do/ws-chunking.d.ts +0 -22
  110. package/dist/cf-worker/do/ws-chunking.d.ts.map +0 -1
  111. package/dist/cf-worker/do/ws-chunking.js +0 -49
  112. package/dist/cf-worker/do/ws-chunking.js.map +0 -1
  113. package/src/cf-worker/do/ws-chunking.ts +0 -76
@@ -1,16 +1,17 @@
1
- import { InvalidPullError, InvalidPushError } from '@livestore/common'
1
+ import { BackendIdMismatchError, ServerAheadError, UnknownError } from '@livestore/common'
2
2
  import { Rpc, RpcGroup, Schema } from '@livestore/utils/effect'
3
+
3
4
  import * as SyncMessage from './sync-message-types.ts'
4
5
 
5
6
  const commonPayloadFields = {
6
7
  /**
7
- * While the storeId is already implied by the durable object, we still need the explicit storeId
8
+ * While the storeId is already implied by the Durable Object, we still need the explicit storeId
8
9
  * since a DO doesn't know its own id.name value. 🫠
9
10
  * https://community.cloudflare.com/t/how-can-i-get-the-name-of-a-durable-object-from-itself/505961/8
10
11
  */
11
12
  storeId: Schema.String,
12
13
  /** Needed for various reasons (e.g. auth) */
13
- payload: Schema.optional(Schema.JsonValue),
14
+ payload: Schema.optional(Schema.Json),
14
15
  }
15
16
 
16
17
  export class SyncDoRpc extends RpcGroup.make(
@@ -34,7 +35,7 @@ export class SyncDoRpc extends RpcGroup.make(
34
35
  rpcRequestId: Schema.String,
35
36
  ...SyncMessage.PullResponse.fields,
36
37
  }),
37
- error: InvalidPullError,
38
+ error: Schema.Union([UnknownError, BackendIdMismatchError]),
38
39
  stream: true,
39
40
  }),
40
41
  Rpc.make('SyncDoRpc.Push', {
@@ -43,7 +44,7 @@ export class SyncDoRpc extends RpcGroup.make(
43
44
  ...commonPayloadFields,
44
45
  },
45
46
  success: SyncMessage.PushAck,
46
- error: InvalidPushError,
47
+ error: Schema.Union([UnknownError, ServerAheadError, BackendIdMismatchError]),
47
48
  }),
48
49
  Rpc.make('SyncDoRpc.Ping', {
49
50
  payload: {
@@ -1,5 +1,6 @@
1
- import { InvalidPullError, InvalidPushError, UnexpectedError } from '@livestore/common'
1
+ import { BackendIdMismatchError, ServerAheadError, UnknownError } from '@livestore/common'
2
2
  import { Rpc, RpcGroup, Schema } from '@livestore/utils/effect'
3
+
3
4
  import * as SyncMessage from './sync-message-types.ts'
4
5
 
5
6
  /**
@@ -13,28 +14,28 @@ export class SyncHttpRpc extends RpcGroup.make(
13
14
  Rpc.make('SyncHttpRpc.Pull', {
14
15
  payload: Schema.Struct({
15
16
  storeId: Schema.String,
16
- payload: Schema.optional(Schema.JsonValue),
17
+ payload: Schema.optional(Schema.Json),
17
18
  ...SyncMessage.PullRequest.fields,
18
19
  }),
19
20
  success: SyncMessage.PullResponse,
20
- error: InvalidPullError,
21
+ error: Schema.Union([UnknownError, BackendIdMismatchError]),
21
22
  stream: true,
22
23
  }),
23
24
  Rpc.make('SyncHttpRpc.Push', {
24
25
  payload: Schema.Struct({
25
26
  storeId: Schema.String,
26
- payload: Schema.optional(Schema.JsonValue),
27
+ payload: Schema.optional(Schema.Json),
27
28
  ...SyncMessage.PushRequest.fields,
28
29
  }),
29
30
  success: SyncMessage.PushAck,
30
- error: InvalidPushError,
31
+ error: Schema.Union([UnknownError, ServerAheadError, BackendIdMismatchError]),
31
32
  }),
32
33
  Rpc.make('SyncHttpRpc.Ping', {
33
34
  payload: Schema.Struct({
34
35
  storeId: Schema.String,
35
- payload: Schema.optional(Schema.JsonValue),
36
+ payload: Schema.optional(Schema.Json),
36
37
  }),
37
38
  success: SyncMessage.Pong,
38
- error: UnexpectedError,
39
+ error: UnknownError,
39
40
  }),
40
41
  ) {}
package/src/common/mod.ts CHANGED
@@ -1,15 +1,16 @@
1
+ export { OversizeChunkItemError, splitArrayBySize } from '@livestore/common/sync'
1
2
  import { Schema } from '@livestore/utils/effect'
2
3
 
3
4
  export type { CfTypes } from '@livestore/common-cf'
4
-
5
+ export * from './constants.ts'
5
6
  export { SyncHttpRpc } from './http-rpc-schema.ts'
6
7
  export * as SyncMessage from './sync-message-types.ts'
7
8
 
8
9
  export const SearchParamsSchema = Schema.Struct({
9
10
  storeId: Schema.String,
10
- payload: Schema.compose(Schema.StringFromUriComponent, Schema.parseJson(Schema.JsonValue)).pipe(Schema.UndefinedOr),
11
+ payload: Schema.optional(Schema.StringFromUriComponent.pipe(Schema.decodeTo(Schema.fromJsonString(Schema.Json)))),
11
12
  // NOTE `do-rpc` is handled differently
12
- transport: Schema.Union(Schema.Literal('http'), Schema.Literal('ws')),
13
+ transport: Schema.Literals(['http', 'ws']),
13
14
  })
14
15
 
15
16
  export type SearchParams = typeof SearchParamsSchema.Type
@@ -11,7 +11,7 @@ import { Schema } from '@livestore/utils/effect'
11
11
  export const SyncMetadata = Schema.TaggedStruct('SyncMessage.SyncMetadata', {
12
12
  /** ISO date format */
13
13
  createdAt: Schema.String,
14
- }).annotations({ title: '@livestore/sync-cf:SyncMetadata' })
14
+ }).annotate({ title: '@livestore/sync-cf:SyncMetadata' })
15
15
 
16
16
  export type SyncMetadata = typeof SyncMetadata.Type
17
17
 
@@ -20,23 +20,23 @@ export const PullRequest = Schema.Struct({
20
20
  cursor: Schema.Option(
21
21
  Schema.Struct({
22
22
  backendId: BackendId,
23
- eventSequenceNumber: EventSequenceNumber.GlobalEventSequenceNumber,
23
+ eventSequenceNumber: EventSequenceNumber.Global.Schema,
24
24
  }),
25
25
  ),
26
- }).annotations({ title: '@livestore/sync-cf:PullRequest' })
26
+ }).annotate({ title: '@livestore/sync-cf:PullRequest' })
27
27
 
28
28
  export type PullRequest = typeof PullRequest.Type
29
29
 
30
30
  export const PullResponse = Schema.Struct({
31
31
  batch: Schema.Array(
32
32
  Schema.Struct({
33
- eventEncoded: LiveStoreEvent.AnyEncodedGlobal,
33
+ eventEncoded: LiveStoreEvent.Global.Encoded,
34
34
  metadata: Schema.Option(SyncMetadata),
35
35
  }),
36
36
  ),
37
37
  pageInfo: SyncBackend.PullResPageInfo,
38
38
  backendId: BackendId,
39
- }).annotations({ title: '@livestore/sync-cf:PullResponse' })
39
+ }).annotate({ title: '@livestore/sync-cf:PullResponse' })
40
40
 
41
41
  export const emptyPullResponse = (backendId: string) =>
42
42
  PullResponse.make({
@@ -48,34 +48,34 @@ export const emptyPullResponse = (backendId: string) =>
48
48
  export type PullResponse = typeof PullResponse.Type
49
49
 
50
50
  export const PushRequest = Schema.Struct({
51
- batch: Schema.Array(LiveStoreEvent.AnyEncodedGlobal),
51
+ batch: Schema.Array(LiveStoreEvent.Global.Encoded),
52
52
  backendId: Schema.Option(BackendId),
53
- }).annotations({ title: '@livestore/sync-cf:PushRequest' })
53
+ }).annotate({ title: '@livestore/sync-cf:PushRequest' })
54
54
 
55
55
  export type PushRequest = typeof PushRequest.Type
56
56
 
57
- export const PushAck = Schema.Struct({}).annotations({
57
+ export const PushAck = Schema.Struct({}).annotate({
58
58
  title: '@livestore/sync-cf:PushAck',
59
59
  })
60
60
 
61
61
  export type PushAck = typeof PushAck.Type
62
62
 
63
- export const Ping = Schema.TaggedStruct('SyncMessage.Ping', {}).annotations({ title: '@livestore/sync-cf:Ping' })
63
+ export const Ping = Schema.TaggedStruct('SyncMessage.Ping', {}).annotate({ title: '@livestore/sync-cf:Ping' })
64
64
 
65
65
  export type Ping = typeof Ping.Type
66
66
 
67
- export const Pong = Schema.TaggedStruct('SyncMessage.Pong', {}).annotations({ title: '@livestore/sync-cf:Pong' })
67
+ export const Pong = Schema.TaggedStruct('SyncMessage.Pong', {}).annotate({ title: '@livestore/sync-cf:Pong' })
68
68
 
69
69
  export type Pong = typeof Pong.Type
70
70
 
71
71
  // Admin operations
72
72
  export const AdminResetRoomRequest = Schema.TaggedStruct('SyncMessage.AdminResetRoomRequest', {
73
73
  adminSecret: Schema.String,
74
- }).annotations({ title: '@livestore/sync-cf:AdminResetRoomRequest' })
74
+ }).annotate({ title: '@livestore/sync-cf:AdminResetRoomRequest' })
75
75
 
76
76
  export type AdminResetRoomRequest = typeof AdminResetRoomRequest.Type
77
77
 
78
- export const AdminResetRoomResponse = Schema.TaggedStruct('SyncMessage.AdminResetRoomResponse', {}).annotations({
78
+ export const AdminResetRoomResponse = Schema.TaggedStruct('SyncMessage.AdminResetRoomResponse', {}).annotate({
79
79
  title: '@livestore/sync-cf:AdminResetRoomResponse',
80
80
  })
81
81
 
@@ -83,7 +83,7 @@ export type AdminResetRoomResponse = typeof AdminResetRoomResponse.Type
83
83
 
84
84
  export const AdminInfoRequest = Schema.TaggedStruct('SyncMessage.AdminInfoRequest', {
85
85
  adminSecret: Schema.String,
86
- }).annotations({ title: '@livestore/sync-cf:AdminInfoRequest' })
86
+ }).annotate({ title: '@livestore/sync-cf:AdminInfoRequest' })
87
87
 
88
88
  export type AdminInfoRequest = typeof AdminInfoRequest.Type
89
89
 
@@ -91,27 +91,27 @@ export const AdminInfoResponse = Schema.TaggedStruct('SyncMessage.AdminInfoRespo
91
91
  info: Schema.Struct({
92
92
  durableObjectId: Schema.String,
93
93
  }),
94
- }).annotations({ title: '@livestore/sync-cf:AdminInfoResponse' })
94
+ }).annotate({ title: '@livestore/sync-cf:AdminInfoResponse' })
95
95
 
96
96
  export type AdminInfoResponse = typeof AdminInfoResponse.Type
97
97
 
98
- export const BackendToClientMessage = Schema.Union(
98
+ export const BackendToClientMessage = Schema.Union([
99
99
  PullResponse,
100
100
  PushAck,
101
101
  Pong,
102
102
  AdminResetRoomResponse,
103
103
  AdminInfoResponse,
104
- )
104
+ ])
105
105
  export type BackendToClientMessage = typeof BackendToClientMessage.Type
106
106
 
107
- export const ClientToBackendMessage = Schema.Union(
107
+ export const ClientToBackendMessage = Schema.Union([
108
108
  PullRequest,
109
109
  PushRequest,
110
110
  Ping,
111
111
  AdminResetRoomRequest,
112
112
  AdminInfoRequest,
113
- )
113
+ ])
114
114
  export type ClientToBackendMessage = typeof ClientToBackendMessage.Type
115
115
 
116
- export const Message = Schema.Union(BackendToClientMessage, ClientToBackendMessage)
116
+ export const Message = Schema.Union([BackendToClientMessage, ClientToBackendMessage])
117
117
  export type Message = typeof Message.Type
@@ -1,5 +1,6 @@
1
- import { InvalidPullError, InvalidPushError } from '@livestore/common'
1
+ import { BackendIdMismatchError, ServerAheadError, UnknownError } from '@livestore/common'
2
2
  import { Rpc, RpcGroup, Schema } from '@livestore/utils/effect'
3
+
3
4
  import * as SyncMessage from './sync-message-types.ts'
4
5
 
5
6
  /**
@@ -13,23 +14,23 @@ export class SyncWsRpc extends RpcGroup.make(
13
14
  Rpc.make('SyncWsRpc.Pull', {
14
15
  payload: Schema.Struct({
15
16
  storeId: Schema.String,
16
- payload: Schema.optional(Schema.JsonValue),
17
+ payload: Schema.optional(Schema.Json),
17
18
  /** Whether to keep the pull stream alive and wait for more events */
18
19
  live: Schema.Boolean,
19
20
  ...SyncMessage.PullRequest.fields,
20
21
  }),
21
22
  success: SyncMessage.PullResponse,
22
- error: InvalidPullError,
23
+ error: Schema.Union([UnknownError, BackendIdMismatchError]),
23
24
  stream: true,
24
25
  }),
25
26
  Rpc.make('SyncWsRpc.Push', {
26
27
  payload: Schema.Struct({
27
28
  storeId: Schema.String,
28
- payload: Schema.optional(Schema.JsonValue),
29
+ payload: Schema.optional(Schema.Json),
29
30
  ...SyncMessage.PushRequest.fields,
30
31
  }),
31
32
  success: SyncMessage.PushAck,
32
- error: InvalidPushError,
33
+ error: Schema.Union([UnknownError, ServerAheadError, BackendIdMismatchError]),
33
34
  }),
34
35
  // Ping <> Pong is handled by DO WS auto-response
35
36
  // TODO add admin RPCs
@@ -1,22 +0,0 @@
1
- import { Chunk } from '@livestore/utils/effect';
2
- /**
3
- * Configuration describing how to break a chunk into smaller payload-safe chunks.
4
- */
5
- export interface ChunkingOptions<A> {
6
- /** Maximum number of items that may appear in any emitted chunk. */
7
- readonly maxItems: number;
8
- /** Maximum encoded byte size allowed for any emitted chunk. */
9
- readonly maxBytes: number;
10
- /**
11
- * Callback that produces a JSON-serialisable structure whose byte size should
12
- * fit within {@link maxBytes}. This lets callers control framing overhead.
13
- */
14
- readonly encode: (items: ReadonlyArray<A>) => unknown;
15
- }
16
- /**
17
- * Derives a function that splits an input chunk into sub-chunks confined by
18
- * both item count and encoded byte size limits. Designed for transports with
19
- * strict frame caps (e.g. Cloudflare hibernated WebSockets).
20
- */
21
- export declare const splitChunkBySize: <A>(options: ChunkingOptions<A>) => (chunk: Chunk.Chunk<A>) => Chunk.Chunk<Chunk.Chunk<A>>;
22
- //# sourceMappingURL=ws-chunking.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ws-chunking.d.ts","sourceRoot":"","sources":["../../../src/cf-worker/do/ws-chunking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAI/C;;GAEG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,OAAO,CAAA;CACtD;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAC1B,CAAC,EAAE,SAAS,eAAe,CAAC,CAAC,CAAC,MAC9B,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAiDlD,CAAA"}
@@ -1,49 +0,0 @@
1
- import { Chunk } from '@livestore/utils/effect';
2
- const textEncoder = new TextEncoder();
3
- /**
4
- * Derives a function that splits an input chunk into sub-chunks confined by
5
- * both item count and encoded byte size limits. Designed for transports with
6
- * strict frame caps (e.g. Cloudflare hibernated WebSockets).
7
- */
8
- export const splitChunkBySize = (options) => (chunk) => {
9
- const maxItems = Math.max(1, options.maxItems);
10
- const maxBytes = Math.max(1, options.maxBytes);
11
- const encode = options.encode;
12
- const measure = (items) => {
13
- const encoded = encode(items);
14
- return textEncoder.encode(JSON.stringify(encoded)).byteLength;
15
- };
16
- const items = Chunk.toReadonlyArray(chunk);
17
- if (items.length === 0) {
18
- return Chunk.fromIterable([]);
19
- }
20
- const result = [];
21
- let current = [];
22
- const flushCurrent = () => {
23
- if (current.length > 0) {
24
- result.push(Chunk.fromIterable(current));
25
- current = [];
26
- }
27
- };
28
- for (const item of items) {
29
- current.push(item);
30
- const exceedsLimit = current.length > maxItems || measure(current) > maxBytes;
31
- if (exceedsLimit) {
32
- // remove the item we just added and emit the previous chunk if it exists
33
- const last = current.pop();
34
- flushCurrent();
35
- if (last !== undefined) {
36
- current = [last];
37
- const singleItemTooLarge = measure(current) > maxBytes;
38
- if (singleItemTooLarge || current.length > maxItems) {
39
- // Emit the oversized item on its own; downstream can decide how to handle it.
40
- result.push(Chunk.of(last));
41
- current = [];
42
- }
43
- }
44
- }
45
- }
46
- flushCurrent();
47
- return Chunk.fromIterable(result);
48
- };
49
- //# sourceMappingURL=ws-chunking.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ws-chunking.js","sourceRoot":"","sources":["../../../src/cf-worker/do/ws-chunking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAE/C,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAA;AAiBrC;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,CAAI,OAA2B,EAAE,EAAE,CACnC,CAAC,KAAqB,EAA+B,EAAE;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;IAE7B,MAAM,OAAO,GAAG,CAAC,KAAuB,EAAE,EAAE;QAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;QAC7B,OAAO,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAA;IAC/D,CAAC,CAAA;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;IAC1C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC,YAAY,CAAiB,EAAE,CAAC,CAAA;IAC/C,CAAC;IAED,MAAM,MAAM,GAA0B,EAAE,CAAA;IACxC,IAAI,OAAO,GAAa,EAAE,CAAA;IAE1B,MAAM,YAAY,GAAG,GAAG,EAAE;QACxB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;YACxC,OAAO,GAAG,EAAE,CAAA;QACd,CAAC;IACH,CAAC,CAAA;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAClB,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAA;QAE7E,IAAI,YAAY,EAAE,CAAC;YACjB,yEAAyE;YACzE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAG,CAAA;YAC3B,YAAY,EAAE,CAAA;YAEd,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,OAAO,GAAG,CAAC,IAAI,CAAC,CAAA;gBAChB,MAAM,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAA;gBACtD,IAAI,kBAAkB,IAAI,OAAO,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;oBACpD,8EAA8E;oBAC9E,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA;oBAC3B,OAAO,GAAG,EAAE,CAAA;gBACd,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,YAAY,EAAE,CAAA;IAEd,OAAO,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;AACnC,CAAC,CAAA"}
@@ -1,76 +0,0 @@
1
- import { Chunk } from '@livestore/utils/effect'
2
-
3
- const textEncoder = new TextEncoder()
4
-
5
- /**
6
- * Configuration describing how to break a chunk into smaller payload-safe chunks.
7
- */
8
- export interface ChunkingOptions<A> {
9
- /** Maximum number of items that may appear in any emitted chunk. */
10
- readonly maxItems: number
11
- /** Maximum encoded byte size allowed for any emitted chunk. */
12
- readonly maxBytes: number
13
- /**
14
- * Callback that produces a JSON-serialisable structure whose byte size should
15
- * fit within {@link maxBytes}. This lets callers control framing overhead.
16
- */
17
- readonly encode: (items: ReadonlyArray<A>) => unknown
18
- }
19
-
20
- /**
21
- * Derives a function that splits an input chunk into sub-chunks confined by
22
- * both item count and encoded byte size limits. Designed for transports with
23
- * strict frame caps (e.g. Cloudflare hibernated WebSockets).
24
- */
25
- export const splitChunkBySize =
26
- <A>(options: ChunkingOptions<A>) =>
27
- (chunk: Chunk.Chunk<A>): Chunk.Chunk<Chunk.Chunk<A>> => {
28
- const maxItems = Math.max(1, options.maxItems)
29
- const maxBytes = Math.max(1, options.maxBytes)
30
- const encode = options.encode
31
-
32
- const measure = (items: ReadonlyArray<A>) => {
33
- const encoded = encode(items)
34
- return textEncoder.encode(JSON.stringify(encoded)).byteLength
35
- }
36
-
37
- const items = Chunk.toReadonlyArray(chunk)
38
- if (items.length === 0) {
39
- return Chunk.fromIterable<Chunk.Chunk<A>>([])
40
- }
41
-
42
- const result: Array<Chunk.Chunk<A>> = []
43
- let current: Array<A> = []
44
-
45
- const flushCurrent = () => {
46
- if (current.length > 0) {
47
- result.push(Chunk.fromIterable(current))
48
- current = []
49
- }
50
- }
51
-
52
- for (const item of items) {
53
- current.push(item)
54
- const exceedsLimit = current.length > maxItems || measure(current) > maxBytes
55
-
56
- if (exceedsLimit) {
57
- // remove the item we just added and emit the previous chunk if it exists
58
- const last = current.pop()!
59
- flushCurrent()
60
-
61
- if (last !== undefined) {
62
- current = [last]
63
- const singleItemTooLarge = measure(current) > maxBytes
64
- if (singleItemTooLarge || current.length > maxItems) {
65
- // Emit the oversized item on its own; downstream can decide how to handle it.
66
- result.push(Chunk.of(last))
67
- current = []
68
- }
69
- }
70
- }
71
- }
72
-
73
- flushCurrent()
74
-
75
- return Chunk.fromIterable(result)
76
- }