@tanstack/db 0.4.9 → 0.4.11

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 (100) hide show
  1. package/dist/cjs/collection/events.cjs +9 -51
  2. package/dist/cjs/collection/events.cjs.map +1 -1
  3. package/dist/cjs/collection/events.d.cts +18 -7
  4. package/dist/cjs/collection/index.cjs +9 -12
  5. package/dist/cjs/collection/index.cjs.map +1 -1
  6. package/dist/cjs/collection/index.d.cts +13 -14
  7. package/dist/cjs/collection/subscription.cjs +62 -6
  8. package/dist/cjs/collection/subscription.cjs.map +1 -1
  9. package/dist/cjs/collection/subscription.d.cts +16 -3
  10. package/dist/cjs/collection/sync.cjs +58 -6
  11. package/dist/cjs/collection/sync.cjs.map +1 -1
  12. package/dist/cjs/collection/sync.d.cts +18 -4
  13. package/dist/cjs/errors.cjs +8 -0
  14. package/dist/cjs/errors.cjs.map +1 -1
  15. package/dist/cjs/errors.d.cts +6 -0
  16. package/dist/cjs/event-emitter.cjs +94 -0
  17. package/dist/cjs/event-emitter.cjs.map +1 -0
  18. package/dist/cjs/event-emitter.d.cts +45 -0
  19. package/dist/cjs/index.cjs +1 -0
  20. package/dist/cjs/index.cjs.map +1 -1
  21. package/dist/cjs/local-only.cjs.map +1 -1
  22. package/dist/cjs/local-only.d.cts +2 -5
  23. package/dist/cjs/query/compiler/index.cjs +6 -2
  24. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  25. package/dist/cjs/query/compiler/index.d.cts +3 -2
  26. package/dist/cjs/query/compiler/joins.cjs +6 -3
  27. package/dist/cjs/query/compiler/joins.cjs.map +1 -1
  28. package/dist/cjs/query/compiler/joins.d.cts +2 -2
  29. package/dist/cjs/query/compiler/order-by.cjs +18 -4
  30. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  31. package/dist/cjs/query/compiler/order-by.d.cts +2 -1
  32. package/dist/cjs/query/compiler/types.d.cts +4 -0
  33. package/dist/cjs/query/index.d.cts +1 -0
  34. package/dist/cjs/query/live/collection-config-builder.cjs +43 -6
  35. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  36. package/dist/cjs/query/live/collection-config-builder.d.cts +27 -1
  37. package/dist/cjs/query/live/collection-subscriber.cjs +29 -0
  38. package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
  39. package/dist/cjs/query/live/collection-subscriber.d.cts +1 -0
  40. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  41. package/dist/cjs/query/live-query-collection.d.cts +2 -2
  42. package/dist/cjs/types.d.cts +82 -11
  43. package/dist/esm/collection/events.d.ts +18 -7
  44. package/dist/esm/collection/events.js +9 -51
  45. package/dist/esm/collection/events.js.map +1 -1
  46. package/dist/esm/collection/index.d.ts +13 -14
  47. package/dist/esm/collection/index.js +9 -12
  48. package/dist/esm/collection/index.js.map +1 -1
  49. package/dist/esm/collection/subscription.d.ts +16 -3
  50. package/dist/esm/collection/subscription.js +62 -6
  51. package/dist/esm/collection/subscription.js.map +1 -1
  52. package/dist/esm/collection/sync.d.ts +18 -4
  53. package/dist/esm/collection/sync.js +59 -7
  54. package/dist/esm/collection/sync.js.map +1 -1
  55. package/dist/esm/errors.d.ts +6 -0
  56. package/dist/esm/errors.js +8 -0
  57. package/dist/esm/errors.js.map +1 -1
  58. package/dist/esm/event-emitter.d.ts +45 -0
  59. package/dist/esm/event-emitter.js +94 -0
  60. package/dist/esm/event-emitter.js.map +1 -0
  61. package/dist/esm/index.js +2 -1
  62. package/dist/esm/local-only.d.ts +2 -5
  63. package/dist/esm/local-only.js.map +1 -1
  64. package/dist/esm/query/compiler/index.d.ts +3 -2
  65. package/dist/esm/query/compiler/index.js +6 -2
  66. package/dist/esm/query/compiler/index.js.map +1 -1
  67. package/dist/esm/query/compiler/joins.d.ts +2 -2
  68. package/dist/esm/query/compiler/joins.js +6 -3
  69. package/dist/esm/query/compiler/joins.js.map +1 -1
  70. package/dist/esm/query/compiler/order-by.d.ts +2 -1
  71. package/dist/esm/query/compiler/order-by.js +18 -4
  72. package/dist/esm/query/compiler/order-by.js.map +1 -1
  73. package/dist/esm/query/compiler/types.d.ts +4 -0
  74. package/dist/esm/query/index.d.ts +1 -0
  75. package/dist/esm/query/live/collection-config-builder.d.ts +27 -1
  76. package/dist/esm/query/live/collection-config-builder.js +44 -7
  77. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  78. package/dist/esm/query/live/collection-subscriber.d.ts +1 -0
  79. package/dist/esm/query/live/collection-subscriber.js +29 -0
  80. package/dist/esm/query/live/collection-subscriber.js.map +1 -1
  81. package/dist/esm/query/live-query-collection.d.ts +2 -2
  82. package/dist/esm/query/live-query-collection.js.map +1 -1
  83. package/dist/esm/types.d.ts +82 -11
  84. package/package.json +2 -2
  85. package/src/collection/events.ts +25 -74
  86. package/src/collection/index.ts +15 -19
  87. package/src/collection/subscription.ts +88 -6
  88. package/src/collection/sync.ts +81 -9
  89. package/src/errors.ts +12 -0
  90. package/src/event-emitter.ts +118 -0
  91. package/src/local-only.ts +5 -12
  92. package/src/query/compiler/index.ts +9 -1
  93. package/src/query/compiler/joins.ts +7 -1
  94. package/src/query/compiler/order-by.ts +23 -2
  95. package/src/query/compiler/types.ts +5 -0
  96. package/src/query/index.ts +1 -0
  97. package/src/query/live/collection-config-builder.ts +76 -7
  98. package/src/query/live/collection-subscriber.ts +50 -0
  99. package/src/query/live-query-collection.ts +8 -4
  100. package/src/types.ts +93 -11
@@ -1 +1 @@
1
- {"version":3,"file":"live-query-collection.cjs","sources":["../../../src/query/live-query-collection.ts"],"sourcesContent":["import { createCollection } from \"../collection/index.js\"\nimport { CollectionConfigBuilder } from \"./live/collection-config-builder.js\"\nimport {\n getBuilderFromConfig,\n registerCollectionBuilder,\n} from \"./live/collection-registry.js\"\nimport type { LiveQueryCollectionUtils } from \"./live/collection-config-builder.js\"\nimport type { LiveQueryCollectionConfig } from \"./live/types.js\"\nimport type { InitialQueryBuilder, QueryBuilder } from \"./builder/index.js\"\nimport type { Collection } from \"../collection/index.js\"\nimport type {\n CollectionConfig,\n CollectionConfigSingleRowOption,\n NonSingleResult,\n SingleResult,\n UtilsRecord,\n} from \"../types.js\"\nimport type { Context, GetResult } from \"./builder/types.js\"\n\ntype CollectionConfigForContext<\n TContext extends Context,\n TResult extends object,\n> = TContext extends SingleResult\n ? CollectionConfigSingleRowOption<TResult> & SingleResult\n : CollectionConfigSingleRowOption<TResult> & NonSingleResult\n\ntype CollectionForContext<\n TContext extends Context,\n TResult extends object,\n> = TContext extends SingleResult\n ? Collection<TResult> & SingleResult\n : Collection<TResult> & NonSingleResult\n\n/**\n * Creates live query collection options for use with createCollection\n *\n * @example\n * ```typescript\n * const options = liveQueryCollectionOptions({\n * // id is optional - will auto-generate if not provided\n * query: (q) => q\n * .from({ post: postsCollection })\n * .where(({ post }) => eq(post.published, true))\n * .select(({ post }) => ({\n * id: post.id,\n * title: post.title,\n * content: post.content,\n * })),\n * // getKey is optional - will use stream key if not provided\n * })\n *\n * const collection = createCollection(options)\n * ```\n *\n * @param config - Configuration options for the live query collection\n * @returns Collection options that can be passed to createCollection\n */\nexport function liveQueryCollectionOptions<\n TContext extends Context,\n TResult extends object = GetResult<TContext>,\n>(\n config: LiveQueryCollectionConfig<TContext, TResult>\n): CollectionConfigForContext<TContext, TResult> & {\n utils: LiveQueryCollectionUtils\n} {\n const collectionConfigBuilder = new CollectionConfigBuilder<\n TContext,\n TResult\n >(config)\n return collectionConfigBuilder.getConfig() as CollectionConfigForContext<\n TContext,\n TResult\n > & { utils: LiveQueryCollectionUtils }\n}\n\n/**\n * Creates a live query collection directly\n *\n * @example\n * ```typescript\n * // Minimal usage - just pass a query function\n * const activeUsers = createLiveQueryCollection(\n * (q) => q\n * .from({ user: usersCollection })\n * .where(({ user }) => eq(user.active, true))\n * .select(({ user }) => ({ id: user.id, name: user.name }))\n * )\n *\n * // Full configuration with custom options\n * const searchResults = createLiveQueryCollection({\n * id: \"search-results\", // Custom ID (auto-generated if omitted)\n * query: (q) => q\n * .from({ post: postsCollection })\n * .where(({ post }) => like(post.title, `%${searchTerm}%`))\n * .select(({ post }) => ({\n * id: post.id,\n * title: post.title,\n * excerpt: post.excerpt,\n * })),\n * getKey: (item) => item.id, // Custom key function (uses stream key if omitted)\n * utils: {\n * updateSearchTerm: (newTerm: string) => {\n * // Custom utility functions\n * }\n * }\n * })\n * ```\n */\n\n// Overload 1: Accept just the query function\nexport function createLiveQueryCollection<\n TContext extends Context,\n TResult extends object = GetResult<TContext>,\n>(\n query: (q: InitialQueryBuilder) => QueryBuilder<TContext>\n): CollectionForContext<TContext, TResult> & {\n utils: LiveQueryCollectionUtils\n}\n\n// Overload 2: Accept full config object with optional utilities\nexport function createLiveQueryCollection<\n TContext extends Context,\n TResult extends object = GetResult<TContext>,\n TUtils extends UtilsRecord = {},\n>(\n config: LiveQueryCollectionConfig<TContext, TResult> & { utils?: TUtils }\n): CollectionForContext<TContext, TResult> & {\n utils: LiveQueryCollectionUtils & TUtils\n}\n\n// Implementation\nexport function createLiveQueryCollection<\n TContext extends Context,\n TResult extends object = GetResult<TContext>,\n TUtils extends UtilsRecord = {},\n>(\n configOrQuery:\n | (LiveQueryCollectionConfig<TContext, TResult> & { utils?: TUtils })\n | ((q: InitialQueryBuilder) => QueryBuilder<TContext>)\n): CollectionForContext<TContext, TResult> & {\n utils: LiveQueryCollectionUtils & TUtils\n} {\n // Determine if the argument is a function (query) or a config object\n if (typeof configOrQuery === `function`) {\n // Simple query function case\n const config: LiveQueryCollectionConfig<TContext, TResult> = {\n query: configOrQuery as (\n q: InitialQueryBuilder\n ) => QueryBuilder<TContext>,\n }\n const options = liveQueryCollectionOptions<TContext, TResult>(config)\n return bridgeToCreateCollection(options) as CollectionForContext<\n TContext,\n TResult\n > & { utils: LiveQueryCollectionUtils & TUtils }\n } else {\n // Config object case\n const config = configOrQuery as LiveQueryCollectionConfig<\n TContext,\n TResult\n > & { utils?: TUtils }\n const options = liveQueryCollectionOptions<TContext, TResult>(config)\n\n // Merge custom utils if provided, preserving the getBuilder() method for dependency tracking\n if (config.utils) {\n options.utils = { ...options.utils, ...config.utils }\n }\n\n return bridgeToCreateCollection(options) as CollectionForContext<\n TContext,\n TResult\n > & { utils: LiveQueryCollectionUtils & TUtils }\n }\n}\n\n/**\n * Bridge function that handles the type compatibility between query2's TResult\n * and core collection's output type without exposing ugly type assertions to users\n */\nfunction bridgeToCreateCollection<\n TResult extends object,\n TUtils extends UtilsRecord = {},\n>(\n options: CollectionConfig<TResult> & { utils: TUtils }\n): Collection<TResult, string | number, TUtils> {\n const collection = createCollection(options as any) as unknown as Collection<\n TResult,\n string | number,\n LiveQueryCollectionUtils\n >\n\n const builder = getBuilderFromConfig(options)\n if (builder) {\n registerCollectionBuilder(collection, builder)\n }\n\n return collection as unknown as Collection<TResult, string | number, TUtils>\n}\n"],"names":["collectionConfigBuilder","CollectionConfigBuilder","createCollection","getBuilderFromConfig","registerCollectionBuilder"],"mappings":";;;;;AAyDO,SAAS,2BAId,QAGA;AACA,QAAMA,4BAA0B,IAAIC,wBAAAA,wBAGlC,MAAM;AACR,SAAOD,0BAAwB,UAAA;AAIjC;AA0DO,SAAS,0BAKd,eAKA;AAEA,MAAI,OAAO,kBAAkB,YAAY;AAEvC,UAAM,SAAuD;AAAA,MAC3D,OAAO;AAAA,IAAA;AAIT,UAAM,UAAU,2BAA8C,MAAM;AACpE,WAAO,yBAAyB,OAAO;AAAA,EAIzC,OAAO;AAEL,UAAM,SAAS;AAIf,UAAM,UAAU,2BAA8C,MAAM;AAGpE,QAAI,OAAO,OAAO;AAChB,cAAQ,QAAQ,EAAE,GAAG,QAAQ,OAAO,GAAG,OAAO,MAAA;AAAA,IAChD;AAEA,WAAO,yBAAyB,OAAO;AAAA,EAIzC;AACF;AAMA,SAAS,yBAIP,SAC8C;AAC9C,QAAM,aAAaE,MAAAA,iBAAiB,OAAc;AAMlD,QAAM,UAAUC,mBAAAA,qBAAqB,OAAO;AAC5C,MAAI,SAAS;AACXC,uBAAAA,0BAA0B,YAAY,OAAO;AAAA,EAC/C;AAEA,SAAO;AACT;;;"}
1
+ {"version":3,"file":"live-query-collection.cjs","sources":["../../../src/query/live-query-collection.ts"],"sourcesContent":["import { createCollection } from \"../collection/index.js\"\nimport { CollectionConfigBuilder } from \"./live/collection-config-builder.js\"\nimport {\n getBuilderFromConfig,\n registerCollectionBuilder,\n} from \"./live/collection-registry.js\"\nimport type { LiveQueryCollectionUtils } from \"./live/collection-config-builder.js\"\nimport type { LiveQueryCollectionConfig } from \"./live/types.js\"\nimport type { InitialQueryBuilder, QueryBuilder } from \"./builder/index.js\"\nimport type { Collection } from \"../collection/index.js\"\nimport type {\n CollectionConfig,\n CollectionConfigSingleRowOption,\n NonSingleResult,\n SingleResult,\n UtilsRecord,\n} from \"../types.js\"\nimport type { Context, GetResult } from \"./builder/types.js\"\n\ntype CollectionConfigForContext<\n TContext extends Context,\n TResult extends object,\n TUtils extends UtilsRecord = {},\n> = TContext extends SingleResult\n ? CollectionConfigSingleRowOption<TResult, string | number, never, TUtils> &\n SingleResult\n : CollectionConfigSingleRowOption<TResult, string | number, never, TUtils> &\n NonSingleResult\n\ntype CollectionForContext<\n TContext extends Context,\n TResult extends object,\n TUtils extends UtilsRecord = {},\n> = TContext extends SingleResult\n ? Collection<TResult, string | number, TUtils> & SingleResult\n : Collection<TResult, string | number, TUtils> & NonSingleResult\n\n/**\n * Creates live query collection options for use with createCollection\n *\n * @example\n * ```typescript\n * const options = liveQueryCollectionOptions({\n * // id is optional - will auto-generate if not provided\n * query: (q) => q\n * .from({ post: postsCollection })\n * .where(({ post }) => eq(post.published, true))\n * .select(({ post }) => ({\n * id: post.id,\n * title: post.title,\n * content: post.content,\n * })),\n * // getKey is optional - will use stream key if not provided\n * })\n *\n * const collection = createCollection(options)\n * ```\n *\n * @param config - Configuration options for the live query collection\n * @returns Collection options that can be passed to createCollection\n */\nexport function liveQueryCollectionOptions<\n TContext extends Context,\n TResult extends object = GetResult<TContext>,\n>(\n config: LiveQueryCollectionConfig<TContext, TResult>\n): CollectionConfigForContext<TContext, TResult> & {\n utils: LiveQueryCollectionUtils\n} {\n const collectionConfigBuilder = new CollectionConfigBuilder<\n TContext,\n TResult\n >(config)\n return collectionConfigBuilder.getConfig() as CollectionConfigForContext<\n TContext,\n TResult\n > & { utils: LiveQueryCollectionUtils }\n}\n\n/**\n * Creates a live query collection directly\n *\n * @example\n * ```typescript\n * // Minimal usage - just pass a query function\n * const activeUsers = createLiveQueryCollection(\n * (q) => q\n * .from({ user: usersCollection })\n * .where(({ user }) => eq(user.active, true))\n * .select(({ user }) => ({ id: user.id, name: user.name }))\n * )\n *\n * // Full configuration with custom options\n * const searchResults = createLiveQueryCollection({\n * id: \"search-results\", // Custom ID (auto-generated if omitted)\n * query: (q) => q\n * .from({ post: postsCollection })\n * .where(({ post }) => like(post.title, `%${searchTerm}%`))\n * .select(({ post }) => ({\n * id: post.id,\n * title: post.title,\n * excerpt: post.excerpt,\n * })),\n * getKey: (item) => item.id, // Custom key function (uses stream key if omitted)\n * utils: {\n * updateSearchTerm: (newTerm: string) => {\n * // Custom utility functions\n * }\n * }\n * })\n * ```\n */\n\n// Overload 1: Accept just the query function\nexport function createLiveQueryCollection<\n TContext extends Context,\n TResult extends object = GetResult<TContext>,\n>(\n query: (q: InitialQueryBuilder) => QueryBuilder<TContext>\n): CollectionForContext<TContext, TResult> & {\n utils: LiveQueryCollectionUtils\n}\n\n// Overload 2: Accept full config object with optional utilities\nexport function createLiveQueryCollection<\n TContext extends Context,\n TResult extends object = GetResult<TContext>,\n TUtils extends UtilsRecord = {},\n>(\n config: LiveQueryCollectionConfig<TContext, TResult> & { utils?: TUtils }\n): CollectionForContext<TContext, TResult> & {\n utils: LiveQueryCollectionUtils & TUtils\n}\n\n// Implementation\nexport function createLiveQueryCollection<\n TContext extends Context,\n TResult extends object = GetResult<TContext>,\n TUtils extends UtilsRecord = {},\n>(\n configOrQuery:\n | (LiveQueryCollectionConfig<TContext, TResult> & { utils?: TUtils })\n | ((q: InitialQueryBuilder) => QueryBuilder<TContext>)\n): CollectionForContext<TContext, TResult> & {\n utils: LiveQueryCollectionUtils & TUtils\n} {\n // Determine if the argument is a function (query) or a config object\n if (typeof configOrQuery === `function`) {\n // Simple query function case\n const config: LiveQueryCollectionConfig<TContext, TResult> = {\n query: configOrQuery as (\n q: InitialQueryBuilder\n ) => QueryBuilder<TContext>,\n }\n const options = liveQueryCollectionOptions<TContext, TResult>(config)\n return bridgeToCreateCollection(options) as CollectionForContext<\n TContext,\n TResult\n > & { utils: LiveQueryCollectionUtils & TUtils }\n } else {\n // Config object case\n const config = configOrQuery as LiveQueryCollectionConfig<\n TContext,\n TResult\n > & { utils?: TUtils }\n const options = liveQueryCollectionOptions<TContext, TResult>(config)\n\n // Merge custom utils if provided, preserving the getBuilder() method for dependency tracking\n if (config.utils) {\n options.utils = { ...options.utils, ...config.utils }\n }\n\n return bridgeToCreateCollection(options) as CollectionForContext<\n TContext,\n TResult\n > & { utils: LiveQueryCollectionUtils & TUtils }\n }\n}\n\n/**\n * Bridge function that handles the type compatibility between query2's TResult\n * and core collection's output type without exposing ugly type assertions to users\n */\nfunction bridgeToCreateCollection<\n TResult extends object,\n TUtils extends UtilsRecord = {},\n>(\n options: CollectionConfig<TResult> & { utils: TUtils }\n): Collection<TResult, string | number, TUtils> {\n const collection = createCollection(options as any) as unknown as Collection<\n TResult,\n string | number,\n LiveQueryCollectionUtils\n >\n\n const builder = getBuilderFromConfig(options)\n if (builder) {\n registerCollectionBuilder(collection, builder)\n }\n\n return collection as unknown as Collection<TResult, string | number, TUtils>\n}\n"],"names":["collectionConfigBuilder","CollectionConfigBuilder","createCollection","getBuilderFromConfig","registerCollectionBuilder"],"mappings":";;;;;AA6DO,SAAS,2BAId,QAGA;AACA,QAAMA,4BAA0B,IAAIC,wBAAAA,wBAGlC,MAAM;AACR,SAAOD,0BAAwB,UAAA;AAIjC;AA0DO,SAAS,0BAKd,eAKA;AAEA,MAAI,OAAO,kBAAkB,YAAY;AAEvC,UAAM,SAAuD;AAAA,MAC3D,OAAO;AAAA,IAAA;AAIT,UAAM,UAAU,2BAA8C,MAAM;AACpE,WAAO,yBAAyB,OAAO;AAAA,EAIzC,OAAO;AAEL,UAAM,SAAS;AAIf,UAAM,UAAU,2BAA8C,MAAM;AAGpE,QAAI,OAAO,OAAO;AAChB,cAAQ,QAAQ,EAAE,GAAG,QAAQ,OAAO,GAAG,OAAO,MAAA;AAAA,IAChD;AAEA,WAAO,yBAAyB,OAAO;AAAA,EAIzC;AACF;AAMA,SAAS,yBAIP,SAC8C;AAC9C,QAAM,aAAaE,MAAAA,iBAAiB,OAAc;AAMlD,QAAM,UAAUC,mBAAAA,qBAAqB,OAAO;AAC5C,MAAI,SAAS;AACXC,uBAAAA,0BAA0B,YAAY,OAAO;AAAA,EAC/C;AAEA,SAAO;AACT;;;"}
@@ -4,8 +4,8 @@ import { InitialQueryBuilder, QueryBuilder } from './builder/index.js';
4
4
  import { Collection } from '../collection/index.js';
5
5
  import { CollectionConfigSingleRowOption, NonSingleResult, SingleResult, UtilsRecord } from '../types.js';
6
6
  import { Context, GetResult } from './builder/types.js';
7
- type CollectionConfigForContext<TContext extends Context, TResult extends object> = TContext extends SingleResult ? CollectionConfigSingleRowOption<TResult> & SingleResult : CollectionConfigSingleRowOption<TResult> & NonSingleResult;
8
- type CollectionForContext<TContext extends Context, TResult extends object> = TContext extends SingleResult ? Collection<TResult> & SingleResult : Collection<TResult> & NonSingleResult;
7
+ type CollectionConfigForContext<TContext extends Context, TResult extends object, TUtils extends UtilsRecord = {}> = TContext extends SingleResult ? CollectionConfigSingleRowOption<TResult, string | number, never, TUtils> & SingleResult : CollectionConfigSingleRowOption<TResult, string | number, never, TUtils> & NonSingleResult;
8
+ type CollectionForContext<TContext extends Context, TResult extends object, TUtils extends UtilsRecord = {}> = TContext extends SingleResult ? Collection<TResult, string | number, TUtils> & SingleResult : Collection<TResult, string | number, TUtils> & NonSingleResult;
9
9
  /**
10
10
  * Creates live query collection options for use with createCollection
11
11
  *
@@ -3,6 +3,7 @@ import { Collection } from './collection/index.js';
3
3
  import { StandardSchemaV1 } from '@standard-schema/spec';
4
4
  import { Transaction } from './transactions.cjs';
5
5
  import { BasicExpression, OrderBy } from './query/ir.js';
6
+ import { EventEmitter } from './event-emitter.js';
6
7
  /**
7
8
  * Helper type to extract the output type from a standard schema
8
9
  *
@@ -92,15 +93,74 @@ type Value<TExtensions = never> = string | number | boolean | bigint | null | TE
92
93
  };
93
94
  export type Row<TExtensions = never> = Record<string, Value<TExtensions>>;
94
95
  export type OperationType = `insert` | `update` | `delete`;
95
- export type OnLoadMoreOptions = {
96
+ /**
97
+ * Subscription status values
98
+ */
99
+ export type SubscriptionStatus = `ready` | `loadingSubset`;
100
+ /**
101
+ * Event emitted when subscription status changes
102
+ */
103
+ export interface SubscriptionStatusChangeEvent {
104
+ type: `status:change`;
105
+ subscription: Subscription;
106
+ previousStatus: SubscriptionStatus;
107
+ status: SubscriptionStatus;
108
+ }
109
+ /**
110
+ * Event emitted when subscription status changes to a specific status
111
+ */
112
+ export interface SubscriptionStatusEvent<T extends SubscriptionStatus> {
113
+ type: `status:${T}`;
114
+ subscription: Subscription;
115
+ previousStatus: SubscriptionStatus;
116
+ status: T;
117
+ }
118
+ /**
119
+ * Event emitted when subscription is unsubscribed
120
+ */
121
+ export interface SubscriptionUnsubscribedEvent {
122
+ type: `unsubscribed`;
123
+ subscription: Subscription;
124
+ }
125
+ /**
126
+ * All subscription events
127
+ */
128
+ export type SubscriptionEvents = {
129
+ "status:change": SubscriptionStatusChangeEvent;
130
+ "status:ready": SubscriptionStatusEvent<`ready`>;
131
+ "status:loadingSubset": SubscriptionStatusEvent<`loadingSubset`>;
132
+ unsubscribed: SubscriptionUnsubscribedEvent;
133
+ };
134
+ /**
135
+ * Public interface for a collection subscription
136
+ * Used by sync implementations to track subscription lifecycle
137
+ */
138
+ export interface Subscription extends EventEmitter<SubscriptionEvents> {
139
+ /** Current status of the subscription */
140
+ readonly status: SubscriptionStatus;
141
+ }
142
+ export type LoadSubsetOptions = {
143
+ /** The where expression to filter the data */
96
144
  where?: BasicExpression<boolean>;
145
+ /** The order by clause to sort the data */
97
146
  orderBy?: OrderBy;
147
+ /** The limit of the data to load */
98
148
  limit?: number;
149
+ /**
150
+ * The subscription that triggered the load.
151
+ * Advanced sync implementations can use this for:
152
+ * - LRU caching keyed by subscription
153
+ * - Reference counting to track active subscriptions
154
+ * - Subscribing to subscription events (e.g., finalization/unsubscribe)
155
+ * @optional Available when called from CollectionSubscription, may be undefined for direct calls
156
+ */
157
+ subscription?: Subscription;
99
158
  };
159
+ export type LoadSubsetFn = (options: LoadSubsetOptions) => true | Promise<void>;
100
160
  export type CleanupFn = () => void;
101
161
  export type SyncConfigRes = {
102
162
  cleanup?: CleanupFn;
103
- onLoadMore?: (options: OnLoadMoreOptions) => void | Promise<void>;
163
+ loadSubset?: LoadSubsetFn;
104
164
  };
105
165
  export interface SyncConfig<T extends object = Record<string, unknown>, TKey extends string | number = string | number> {
106
166
  sync: (params: {
@@ -161,21 +221,21 @@ export interface InsertConfig {
161
221
  /** Whether to apply optimistic updates immediately. Defaults to true. */
162
222
  optimistic?: boolean;
163
223
  }
164
- export type UpdateMutationFnParams<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>> = {
224
+ export type UpdateMutationFnParams<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord> = {
165
225
  transaction: TransactionWithMutations<T, `update`>;
166
226
  collection: Collection<T, TKey, TUtils>;
167
227
  };
168
- export type InsertMutationFnParams<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>> = {
228
+ export type InsertMutationFnParams<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord> = {
169
229
  transaction: TransactionWithMutations<T, `insert`>;
170
230
  collection: Collection<T, TKey, TUtils>;
171
231
  };
172
- export type DeleteMutationFnParams<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>> = {
232
+ export type DeleteMutationFnParams<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord> = {
173
233
  transaction: TransactionWithMutations<T, `delete`>;
174
234
  collection: Collection<T, TKey, TUtils>;
175
235
  };
176
- export type InsertMutationFn<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>, TReturn = any> = (params: InsertMutationFnParams<T, TKey, TUtils>) => Promise<TReturn>;
177
- export type UpdateMutationFn<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>, TReturn = any> = (params: UpdateMutationFnParams<T, TKey, TUtils>) => Promise<TReturn>;
178
- export type DeleteMutationFn<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>, TReturn = any> = (params: DeleteMutationFnParams<T, TKey, TUtils>) => Promise<TReturn>;
236
+ export type InsertMutationFn<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord, TReturn = any> = (params: InsertMutationFnParams<T, TKey, TUtils>) => Promise<TReturn>;
237
+ export type UpdateMutationFn<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord, TReturn = any> = (params: UpdateMutationFnParams<T, TKey, TUtils>) => Promise<TReturn>;
238
+ export type DeleteMutationFn<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord, TReturn = any> = (params: DeleteMutationFnParams<T, TKey, TUtils>) => Promise<TReturn>;
179
239
  /**
180
240
  * Collection status values for lifecycle management
181
241
  * @example
@@ -202,7 +262,8 @@ export type CollectionStatus =
202
262
  | `error`
203
263
  /** Collection has been cleaned up and resources freed */
204
264
  | `cleaned-up`;
205
- export interface BaseCollectionConfig<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = Record<string, Fn>, TReturn = any> {
265
+ export type SyncMode = `eager` | `on-demand`;
266
+ export interface BaseCollectionConfig<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = UtilsRecord, TReturn = any> {
206
267
  id?: string;
207
268
  schema?: TSchema;
208
269
  /**
@@ -251,6 +312,15 @@ export interface BaseCollectionConfig<T extends object = Record<string, unknown>
251
312
  * compare: (x, y) => x.createdAt.getTime() - y.createdAt.getTime()
252
313
  */
253
314
  compare?: (x: T, y: T) => number;
315
+ /**
316
+ * The mode of sync to use for the collection.
317
+ * @default `eager`
318
+ * @description
319
+ * - `eager`: syncs all data immediately on preload
320
+ * - `on-demand`: syncs data in incremental snapshots when the collection is queried
321
+ * The exact implementation of the sync mode is up to the sync implementation.
322
+ */
323
+ syncMode?: SyncMode;
254
324
  /**
255
325
  * Optional asynchronous handler function called before an insert operation
256
326
  * @param params Object containing transaction and collection information
@@ -379,8 +449,9 @@ export interface BaseCollectionConfig<T extends object = Record<string, unknown>
379
449
  * }
380
450
  */
381
451
  onDelete?: DeleteMutationFn<T, TKey, TUtils, TReturn>;
452
+ utils?: TUtils;
382
453
  }
383
- export interface CollectionConfig<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = never> extends BaseCollectionConfig<T, TKey, TSchema> {
454
+ export interface CollectionConfig<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = UtilsRecord> extends BaseCollectionConfig<T, TKey, TSchema, TUtils> {
384
455
  sync: SyncConfig<T, TKey>;
385
456
  }
386
457
  export type SingleResult = {
@@ -395,7 +466,7 @@ export type MaybeSingleResult = {
395
466
  */
396
467
  singleResult?: true;
397
468
  };
398
- export type CollectionConfigSingleRowOption<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = never> = CollectionConfig<T, TKey, TSchema> & MaybeSingleResult;
469
+ export type CollectionConfigSingleRowOption<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = never, TUtils extends UtilsRecord = {}> = CollectionConfig<T, TKey, TSchema, TUtils> & MaybeSingleResult;
399
470
  export type ChangesPayload<T extends object = Record<string, unknown>> = Array<ChangeMessage<T>>;
400
471
  /**
401
472
  * An input row from a collection
@@ -1,3 +1,4 @@
1
+ import { EventEmitter } from '../event-emitter.js';
1
2
  import { Collection } from './index.js';
2
3
  import { CollectionStatus } from '../types.js';
3
4
  /**
@@ -27,25 +28,35 @@ export interface CollectionSubscribersChangeEvent {
27
28
  previousSubscriberCount: number;
28
29
  subscriberCount: number;
29
30
  }
31
+ /**
32
+ * Event emitted when the collection's loading more state changes
33
+ */
34
+ export interface CollectionLoadingSubsetChangeEvent {
35
+ type: `loadingSubset:change`;
36
+ collection: Collection<any, any, any, any, any>;
37
+ isLoadingSubset: boolean;
38
+ previousIsLoadingSubset: boolean;
39
+ loadingSubsetTransition: `start` | `end`;
40
+ }
30
41
  export type AllCollectionEvents = {
31
42
  "status:change": CollectionStatusChangeEvent;
32
43
  "subscribers:change": CollectionSubscribersChangeEvent;
44
+ "loadingSubset:change": CollectionLoadingSubsetChangeEvent;
33
45
  } & {
34
46
  [K in CollectionStatus as `status:${K}`]: CollectionStatusEvent<K>;
35
47
  };
36
- export type CollectionEvent = AllCollectionEvents[keyof AllCollectionEvents] | CollectionStatusChangeEvent | CollectionSubscribersChangeEvent;
48
+ export type CollectionEvent = AllCollectionEvents[keyof AllCollectionEvents] | CollectionStatusChangeEvent | CollectionSubscribersChangeEvent | CollectionLoadingSubsetChangeEvent;
37
49
  export type CollectionEventHandler<T extends keyof AllCollectionEvents> = (event: AllCollectionEvents[T]) => void;
38
- export declare class CollectionEventsManager {
50
+ export declare class CollectionEventsManager extends EventEmitter<AllCollectionEvents> {
39
51
  private collection;
40
- private listeners;
41
52
  constructor();
42
53
  setDeps(deps: {
43
54
  collection: Collection<any, any, any, any, any>;
44
55
  }): void;
45
- on<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): () => void;
46
- once<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): () => void;
47
- off<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): void;
48
- waitFor<T extends keyof AllCollectionEvents>(event: T, timeout?: number): Promise<AllCollectionEvents[T]>;
56
+ /**
57
+ * Emit an event to all listeners
58
+ * Public API for emitting collection events
59
+ */
49
60
  emit<T extends keyof AllCollectionEvents>(event: T, eventPayload: AllCollectionEvents[T]): void;
50
61
  emitStatusChange<T extends CollectionStatus>(status: T, previousStatus: CollectionStatus): void;
51
62
  emitSubscribersChange(subscriberCount: number, previousSubscriberCount: number): void;
@@ -1,59 +1,17 @@
1
- class CollectionEventsManager {
1
+ import { EventEmitter } from "../event-emitter.js";
2
+ class CollectionEventsManager extends EventEmitter {
2
3
  constructor() {
3
- this.listeners = /* @__PURE__ */ new Map();
4
+ super();
4
5
  }
5
6
  setDeps(deps) {
6
7
  this.collection = deps.collection;
7
8
  }
8
- on(event, callback) {
9
- if (!this.listeners.has(event)) {
10
- this.listeners.set(event, /* @__PURE__ */ new Set());
11
- }
12
- this.listeners.get(event).add(callback);
13
- return () => {
14
- this.listeners.get(event)?.delete(callback);
15
- };
16
- }
17
- once(event, callback) {
18
- const unsubscribe = this.on(event, (eventPayload) => {
19
- callback(eventPayload);
20
- unsubscribe();
21
- });
22
- return unsubscribe;
23
- }
24
- off(event, callback) {
25
- this.listeners.get(event)?.delete(callback);
26
- }
27
- waitFor(event, timeout) {
28
- return new Promise((resolve, reject) => {
29
- let timeoutId;
30
- const unsubscribe = this.on(event, (eventPayload) => {
31
- if (timeoutId) {
32
- clearTimeout(timeoutId);
33
- timeoutId = void 0;
34
- }
35
- resolve(eventPayload);
36
- unsubscribe();
37
- });
38
- if (timeout) {
39
- timeoutId = setTimeout(() => {
40
- timeoutId = void 0;
41
- unsubscribe();
42
- reject(new Error(`Timeout waiting for event ${event}`));
43
- }, timeout);
44
- }
45
- });
46
- }
9
+ /**
10
+ * Emit an event to all listeners
11
+ * Public API for emitting collection events
12
+ */
47
13
  emit(event, eventPayload) {
48
- this.listeners.get(event)?.forEach((listener) => {
49
- try {
50
- listener(eventPayload);
51
- } catch (error) {
52
- queueMicrotask(() => {
53
- throw error;
54
- });
55
- }
56
- });
14
+ this.emitInner(event, eventPayload);
57
15
  }
58
16
  emitStatusChange(status, previousStatus) {
59
17
  this.emit(`status:change`, {
@@ -79,7 +37,7 @@ class CollectionEventsManager {
79
37
  });
80
38
  }
81
39
  cleanup() {
82
- this.listeners.clear();
40
+ this.clearListeners();
83
41
  }
84
42
  }
85
43
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sources":["../../../src/collection/events.ts"],"sourcesContent":["import type { Collection } from \"./index.js\"\nimport type { CollectionStatus } from \"../types.js\"\n\n/**\n * Event emitted when the collection status changes\n */\nexport interface CollectionStatusChangeEvent {\n type: `status:change`\n collection: Collection\n previousStatus: CollectionStatus\n status: CollectionStatus\n}\n\n/**\n * Event emitted when the collection status changes to a specific status\n */\nexport interface CollectionStatusEvent<T extends CollectionStatus> {\n type: `status:${T}`\n collection: Collection\n previousStatus: CollectionStatus\n status: T\n}\n\n/**\n * Event emitted when the number of subscribers to the collection changes\n */\nexport interface CollectionSubscribersChangeEvent {\n type: `subscribers:change`\n collection: Collection\n previousSubscriberCount: number\n subscriberCount: number\n}\n\nexport type AllCollectionEvents = {\n \"status:change\": CollectionStatusChangeEvent\n \"subscribers:change\": CollectionSubscribersChangeEvent\n} & {\n [K in CollectionStatus as `status:${K}`]: CollectionStatusEvent<K>\n}\n\nexport type CollectionEvent =\n | AllCollectionEvents[keyof AllCollectionEvents]\n | CollectionStatusChangeEvent\n | CollectionSubscribersChangeEvent\n\nexport type CollectionEventHandler<T extends keyof AllCollectionEvents> = (\n event: AllCollectionEvents[T]\n) => void\n\nexport class CollectionEventsManager {\n private collection!: Collection<any, any, any, any, any>\n private listeners = new Map<\n keyof AllCollectionEvents,\n Set<CollectionEventHandler<any>>\n >()\n\n constructor() {}\n\n setDeps(deps: { collection: Collection<any, any, any, any, any> }) {\n this.collection = deps.collection\n }\n\n on<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>\n ) {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set())\n }\n this.listeners.get(event)!.add(callback)\n\n return () => {\n this.listeners.get(event)?.delete(callback)\n }\n }\n\n once<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>\n ) {\n const unsubscribe = this.on(event, (eventPayload) => {\n callback(eventPayload)\n unsubscribe()\n })\n return unsubscribe\n }\n\n off<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>\n ) {\n this.listeners.get(event)?.delete(callback)\n }\n\n waitFor<T extends keyof AllCollectionEvents>(\n event: T,\n timeout?: number\n ): Promise<AllCollectionEvents[T]> {\n return new Promise((resolve, reject) => {\n let timeoutId: NodeJS.Timeout | undefined\n const unsubscribe = this.on(event, (eventPayload) => {\n if (timeoutId) {\n clearTimeout(timeoutId)\n timeoutId = undefined\n }\n resolve(eventPayload)\n unsubscribe()\n })\n if (timeout) {\n timeoutId = setTimeout(() => {\n timeoutId = undefined\n unsubscribe()\n reject(new Error(`Timeout waiting for event ${event}`))\n }, timeout)\n }\n })\n }\n\n emit<T extends keyof AllCollectionEvents>(\n event: T,\n eventPayload: AllCollectionEvents[T]\n ) {\n this.listeners.get(event)?.forEach((listener) => {\n try {\n listener(eventPayload)\n } catch (error) {\n // Re-throw in a microtask to surface the error\n queueMicrotask(() => {\n throw error\n })\n }\n })\n }\n\n emitStatusChange<T extends CollectionStatus>(\n status: T,\n previousStatus: CollectionStatus\n ) {\n this.emit(`status:change`, {\n type: `status:change`,\n collection: this.collection,\n previousStatus,\n status,\n })\n\n // Emit specific status event using type assertion\n const eventKey: `status:${T}` = `status:${status}`\n this.emit(eventKey, {\n type: eventKey,\n collection: this.collection,\n previousStatus,\n status,\n } as AllCollectionEvents[`status:${T}`])\n }\n\n emitSubscribersChange(\n subscriberCount: number,\n previousSubscriberCount: number\n ) {\n this.emit(`subscribers:change`, {\n type: `subscribers:change`,\n collection: this.collection,\n previousSubscriberCount,\n subscriberCount,\n })\n }\n\n cleanup() {\n this.listeners.clear()\n }\n}\n"],"names":[],"mappings":"AAiDO,MAAM,wBAAwB;AAAA,EAOnC,cAAc;AALd,SAAQ,gCAAgB,IAAA;AAAA,EAKT;AAAA,EAEf,QAAQ,MAA2D;AACjE,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA,EAEA,GACE,OACA,UACA;AACA,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,oBAAI,KAAK;AAAA,IACrC;AACA,SAAK,UAAU,IAAI,KAAK,EAAG,IAAI,QAAQ;AAEvC,WAAO,MAAM;AACX,WAAK,UAAU,IAAI,KAAK,GAAG,OAAO,QAAQ;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,KACE,OACA,UACA;AACA,UAAM,cAAc,KAAK,GAAG,OAAO,CAAC,iBAAiB;AACnD,eAAS,YAAY;AACrB,kBAAA;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,IACE,OACA,UACA;AACA,SAAK,UAAU,IAAI,KAAK,GAAG,OAAO,QAAQ;AAAA,EAC5C;AAAA,EAEA,QACE,OACA,SACiC;AACjC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI;AACJ,YAAM,cAAc,KAAK,GAAG,OAAO,CAAC,iBAAiB;AACnD,YAAI,WAAW;AACb,uBAAa,SAAS;AACtB,sBAAY;AAAA,QACd;AACA,gBAAQ,YAAY;AACpB,oBAAA;AAAA,MACF,CAAC;AACD,UAAI,SAAS;AACX,oBAAY,WAAW,MAAM;AAC3B,sBAAY;AACZ,sBAAA;AACA,iBAAO,IAAI,MAAM,6BAA6B,KAAK,EAAE,CAAC;AAAA,QACxD,GAAG,OAAO;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,KACE,OACA,cACA;AACA,SAAK,UAAU,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa;AAC/C,UAAI;AACF,iBAAS,YAAY;AAAA,MACvB,SAAS,OAAO;AAEd,uBAAe,MAAM;AACnB,gBAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,iBACE,QACA,gBACA;AACA,SAAK,KAAK,iBAAiB;AAAA,MACzB,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACD;AAGD,UAAM,WAA0B,UAAU,MAAM;AAChD,SAAK,KAAK,UAAU;AAAA,MAClB,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACqC;AAAA,EACzC;AAAA,EAEA,sBACE,iBACA,yBACA;AACA,SAAK,KAAK,sBAAsB;AAAA,MAC9B,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEA,UAAU;AACR,SAAK,UAAU,MAAA;AAAA,EACjB;AACF;"}
1
+ {"version":3,"file":"events.js","sources":["../../../src/collection/events.ts"],"sourcesContent":["import { EventEmitter } from \"../event-emitter.js\"\nimport type { Collection } from \"./index.js\"\nimport type { CollectionStatus } from \"../types.js\"\n\n/**\n * Event emitted when the collection status changes\n */\nexport interface CollectionStatusChangeEvent {\n type: `status:change`\n collection: Collection\n previousStatus: CollectionStatus\n status: CollectionStatus\n}\n\n/**\n * Event emitted when the collection status changes to a specific status\n */\nexport interface CollectionStatusEvent<T extends CollectionStatus> {\n type: `status:${T}`\n collection: Collection\n previousStatus: CollectionStatus\n status: T\n}\n\n/**\n * Event emitted when the number of subscribers to the collection changes\n */\nexport interface CollectionSubscribersChangeEvent {\n type: `subscribers:change`\n collection: Collection\n previousSubscriberCount: number\n subscriberCount: number\n}\n\n/**\n * Event emitted when the collection's loading more state changes\n */\nexport interface CollectionLoadingSubsetChangeEvent {\n type: `loadingSubset:change`\n collection: Collection<any, any, any, any, any>\n isLoadingSubset: boolean\n previousIsLoadingSubset: boolean\n loadingSubsetTransition: `start` | `end`\n}\n\nexport type AllCollectionEvents = {\n \"status:change\": CollectionStatusChangeEvent\n \"subscribers:change\": CollectionSubscribersChangeEvent\n \"loadingSubset:change\": CollectionLoadingSubsetChangeEvent\n} & {\n [K in CollectionStatus as `status:${K}`]: CollectionStatusEvent<K>\n}\n\nexport type CollectionEvent =\n | AllCollectionEvents[keyof AllCollectionEvents]\n | CollectionStatusChangeEvent\n | CollectionSubscribersChangeEvent\n | CollectionLoadingSubsetChangeEvent\n\nexport type CollectionEventHandler<T extends keyof AllCollectionEvents> = (\n event: AllCollectionEvents[T]\n) => void\n\nexport class CollectionEventsManager extends EventEmitter<AllCollectionEvents> {\n private collection!: Collection<any, any, any, any, any>\n\n constructor() {\n super()\n }\n\n setDeps(deps: { collection: Collection<any, any, any, any, any> }) {\n this.collection = deps.collection\n }\n\n /**\n * Emit an event to all listeners\n * Public API for emitting collection events\n */\n emit<T extends keyof AllCollectionEvents>(\n event: T,\n eventPayload: AllCollectionEvents[T]\n ): void {\n this.emitInner(event, eventPayload)\n }\n\n emitStatusChange<T extends CollectionStatus>(\n status: T,\n previousStatus: CollectionStatus\n ) {\n this.emit(`status:change`, {\n type: `status:change`,\n collection: this.collection,\n previousStatus,\n status,\n })\n\n // Emit specific status event using type assertion\n const eventKey: `status:${T}` = `status:${status}`\n this.emit(eventKey, {\n type: eventKey,\n collection: this.collection,\n previousStatus,\n status,\n } as AllCollectionEvents[`status:${T}`])\n }\n\n emitSubscribersChange(\n subscriberCount: number,\n previousSubscriberCount: number\n ) {\n this.emit(`subscribers:change`, {\n type: `subscribers:change`,\n collection: this.collection,\n previousSubscriberCount,\n subscriberCount,\n })\n }\n\n cleanup() {\n this.clearListeners()\n }\n}\n"],"names":[],"mappings":";AA+DO,MAAM,gCAAgC,aAAkC;AAAA,EAG7E,cAAc;AACZ,UAAA;AAAA,EACF;AAAA,EAEA,QAAQ,MAA2D;AACjE,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KACE,OACA,cACM;AACN,SAAK,UAAU,OAAO,YAAY;AAAA,EACpC;AAAA,EAEA,iBACE,QACA,gBACA;AACA,SAAK,KAAK,iBAAiB;AAAA,MACzB,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACD;AAGD,UAAM,WAA0B,UAAU,MAAM;AAChD,SAAK,KAAK,UAAU;AAAA,MAClB,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACqC;AAAA,EACzC;AAAA,EAEA,sBACE,iBACA,yBACA;AACA,SAAK,KAAK,sBAAsB;AAAA,MAC9B,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEA,UAAU;AACR,SAAK,eAAA;AAAA,EACP;AACF;"}
@@ -1,10 +1,11 @@
1
1
  import { CollectionStateManager } from './state.js';
2
2
  import { CollectionLifecycleManager } from './lifecycle.js';
3
+ import { CollectionSyncManager } from './sync.js';
3
4
  import { CollectionSubscription } from './subscription.js';
4
5
  import { AllCollectionEvents, CollectionEventHandler } from './events.js';
5
6
  import { BaseIndex, IndexResolver } from '../indexes/base-index.js';
6
7
  import { IndexOptions } from '../indexes/index-options.js';
7
- import { ChangeMessage, CollectionConfig, CollectionStatus, CurrentStateAsChangesOptions, Fn, InferSchemaInput, InferSchemaOutput, InsertConfig, NonSingleResult, OnLoadMoreOptions, OperationConfig, SingleResult, SubscribeChangesOptions, Transaction as TransactionType, UtilsRecord, WritableDeep } from '../types.js';
8
+ import { ChangeMessage, CollectionConfig, CollectionStatus, CurrentStateAsChangesOptions, Fn, InferSchemaInput, InferSchemaOutput, InsertConfig, NonSingleResult, OperationConfig, SingleResult, SubscribeChangesOptions, Transaction as TransactionType, UtilsRecord, WritableDeep } from '../types.js';
8
9
  import { SingleRowRefProxy } from '../query/builder/ref-proxy.js';
9
10
  import { StandardSchemaV1 } from '@standard-schema/spec';
10
11
  import { BTreeIndex } from '../indexes/btree-index.js';
@@ -16,7 +17,7 @@ import { IndexProxy } from '../indexes/lazy-index.js';
16
17
  * @template TUtils - The utilities record type
17
18
  * @template TInsertInput - The type for insert operations (can be different from T for schemas with defaults)
18
19
  */
19
- export interface Collection<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}, TSchema extends StandardSchemaV1 = StandardSchemaV1, TInsertInput extends object = T> extends CollectionImpl<T, TKey, TUtils, TSchema, TInsertInput> {
20
+ export interface Collection<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord, TSchema extends StandardSchemaV1 = StandardSchemaV1, TInsertInput extends object = T> extends CollectionImpl<T, TKey, TUtils, TSchema, TInsertInput> {
20
21
  readonly utils: TUtils;
21
22
  readonly singleResult?: true;
22
23
  }
@@ -90,19 +91,19 @@ export interface Collection<T extends object = Record<string, unknown>, TKey ext
90
91
  * })
91
92
  *
92
93
  */
93
- export declare function createCollection<T extends StandardSchemaV1, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}>(options: CollectionConfig<InferSchemaOutput<T>, TKey, T> & {
94
+ export declare function createCollection<T extends StandardSchemaV1, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord>(options: CollectionConfig<InferSchemaOutput<T>, TKey, T> & {
94
95
  schema: T;
95
96
  utils?: TUtils;
96
97
  } & NonSingleResult): Collection<InferSchemaOutput<T>, TKey, TUtils, T, InferSchemaInput<T>> & NonSingleResult;
97
- export declare function createCollection<T extends StandardSchemaV1, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}>(options: CollectionConfig<InferSchemaOutput<T>, TKey, T> & {
98
+ export declare function createCollection<T extends StandardSchemaV1, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord>(options: CollectionConfig<InferSchemaOutput<T>, TKey, T> & {
98
99
  schema: T;
99
100
  utils?: TUtils;
100
101
  } & SingleResult): Collection<InferSchemaOutput<T>, TKey, TUtils, T, InferSchemaInput<T>> & SingleResult;
101
- export declare function createCollection<T extends object, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}>(options: CollectionConfig<T, TKey, never> & {
102
+ export declare function createCollection<T extends object, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord>(options: CollectionConfig<T, TKey, never> & {
102
103
  schema?: never;
103
104
  utils?: TUtils;
104
105
  } & NonSingleResult): Collection<T, TKey, TUtils, never, T> & NonSingleResult;
105
- export declare function createCollection<T extends object, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}>(options: CollectionConfig<T, TKey, never> & {
106
+ export declare function createCollection<T extends object, TKey extends string | number = string | number, TUtils extends UtilsRecord = UtilsRecord>(options: CollectionConfig<T, TKey, never> & {
106
107
  schema?: never;
107
108
  utils?: TUtils;
108
109
  } & SingleResult): Collection<T, TKey, TUtils, never, T> & SingleResult;
@@ -113,7 +114,7 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
113
114
  private _events;
114
115
  private _changes;
115
116
  _lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>;
116
- private _sync;
117
+ _sync: CollectionSyncManager<TOutput, TKey, TSchema, TInput>;
117
118
  private _indexes;
118
119
  private _mutations;
119
120
  _state: CollectionStateManager<TOutput, TKey, TSchema, TInput>;
@@ -156,18 +157,16 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
156
157
  * }
157
158
  */
158
159
  isReady(): boolean;
160
+ /**
161
+ * Check if the collection is currently loading more data
162
+ * @returns true if the collection has pending load more operations, false otherwise
163
+ */
164
+ get isLoadingSubset(): boolean;
159
165
  /**
160
166
  * Start sync immediately - internal method for compiled queries
161
167
  * This bypasses lazy loading for special cases like live query results
162
168
  */
163
169
  startSyncImmediate(): void;
164
- /**
165
- * Requests the sync layer to load more data.
166
- * @param options Options to control what data is being loaded
167
- * @returns If data loading is asynchronous, this method returns a promise that resolves when the data is loaded.
168
- * If data loading is synchronous, the data is loaded when the method returns.
169
- */
170
- syncMore(options: OnLoadMoreOptions): void | Promise<void>;
171
170
  /**
172
171
  * Preload the collection data by starting sync if not already started
173
172
  * Multiple concurrent calls will share the same promise
@@ -94,7 +94,8 @@ class CollectionImpl {
94
94
  collection: this,
95
95
  // Required for passing to config.sync callback
96
96
  state: this._state,
97
- lifecycle: this._lifecycle
97
+ lifecycle: this._lifecycle,
98
+ events: this._events
98
99
  });
99
100
  if (config.startSync === true) {
100
101
  this._sync.startSync();
@@ -140,6 +141,13 @@ class CollectionImpl {
140
141
  isReady() {
141
142
  return this._lifecycle.status === `ready`;
142
143
  }
144
+ /**
145
+ * Check if the collection is currently loading more data
146
+ * @returns true if the collection has pending load more operations, false otherwise
147
+ */
148
+ get isLoadingSubset() {
149
+ return this._sync.isLoadingSubset;
150
+ }
143
151
  /**
144
152
  * Start sync immediately - internal method for compiled queries
145
153
  * This bypasses lazy loading for special cases like live query results
@@ -147,17 +155,6 @@ class CollectionImpl {
147
155
  startSyncImmediate() {
148
156
  this._sync.startSync();
149
157
  }
150
- /**
151
- * Requests the sync layer to load more data.
152
- * @param options Options to control what data is being loaded
153
- * @returns If data loading is asynchronous, this method returns a promise that resolves when the data is loaded.
154
- * If data loading is synchronous, the data is loaded when the method returns.
155
- */
156
- syncMore(options) {
157
- if (this._sync.syncOnLoadMoreFn) {
158
- return this._sync.syncOnLoadMoreFn(options);
159
- }
160
- }
161
158
  /**
162
159
  * Preload the collection data by starting sync if not already started
163
160
  * Multiple concurrent calls will share the same promise