@hops-ops/distributed 0.0.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 (242) hide show
  1. package/README.md +454 -0
  2. package/dist/auth-headers.d.ts +8 -0
  3. package/dist/auth-headers.js +35 -0
  4. package/dist/diagnostics.d.ts +2 -0
  5. package/dist/diagnostics.js +1 -0
  6. package/dist/document.d.ts +8 -0
  7. package/dist/document.js +6 -0
  8. package/dist/identity.d.ts +23 -0
  9. package/dist/identity.js +73 -0
  10. package/dist/index.d.ts +7 -0
  11. package/dist/index.js +6 -0
  12. package/dist/internal/cache-engine/create.d.ts +5 -0
  13. package/dist/internal/cache-engine/create.js +7 -0
  14. package/dist/internal/cache-engine/engine.d.ts +32 -0
  15. package/dist/internal/cache-engine/engine.js +1287 -0
  16. package/dist/internal/cache-engine/errors.d.ts +5 -0
  17. package/dist/internal/cache-engine/errors.js +10 -0
  18. package/dist/internal/cache-engine/helpers.d.ts +55 -0
  19. package/dist/internal/cache-engine/helpers.js +545 -0
  20. package/dist/internal/cache-engine/index.d.ts +3 -0
  21. package/dist/internal/cache-engine/index.js +2 -0
  22. package/dist/internal/cache-engine/types.d.ts +300 -0
  23. package/dist/internal/cache-engine/types.js +1 -0
  24. package/dist/internal/cache-engine.d.ts +3 -0
  25. package/dist/internal/cache-engine.js +2 -0
  26. package/dist/lib/assert-name.d.ts +2 -0
  27. package/dist/lib/assert-name.js +6 -0
  28. package/dist/lib/compare-code-units.d.ts +2 -0
  29. package/dist/lib/compare-code-units.js +4 -0
  30. package/dist/lib/deep-equal.d.ts +2 -0
  31. package/dist/lib/deep-equal.js +24 -0
  32. package/dist/lib/freeze-record.d.ts +2 -0
  33. package/dist/lib/freeze-record.js +13 -0
  34. package/dist/lib/index.d.ts +6 -0
  35. package/dist/lib/index.js +6 -0
  36. package/dist/lib/is-plain-record.d.ts +2 -0
  37. package/dist/lib/is-plain-record.js +8 -0
  38. package/dist/lib/report.d.ts +7 -0
  39. package/dist/lib/report.js +26 -0
  40. package/dist/protocol.d.ts +179 -0
  41. package/dist/protocol.js +558 -0
  42. package/dist/react/context.d.ts +20 -0
  43. package/dist/react/context.js +24 -0
  44. package/dist/react/index.d.ts +2 -0
  45. package/dist/react/index.js +3 -0
  46. package/dist/react/query.d.ts +20 -0
  47. package/dist/react/query.js +99 -0
  48. package/dist/replica/command-id.d.ts +2 -0
  49. package/dist/replica/command-id.js +19 -0
  50. package/dist/replica/command-runtime/constants.d.ts +5 -0
  51. package/dist/replica/command-runtime/constants.js +5 -0
  52. package/dist/replica/command-runtime/create.d.ts +2 -0
  53. package/dist/replica/command-runtime/create.js +644 -0
  54. package/dist/replica/command-runtime/errors.d.ts +16 -0
  55. package/dist/replica/command-runtime/errors.js +41 -0
  56. package/dist/replica/command-runtime/index.d.ts +5 -0
  57. package/dist/replica/command-runtime/index.js +4 -0
  58. package/dist/replica/command-runtime/lib/binding.d.ts +4 -0
  59. package/dist/replica/command-runtime/lib/binding.js +64 -0
  60. package/dist/replica/command-runtime/lib/effects.d.ts +11 -0
  61. package/dist/replica/command-runtime/lib/effects.js +89 -0
  62. package/dist/replica/command-runtime/lib/index.d.ts +9 -0
  63. package/dist/replica/command-runtime/lib/index.js +9 -0
  64. package/dist/replica/command-runtime/lib/inventory.d.ts +10 -0
  65. package/dist/replica/command-runtime/lib/inventory.js +141 -0
  66. package/dist/replica/command-runtime/lib/output.d.ts +13 -0
  67. package/dist/replica/command-runtime/lib/output.js +190 -0
  68. package/dist/replica/command-runtime/lib/projection.d.ts +20 -0
  69. package/dist/replica/command-runtime/lib/projection.js +255 -0
  70. package/dist/replica/command-runtime/lib/status.d.ts +7 -0
  71. package/dist/replica/command-runtime/lib/status.js +127 -0
  72. package/dist/replica/command-runtime/lib/transport.d.ts +16 -0
  73. package/dist/replica/command-runtime/lib/transport.js +112 -0
  74. package/dist/replica/command-runtime/lib/util.d.ts +16 -0
  75. package/dist/replica/command-runtime/lib/util.js +127 -0
  76. package/dist/replica/command-runtime/lifecycle.d.ts +11 -0
  77. package/dist/replica/command-runtime/lifecycle.js +13 -0
  78. package/dist/replica/command-runtime/symbols.d.ts +30 -0
  79. package/dist/replica/command-runtime/symbols.js +30 -0
  80. package/dist/replica/command-runtime/types.d.ts +222 -0
  81. package/dist/replica/command-runtime/types.js +1 -0
  82. package/dist/replica/command-runtime.d.ts +3 -0
  83. package/dist/replica/command-runtime.js +2 -0
  84. package/dist/replica/commands/clone.d.ts +12 -0
  85. package/dist/replica/commands/clone.js +187 -0
  86. package/dist/replica/commands/constants.d.ts +6 -0
  87. package/dist/replica/commands/constants.js +6 -0
  88. package/dist/replica/commands/errors.d.ts +7 -0
  89. package/dist/replica/commands/errors.js +22 -0
  90. package/dist/replica/commands/implementation.d.ts +8 -0
  91. package/dist/replica/commands/implementation.js +8 -0
  92. package/dist/replica/commands/index.d.ts +5 -0
  93. package/dist/replica/commands/index.js +4 -0
  94. package/dist/replica/commands/prepare.d.ts +27 -0
  95. package/dist/replica/commands/prepare.js +77 -0
  96. package/dist/replica/commands/presets.d.ts +12 -0
  97. package/dist/replica/commands/presets.js +84 -0
  98. package/dist/replica/commands/receipt.d.ts +3 -0
  99. package/dist/replica/commands/receipt.js +36 -0
  100. package/dist/replica/commands/resolve.d.ts +11 -0
  101. package/dist/replica/commands/resolve.js +165 -0
  102. package/dist/replica/commands/types.d.ts +278 -0
  103. package/dist/replica/commands/types.js +2 -0
  104. package/dist/replica/commands/util.d.ts +21 -0
  105. package/dist/replica/commands/util.js +96 -0
  106. package/dist/replica/commands/validate.d.ts +18 -0
  107. package/dist/replica/commands/validate.js +477 -0
  108. package/dist/replica/commands.d.ts +3 -0
  109. package/dist/replica/commands.js +2 -0
  110. package/dist/replica/diagnostics/event-log.d.ts +4 -0
  111. package/dist/replica/diagnostics/event-log.js +463 -0
  112. package/dist/replica/diagnostics/implementation.d.ts +4 -0
  113. package/dist/replica/diagnostics/implementation.js +4 -0
  114. package/dist/replica/diagnostics/index.d.ts +2 -0
  115. package/dist/replica/diagnostics/index.js +1 -0
  116. package/dist/replica/diagnostics/inspect.d.ts +6 -0
  117. package/dist/replica/diagnostics/inspect.js +170 -0
  118. package/dist/replica/diagnostics/types.d.ts +295 -0
  119. package/dist/replica/diagnostics/types.js +2 -0
  120. package/dist/replica/diagnostics.d.ts +3 -0
  121. package/dist/replica/diagnostics.js +2 -0
  122. package/dist/replica/distributed-replica/clocks.d.ts +23 -0
  123. package/dist/replica/distributed-replica/clocks.js +156 -0
  124. package/dist/replica/distributed-replica/constants.d.ts +12 -0
  125. package/dist/replica/distributed-replica/constants.js +10 -0
  126. package/dist/replica/distributed-replica/helpers.d.ts +48 -0
  127. package/dist/replica/distributed-replica/helpers.js +445 -0
  128. package/dist/replica/distributed-replica/hydration.d.ts +19 -0
  129. package/dist/replica/distributed-replica/hydration.js +380 -0
  130. package/dist/replica/distributed-replica/impl-diagnostics.d.ts +22 -0
  131. package/dist/replica/distributed-replica/impl-diagnostics.js +189 -0
  132. package/dist/replica/distributed-replica/impl-fetch-live.d.ts +39 -0
  133. package/dist/replica/distributed-replica/impl-fetch-live.js +395 -0
  134. package/dist/replica/distributed-replica/impl-hydration-orchestrate.d.ts +56 -0
  135. package/dist/replica/distributed-replica/impl-hydration-orchestrate.js +299 -0
  136. package/dist/replica/distributed-replica/impl-optimistic.d.ts +31 -0
  137. package/dist/replica/distributed-replica/impl-optimistic.js +172 -0
  138. package/dist/replica/distributed-replica/impl-protocol.d.ts +53 -0
  139. package/dist/replica/distributed-replica/impl-protocol.js +108 -0
  140. package/dist/replica/distributed-replica/impl.d.ts +44 -0
  141. package/dist/replica/distributed-replica/impl.js +1776 -0
  142. package/dist/replica/distributed-replica/index.d.ts +2 -0
  143. package/dist/replica/distributed-replica/index.js +4 -0
  144. package/dist/replica/distributed-replica/optimistic.d.ts +30 -0
  145. package/dist/replica/distributed-replica/optimistic.js +221 -0
  146. package/dist/replica/distributed-replica/types.d.ts +147 -0
  147. package/dist/replica/distributed-replica/types.js +1 -0
  148. package/dist/replica/distributed-replica/watch.d.ts +19 -0
  149. package/dist/replica/distributed-replica/watch.js +95 -0
  150. package/dist/replica/distributed-replica.d.ts +2 -0
  151. package/dist/replica/distributed-replica.js +2 -0
  152. package/dist/replica/graphql-transport.d.ts +24 -0
  153. package/dist/replica/graphql-transport.js +122 -0
  154. package/dist/replica/identity/clone.d.ts +6 -0
  155. package/dist/replica/identity/clone.js +54 -0
  156. package/dist/replica/identity/codec.d.ts +48 -0
  157. package/dist/replica/identity/codec.js +789 -0
  158. package/dist/replica/identity/constants.d.ts +3 -0
  159. package/dist/replica/identity/constants.js +18 -0
  160. package/dist/replica/identity/implementation.d.ts +5 -0
  161. package/dist/replica/identity/implementation.js +5 -0
  162. package/dist/replica/identity/index.d.ts +3 -0
  163. package/dist/replica/identity/index.js +3 -0
  164. package/dist/replica/identity/keys.d.ts +29 -0
  165. package/dist/replica/identity/keys.js +173 -0
  166. package/dist/replica/identity.d.ts +2 -0
  167. package/dist/replica/identity.js +2 -0
  168. package/dist/replica/index-maintenance/engine.d.ts +84 -0
  169. package/dist/replica/index-maintenance/engine.js +677 -0
  170. package/dist/replica/index-maintenance/implementation.d.ts +4 -0
  171. package/dist/replica/index-maintenance/implementation.js +4 -0
  172. package/dist/replica/index-maintenance/index.d.ts +2 -0
  173. package/dist/replica/index-maintenance/index.js +1 -0
  174. package/dist/replica/index-maintenance/registry.d.ts +2 -0
  175. package/dist/replica/index-maintenance/registry.js +49 -0
  176. package/dist/replica/index-maintenance/types.d.ts +58 -0
  177. package/dist/replica/index-maintenance/types.js +1 -0
  178. package/dist/replica/index-maintenance.d.ts +3 -0
  179. package/dist/replica/index-maintenance.js +2 -0
  180. package/dist/replica/index.d.ts +17 -0
  181. package/dist/replica/index.js +9 -0
  182. package/dist/replica/materialize.d.ts +10 -0
  183. package/dist/replica/materialize.js +163 -0
  184. package/dist/replica/normalize.d.ts +27 -0
  185. package/dist/replica/normalize.js +298 -0
  186. package/dist/replica/operation-binding.d.ts +14 -0
  187. package/dist/replica/operation-binding.js +76 -0
  188. package/dist/replica/persistence/idb.d.ts +20 -0
  189. package/dist/replica/persistence/idb.js +193 -0
  190. package/dist/replica/persistence/implementation.d.ts +4 -0
  191. package/dist/replica/persistence/implementation.js +4 -0
  192. package/dist/replica/persistence/index.d.ts +2 -0
  193. package/dist/replica/persistence/index.js +1 -0
  194. package/dist/replica/persistence/state.d.ts +104 -0
  195. package/dist/replica/persistence/state.js +734 -0
  196. package/dist/replica/persistence/types.d.ts +54 -0
  197. package/dist/replica/persistence/types.js +1 -0
  198. package/dist/replica/persistence.d.ts +3 -0
  199. package/dist/replica/persistence.js +2 -0
  200. package/dist/replica/query-plan/constants.d.ts +13 -0
  201. package/dist/replica/query-plan/constants.js +7 -0
  202. package/dist/replica/query-plan/filter.d.ts +39 -0
  203. package/dist/replica/query-plan/filter.js +544 -0
  204. package/dist/replica/query-plan/index.d.ts +4 -0
  205. package/dist/replica/query-plan/index.js +3 -0
  206. package/dist/replica/query-plan/order.d.ts +11 -0
  207. package/dist/replica/query-plan/order.js +158 -0
  208. package/dist/replica/query-plan/pagination.d.ts +6 -0
  209. package/dist/replica/query-plan/pagination.js +90 -0
  210. package/dist/replica/query-plan/resolve.d.ts +14 -0
  211. package/dist/replica/query-plan/resolve.js +163 -0
  212. package/dist/replica/query-plan/types.d.ts +89 -0
  213. package/dist/replica/query-plan/types.js +1 -0
  214. package/dist/replica/query-plan/util.d.ts +13 -0
  215. package/dist/replica/query-plan/util.js +62 -0
  216. package/dist/replica/query-plan.d.ts +3 -0
  217. package/dist/replica/query-plan.js +2 -0
  218. package/dist/replica/revalidation.d.ts +9 -0
  219. package/dist/replica/revalidation.js +86 -0
  220. package/dist/replica/selection.d.ts +20 -0
  221. package/dist/replica/selection.js +109 -0
  222. package/dist/replica/types.d.ts +657 -0
  223. package/dist/replica/types.js +1 -0
  224. package/dist/request.d.ts +17 -0
  225. package/dist/request.js +123 -0
  226. package/dist/sveltekit/auth.d.ts +18 -0
  227. package/dist/sveltekit/auth.js +9 -0
  228. package/dist/sveltekit/context.d.ts +28 -0
  229. package/dist/sveltekit/context.js +58 -0
  230. package/dist/sveltekit/index.d.ts +4 -0
  231. package/dist/sveltekit/index.js +4 -0
  232. package/dist/sveltekit/replica.d.ts +145 -0
  233. package/dist/sveltekit/replica.js +491 -0
  234. package/dist/sveltekit/server-replica.d.ts +52 -0
  235. package/dist/sveltekit/server-replica.js +176 -0
  236. package/dist/sveltekit/vite.d.ts +103 -0
  237. package/dist/sveltekit/vite.js +878 -0
  238. package/dist/types.d.ts +33 -0
  239. package/dist/types.js +1 -0
  240. package/dist/websocket.d.ts +41 -0
  241. package/dist/websocket.js +187 -0
  242. package/package.json +95 -0
@@ -0,0 +1,54 @@
1
+ import type { ReplicaAuthoritativeScope, ReplicaDehydratedState } from '../types.js';
2
+ export declare const REPLICA_OFFLINE_COMMAND_OUTBOX_SUPPORTED: false;
3
+ export type ReplicaPersistenceModelPolicy = {
4
+ /**
5
+ * Durable storage is an explicit per-model choice. `memory-only` is always
6
+ * honored, even when the model is otherwise non-sensitive.
7
+ */
8
+ readonly retention: 'persist-confirmed' | 'memory-only';
9
+ /**
10
+ * Sensitivity must be classified explicitly. Sensitive or unclassified
11
+ * models are never written to durable storage.
12
+ */
13
+ readonly sensitive: boolean;
14
+ };
15
+ export type ReplicaPersistencePolicy = {
16
+ readonly models: Readonly<Record<string, ReplicaPersistenceModelPolicy>>;
17
+ };
18
+ export type ReplicaIndexedDbFactory = Pick<IDBFactory, 'open'>;
19
+ export type ReplicaIndexedDbPersistenceOptions = {
20
+ /**
21
+ * Defaults to the browser's IndexedDB factory. Supplying a factory makes the
22
+ * boundary deterministic in tests and non-window runtimes.
23
+ */
24
+ readonly indexedDB?: ReplicaIndexedDbFactory;
25
+ readonly databaseName?: string;
26
+ /**
27
+ * Missing models, missing policy, `memory-only`, and `sensitive: true` all
28
+ * fail closed. A model persists only when both fields explicitly allow it.
29
+ */
30
+ readonly policy?: ReplicaPersistencePolicy;
31
+ };
32
+ export interface ReplicaIndexedDbPersistence {
33
+ readonly supportsOfflineCommandOutbox: false;
34
+ /**
35
+ * Validate, policy-filter, and persist one confirmed dehydration envelope.
36
+ *
37
+ * Returns false when policy leaves no durable records or causal fences.
38
+ * Malformed caller input rejects rather than entering durable storage.
39
+ */
40
+ save(state: ReplicaDehydratedState): Promise<boolean>;
41
+ /**
42
+ * Restore only after the caller has independently obtained the exact current
43
+ * server scope. Corrupt, unsupported, or mismatched entries are deleted and
44
+ * return undefined.
45
+ */
46
+ restore(authoritativeScope: ReplicaAuthoritativeScope): Promise<ReplicaDehydratedState | undefined>;
47
+ /** Delete confirmed state for exactly one independently authoritative scope. */
48
+ discard(authoritativeScope: ReplicaAuthoritativeScope): Promise<void>;
49
+ /**
50
+ * Close this instance's database handle. Persistence deliberately performs
51
+ * no BroadcastChannel, leader election, or active multi-tab synchronization.
52
+ */
53
+ close(): void;
54
+ }
@@ -0,0 +1 @@
1
+ export const REPLICA_OFFLINE_COMMAND_OUTBOX_SUPPORTED = false;
@@ -0,0 +1,3 @@
1
+ /** Confirmed-state IndexedDB persistence; implementation in ./persistence/. */
2
+ export { createReplicaIndexedDbPersistence, REPLICA_OFFLINE_COMMAND_OUTBOX_SUPPORTED } from './persistence/index.js';
3
+ export type { ReplicaIndexedDbFactory, ReplicaIndexedDbPersistence, ReplicaIndexedDbPersistenceOptions, ReplicaPersistenceModelPolicy, ReplicaPersistencePolicy } from './persistence/index.js';
@@ -0,0 +1,2 @@
1
+ /** Confirmed-state IndexedDB persistence; implementation in ./persistence/. */
2
+ export { createReplicaIndexedDbPersistence, REPLICA_OFFLINE_COMMAND_OUTBOX_SUPPORTED } from './persistence/index.js';
@@ -0,0 +1,13 @@
1
+ export declare const FILTER_MATCH: Readonly<{
2
+ result: "match";
3
+ }>;
4
+ export declare const FILTER_NO_MATCH: Readonly<{
5
+ result: "no_match";
6
+ }>;
7
+ export declare const ORDER_EQUAL: Readonly<{
8
+ result: "equal";
9
+ }>;
10
+ export declare const PAGINATION_LOCAL: Readonly<{
11
+ decision: "local";
12
+ }>;
13
+ export declare const UTF8_ENCODER: TextEncoder;
@@ -0,0 +1,7 @@
1
+ export const FILTER_MATCH = Object.freeze({ result: 'match' });
2
+ export const FILTER_NO_MATCH = Object.freeze({ result: 'no_match' });
3
+ export const ORDER_EQUAL = Object.freeze({ result: 'equal' });
4
+ export const PAGINATION_LOCAL = Object.freeze({
5
+ decision: 'local'
6
+ });
7
+ export const UTF8_ENCODER = new TextEncoder();
@@ -0,0 +1,39 @@
1
+ import type { GraphqlVariables } from '../../types.js';
2
+ import type { ReplicaFilterArtifact, ReplicaFilterExpression, ReplicaFilterFieldArtifact, ReplicaFilterOperator, ReplicaRelationshipArtifact, ReplicaRelationshipKeyMapping, ReplicaValue } from '../types.js';
3
+ import type { FilterCatalog, ReplicaFilterEvaluation, ReplicaFilterEvaluationOptions, ReplicaQueryPlanPath, ReplicaQueryPlanReason, ResolvedOperand } from './types.js';
4
+ export declare function evaluateReplicaFilter(artifact: ReplicaFilterArtifact, record: Readonly<Record<string, ReplicaValue>>, variables?: GraphqlVariables, options?: ReplicaFilterEvaluationOptions): ReplicaFilterEvaluation;
5
+ export declare function evaluateCallerWhere(value: ReplicaValue, record: Readonly<Record<string, ReplicaValue>>, catalog: FilterCatalog, options: ReplicaFilterEvaluationOptions, path: ReplicaQueryPlanPath): ReplicaFilterEvaluation;
6
+ export declare function evaluateCallerField(field: ReplicaFilterFieldArtifact, value: ReplicaValue, record: Readonly<Record<string, ReplicaValue>>, path: ReplicaQueryPlanPath): ReplicaFilterEvaluation;
7
+ export declare function evaluateClientOperator(field: ReplicaFilterFieldArtifact, operator: ReplicaFilterOperator, left: ReplicaValue, rhs: ReplicaValue, path: ReplicaQueryPlanPath): ReplicaFilterEvaluation;
8
+ export declare function evaluateRowPolicy(artifact: ReplicaFilterArtifact, record: Readonly<Record<string, ReplicaValue>>, catalog: FilterCatalog, options: ReplicaFilterEvaluationOptions, path: ReplicaQueryPlanPath): ReplicaFilterEvaluation;
9
+ export declare function evaluatePolicyExpression(expression: ReplicaFilterExpression, record: Readonly<Record<string, ReplicaValue>>, catalog: FilterCatalog, options: ReplicaFilterEvaluationOptions, path: ReplicaQueryPlanPath): ReplicaFilterEvaluation;
10
+ export declare function evaluateComparison(codec: string, operator: ReplicaFilterOperator, left: ReplicaValue, right: ReplicaValue, path: ReplicaQueryPlanPath): ReplicaFilterEvaluation;
11
+ export declare function evaluateIn(codec: string, left: ReplicaValue, values: readonly ReplicaValue[], negated: boolean, path: ReplicaQueryPlanPath): ReplicaFilterEvaluation;
12
+ export declare function validateComparableValues(codec: string, left: ReplicaValue, right: ReplicaValue, path: ReplicaQueryPlanPath): ReplicaQueryPlanReason | undefined;
13
+ /**
14
+ * Protocol v1 orders strings by their unsigned UTF-8 bytes. The GraphQL SQL
15
+ * compiler emits the matching binary collation for every textual ORDER BY,
16
+ * making optimistic index maintenance identical on SQLite and PostgreSQL.
17
+ */
18
+ export declare function validateRowPolicyLiteral(field: ReplicaFilterFieldArtifact, operand: Extract<ResolvedOperand, {
19
+ readonly kind: 'value';
20
+ }>, path: ReplicaQueryPlanPath, operator?: ReplicaFilterOperator): ReplicaQueryPlanReason | undefined;
21
+ export declare function filterCatalog(artifact: ReplicaFilterArtifact): FilterCatalog | {
22
+ readonly reason: ReplicaQueryPlanReason;
23
+ };
24
+ export declare function relationshipArtifact(value: unknown, path: ReplicaQueryPlanPath): {
25
+ readonly value: ReplicaRelationshipArtifact;
26
+ } | {
27
+ readonly reason: ReplicaQueryPlanReason;
28
+ };
29
+ export declare function relationshipKeyMapping(value: unknown, dependencies: readonly string[], path: ReplicaQueryPlanPath): {
30
+ readonly value: ReplicaRelationshipKeyMapping;
31
+ } | {
32
+ readonly reason: ReplicaQueryPlanReason;
33
+ };
34
+ export declare function filterAnd(values: readonly ReplicaFilterEvaluation[]): ReplicaFilterEvaluation;
35
+ export declare function filterOr(values: readonly ReplicaFilterEvaluation[]): ReplicaFilterEvaluation;
36
+ export declare function filterNot(value: ReplicaFilterEvaluation): ReplicaFilterEvaluation;
37
+ export declare function filterUnknown(reasonValue: ReplicaQueryPlanReason): ReplicaFilterEvaluation;
38
+ export declare function isFilterOperator(value: string): value is ReplicaFilterOperator;
39
+ export declare function isOperatorScalarCompatible(scalar: string, operator: ReplicaFilterOperator): boolean;
@@ -0,0 +1,544 @@
1
+ import { FILTER_MATCH, FILTER_NO_MATCH } from './constants.js';
2
+ import { recordField, resolveInput, resolveOperand, uniqueStringList } from './resolve.js';
3
+ import { isFiniteNumber, isInt32, isName, isPortableNumericCodec, isRecord, isSafeIntegerNumber, isScalarCodecPair, reason } from './util.js';
4
+ export function evaluateReplicaFilter(artifact, record, variables = {}, options = {}) {
5
+ const catalog = filterCatalog(artifact);
6
+ if ('reason' in catalog)
7
+ return filterUnknown(catalog.reason);
8
+ const input = resolveInput(artifact.input, variables, ['where']);
9
+ const caller = input.kind === 'unknown'
10
+ ? filterUnknown(input.reason)
11
+ : input.kind === 'omitted' || input.value === null
12
+ ? FILTER_MATCH
13
+ : evaluateCallerWhere(input.value, record, catalog, options, ['where']);
14
+ const policy = evaluateRowPolicy(artifact, record, catalog, options, ['rowPolicy']);
15
+ return filterAnd([caller, policy]);
16
+ }
17
+ export function evaluateCallerWhere(value, record, catalog, options, path) {
18
+ if (value === null)
19
+ return FILTER_MATCH;
20
+ if (!isRecord(value)) {
21
+ return filterUnknown(reason('invalid_filter_input', path, 'where predicates must be objects'));
22
+ }
23
+ const predicates = [];
24
+ for (const [key, predicate] of Object.entries(value)) {
25
+ const predicatePath = [...path, key];
26
+ if (key === '_and' || key === '_or') {
27
+ if (predicate === null)
28
+ continue;
29
+ const items = Array.isArray(predicate)
30
+ ? predicate
31
+ : isRecord(predicate)
32
+ ? [predicate]
33
+ : undefined;
34
+ if (items === undefined) {
35
+ predicates.push(filterUnknown(reason('invalid_filter_input', predicatePath, `${key} must be an object or list of objects`)));
36
+ continue;
37
+ }
38
+ // The server treats both empty caller lists as no predicate (TRUE).
39
+ if (items.length === 0)
40
+ continue;
41
+ const evaluated = items.map((item, index) => evaluateCallerWhere(item, record, catalog, options, [...predicatePath, index]));
42
+ predicates.push(key === '_and' ? filterAnd(evaluated) : filterOr(evaluated));
43
+ continue;
44
+ }
45
+ if (key === '_not') {
46
+ if (predicate === null) {
47
+ predicates.push(FILTER_NO_MATCH);
48
+ }
49
+ else {
50
+ predicates.push(filterNot(evaluateCallerWhere(predicate, record, catalog, options, predicatePath)));
51
+ }
52
+ continue;
53
+ }
54
+ const field = catalog.fields.get(key);
55
+ if (field !== undefined) {
56
+ predicates.push(evaluateCallerField(field, predicate, record, predicatePath));
57
+ continue;
58
+ }
59
+ const relationship = catalog.relationships.get(key);
60
+ if (relationship !== undefined) {
61
+ const resolver = options.resolveRelationship;
62
+ predicates.push(resolver === undefined
63
+ ? filterUnknown(reason('relationship_resolver_required', predicatePath, `relationship predicate ${key} requires an explicit resolver`))
64
+ : resolver({
65
+ source: 'caller',
66
+ relationship,
67
+ predicate,
68
+ record,
69
+ path: Object.freeze(predicatePath)
70
+ }));
71
+ continue;
72
+ }
73
+ predicates.push(filterUnknown(reason('unknown_field', predicatePath, `where field ${key} is not declared by the artifact`)));
74
+ }
75
+ return filterAnd(predicates);
76
+ }
77
+ export function evaluateCallerField(field, value, record, path) {
78
+ if (value === null)
79
+ return FILTER_MATCH;
80
+ if (!isRecord(value)) {
81
+ return filterUnknown(reason('invalid_filter_input', path, `comparison expression for ${field.field} must be an object`));
82
+ }
83
+ const predicates = [];
84
+ for (const [rawOperator, rhs] of Object.entries(value)) {
85
+ const operatorPath = [...path, rawOperator];
86
+ if (!isFilterOperator(rawOperator) ||
87
+ !field.operators.includes(rawOperator)) {
88
+ predicates.push(filterUnknown(reason('unsupported_operator', operatorPath, `operator ${rawOperator} is not declared for ${field.field}`)));
89
+ continue;
90
+ }
91
+ const left = recordField(record, field.field, operatorPath);
92
+ if (left.kind === 'unknown') {
93
+ predicates.push(filterUnknown(left.reason));
94
+ continue;
95
+ }
96
+ predicates.push(evaluateClientOperator(field, rawOperator, left.value, rhs, operatorPath));
97
+ }
98
+ return filterAnd(predicates);
99
+ }
100
+ export function evaluateClientOperator(field, operator, left, rhs, path) {
101
+ if (operator === '_is_null') {
102
+ if (rhs !== null && typeof rhs !== 'boolean') {
103
+ return filterUnknown(reason('invalid_filter_value', path, '_is_null must be a boolean or null'));
104
+ }
105
+ const wantsNull = rhs === true;
106
+ return (left === null) === wantsNull ? FILTER_MATCH : FILTER_NO_MATCH;
107
+ }
108
+ if (operator === '_in' || operator === '_nin') {
109
+ if (rhs === null) {
110
+ return filterUnknown(reason('invalid_filter_value', path, `${operator} requires a list`));
111
+ }
112
+ const values = Array.isArray(rhs) ? rhs : [rhs];
113
+ return evaluateIn(field.codec, left, values, operator === '_nin', path);
114
+ }
115
+ return evaluateComparison(field.codec, operator, left, rhs, path);
116
+ }
117
+ export function evaluateRowPolicy(artifact, record, catalog, options, path) {
118
+ const policy = artifact.rowPolicy;
119
+ if (policy.kind === 'unrestricted')
120
+ return FILTER_MATCH;
121
+ if (policy.kind === 'server_only') {
122
+ return filterUnknown(reason('server_only_policy', path, 'server-only row policy cannot be evaluated in the replica'));
123
+ }
124
+ if (policy.kind !== 'predicate' || !isRecord(policy.expression)) {
125
+ return filterUnknown(reason('invalid_artifact', path, 'row policy artifact is malformed'));
126
+ }
127
+ return evaluatePolicyExpression(policy.expression, record, catalog, options, [...path, 'expression']);
128
+ }
129
+ export function evaluatePolicyExpression(expression, record, catalog, options, path) {
130
+ if (expression.kind === 'and' || expression.kind === 'or') {
131
+ if (!Array.isArray(expression.value)) {
132
+ return filterUnknown(reason('invalid_artifact', path, 'row-policy boolean value must be a list'));
133
+ }
134
+ const evaluated = expression.value.map((item, index) => evaluatePolicyExpression(item, record, catalog, options, [...path, expression.kind, index]));
135
+ // Unlike caller `_or: []`, a row-policy Or([]) is SQL FALSE.
136
+ return expression.kind === 'and' ? filterAnd(evaluated) : filterOr(evaluated);
137
+ }
138
+ if (expression.kind === 'not') {
139
+ return filterNot(evaluatePolicyExpression(expression.value, record, catalog, options, [...path, 'not']));
140
+ }
141
+ if (expression.kind === 'rel') {
142
+ const field = expression.value.field;
143
+ const relationshipPath = [...path, 'rel', field];
144
+ const relationship = catalog.relationships.get(field);
145
+ if (relationship === undefined) {
146
+ return filterUnknown(reason('unknown_relationship', relationshipPath, `row policy relationship ${field} is not declared by the artifact`));
147
+ }
148
+ const resolver = options.resolveRelationship;
149
+ return resolver === undefined
150
+ ? filterUnknown(reason('relationship_resolver_required', relationshipPath, `row policy relationship ${field} requires an explicit resolver`))
151
+ : resolver({
152
+ source: 'row_policy',
153
+ relationship,
154
+ predicate: expression.value.predicate,
155
+ record,
156
+ path: Object.freeze(relationshipPath)
157
+ });
158
+ }
159
+ if (expression.kind !== 'cmp' &&
160
+ expression.kind !== 'in' &&
161
+ expression.kind !== 'is_null') {
162
+ return filterUnknown(reason('invalid_artifact', path, 'row policy expression kind is unsupported'));
163
+ }
164
+ const column = expression.value.column;
165
+ const field = catalog.fields.get(column);
166
+ const columnPath = [...path, expression.kind, column];
167
+ if (field === undefined) {
168
+ return filterUnknown(reason('unknown_field', columnPath, `row policy field ${column} is not declared by the artifact`));
169
+ }
170
+ const left = recordField(record, column, columnPath);
171
+ if (left.kind === 'unknown')
172
+ return filterUnknown(left.reason);
173
+ if (expression.kind === 'is_null') {
174
+ if (typeof expression.value.is_null !== 'boolean') {
175
+ return filterUnknown(reason('invalid_artifact', [...columnPath, 'is_null'], 'row-policy is_null must be a boolean'));
176
+ }
177
+ return (left.value === null) === expression.value.is_null
178
+ ? FILTER_MATCH
179
+ : FILTER_NO_MATCH;
180
+ }
181
+ if (expression.kind === 'cmp') {
182
+ const operator = `_${expression.value.op}`;
183
+ if (!isFilterOperator(operator) ||
184
+ !isOperatorScalarCompatible(field.scalar, operator)) {
185
+ return filterUnknown(reason('invalid_artifact', [...columnPath, 'op'], `row-policy operator ${expression.value.op} cannot target ${field.scalar}`));
186
+ }
187
+ const operand = resolveOperand(expression.value.rhs, field, options, [...columnPath, 'rhs']);
188
+ if (operand.kind === 'unknown')
189
+ return filterUnknown(operand.reason);
190
+ const literalValidity = validateRowPolicyLiteral(field, operand, [...columnPath, 'rhs'], operator);
191
+ if (literalValidity !== undefined)
192
+ return filterUnknown(literalValidity);
193
+ return evaluateComparison(field.codec, `_${expression.value.op}`, left.value, operand.value, columnPath);
194
+ }
195
+ if (expression.kind === 'in') {
196
+ if (typeof expression.value.negated !== 'boolean' ||
197
+ !Array.isArray(expression.value.values)) {
198
+ return filterUnknown(reason('invalid_artifact', columnPath, 'row-policy IN values must be a list and negated must be a boolean'));
199
+ }
200
+ const values = [];
201
+ for (const [index, operand] of expression.value.values.entries()) {
202
+ const operandPath = [...columnPath, 'values', index];
203
+ const resolved = resolveOperand(operand, field, options, operandPath);
204
+ if (resolved.kind === 'unknown')
205
+ return filterUnknown(resolved.reason);
206
+ const literalValidity = validateRowPolicyLiteral(field, resolved, operandPath);
207
+ if (literalValidity !== undefined)
208
+ return filterUnknown(literalValidity);
209
+ values.push(resolved.value);
210
+ }
211
+ return evaluateIn(field.codec, left.value, values, expression.value.negated, columnPath);
212
+ }
213
+ return filterUnknown(reason('invalid_artifact', path, 'row policy expression kind is unsupported'));
214
+ }
215
+ export function evaluateComparison(codec, operator, left, right, path) {
216
+ if (operator === '_like' ||
217
+ operator === '_ilike' ||
218
+ operator === '_contains' ||
219
+ operator === '_contained_in' ||
220
+ operator === '_has_key') {
221
+ return filterUnknown(reason('unsupported_operator', path, `${operator} has no certified cross-dialect replica evaluator`));
222
+ }
223
+ if (left === null || right === null) {
224
+ return filterUnknown(reason('sql_null', path, 'SQL comparison with null is unknown'));
225
+ }
226
+ const validity = validateComparableValues(codec, left, right, path);
227
+ if (validity !== undefined)
228
+ return filterUnknown(validity);
229
+ if (operator === '_eq' || operator === '_neq') {
230
+ if (codec === 'string' && left !== right) {
231
+ return filterUnknown(reason('collation_not_portable', path, 'non-identical strings require an explicit server collation contract'));
232
+ }
233
+ const equal = left === right;
234
+ return equal === (operator === '_eq') ? FILTER_MATCH : FILTER_NO_MATCH;
235
+ }
236
+ if (operator !== '_gt' &&
237
+ operator !== '_gte' &&
238
+ operator !== '_lt' &&
239
+ operator !== '_lte') {
240
+ return filterUnknown(reason('unsupported_operator', path, `operator ${operator} is not portable`));
241
+ }
242
+ if (!isPortableNumericCodec(codec)) {
243
+ return filterUnknown(reason('unsupported_operator', path, `${operator} is portable only for certified numeric codecs`));
244
+ }
245
+ const a = left;
246
+ const b = right;
247
+ const matches = operator === '_gt'
248
+ ? a > b
249
+ : operator === '_gte'
250
+ ? a >= b
251
+ : operator === '_lt'
252
+ ? a < b
253
+ : a <= b;
254
+ return matches ? FILTER_MATCH : FILTER_NO_MATCH;
255
+ }
256
+ export function evaluateIn(codec, left, values, negated, path) {
257
+ // Server compilation special-cases empty lists before evaluating the column.
258
+ if (values.length === 0)
259
+ return negated ? FILTER_MATCH : FILTER_NO_MATCH;
260
+ if (left === null) {
261
+ return filterUnknown(reason('sql_null', path, 'SQL IN comparison with a null column is unknown'));
262
+ }
263
+ // GraphQL rejects a type-invalid list before SQL runs. Validate every
264
+ // non-null candidate before allowing a later equality to establish truth.
265
+ // SQL NULL is different: a concrete equality may still dominate UNKNOWN.
266
+ for (const [index, candidate] of values.entries()) {
267
+ if (candidate === null)
268
+ continue;
269
+ const validity = validateComparableValues(codec, left, candidate, [...path, index]);
270
+ if (validity !== undefined)
271
+ return filterUnknown(validity);
272
+ }
273
+ let firstUnknown;
274
+ for (const [index, candidate] of values.entries()) {
275
+ if (candidate === null) {
276
+ firstUnknown ??= reason('sql_null', [...path, index], 'SQL IN list containing null can produce an unknown result');
277
+ continue;
278
+ }
279
+ if (left === candidate)
280
+ return negated ? FILTER_NO_MATCH : FILTER_MATCH;
281
+ if (codec === 'string') {
282
+ firstUnknown ??= reason('collation_not_portable', [...path, index], 'non-identical strings in SQL IN require an explicit server collation contract');
283
+ }
284
+ }
285
+ if (firstUnknown !== undefined)
286
+ return filterUnknown(firstUnknown);
287
+ return negated ? FILTER_MATCH : FILTER_NO_MATCH;
288
+ }
289
+ export function validateComparableValues(codec, left, right, path) {
290
+ if (codec === 'string') {
291
+ return typeof left === 'string' && typeof right === 'string'
292
+ ? undefined
293
+ : reason('invalid_filter_value', path, 'string codec requires string values');
294
+ }
295
+ if (codec === 'boolean') {
296
+ return typeof left === 'boolean' && typeof right === 'boolean'
297
+ ? undefined
298
+ : reason('invalid_filter_value', path, 'boolean codec requires boolean values');
299
+ }
300
+ if (codec === 'int32') {
301
+ return isInt32(left) && isInt32(right)
302
+ ? undefined
303
+ : reason('invalid_filter_value', path, 'int32 codec requires signed 32-bit integer values');
304
+ }
305
+ if (codec === 'float64') {
306
+ return isFiniteNumber(left) && isFiniteNumber(right)
307
+ ? undefined
308
+ : reason('invalid_filter_value', path, 'float64 codec requires finite number values');
309
+ }
310
+ if (codec === 'json_number_precision_limited') {
311
+ return isSafeIntegerNumber(left) && isSafeIntegerNumber(right)
312
+ ? undefined
313
+ : reason('invalid_filter_value', path, 'BigInt codec requires JavaScript-safe integer values');
314
+ }
315
+ return reason('unsupported_codec', path, `codec ${codec} has no certified replica comparison semantics`);
316
+ }
317
+ /**
318
+ * Protocol v1 orders strings by their unsigned UTF-8 bytes. The GraphQL SQL
319
+ * compiler emits the matching binary collation for every textual ORDER BY,
320
+ * making optimistic index maintenance identical on SQLite and PostgreSQL.
321
+ */
322
+ export function validateRowPolicyLiteral(field, operand, path, operator) {
323
+ if (operand.source === 'trusted_preset')
324
+ return undefined;
325
+ const literalKind = operand.literalKind;
326
+ if (literalKind === undefined) {
327
+ return reason('invalid_artifact', path, 'row-policy literal is missing its tagged kind');
328
+ }
329
+ if (literalKind === 'null')
330
+ return undefined;
331
+ const expected = field.scalar === 'ID' ||
332
+ field.scalar === 'String' ||
333
+ field.scalar === 'Timestamptz'
334
+ ? ['string']
335
+ : field.scalar === 'Boolean'
336
+ ? ['bool']
337
+ : field.scalar === 'Int' || field.scalar === 'BigInt'
338
+ ? ['i64']
339
+ : field.scalar === 'Float'
340
+ ? ['f64', 'i64']
341
+ : field.scalar === 'JSON'
342
+ ? operator === '_has_key'
343
+ ? ['string']
344
+ : ['json']
345
+ : undefined;
346
+ if (expected?.includes(literalKind))
347
+ return undefined;
348
+ return reason('invalid_artifact', path, expected === undefined
349
+ ? `scalar ${field.scalar} has no non-null row-policy literal encoding`
350
+ : `row-policy literal kind ${literalKind} cannot target ${field.scalar}; expected ${expected.join(' or ')}`);
351
+ }
352
+ export function filterCatalog(artifact) {
353
+ const fields = new Map();
354
+ for (const [index, field] of artifact.fields.entries()) {
355
+ if (!isName(field.field) ||
356
+ !isScalarCodecPair(field.scalar, field.codec) ||
357
+ typeof field.nullable !== 'boolean' ||
358
+ !Array.isArray(field.operators) ||
359
+ fields.has(field.field)) {
360
+ return {
361
+ reason: reason('invalid_artifact', ['filter', 'fields', index], 'filter fields must have unique names, exact scalar-codec pairs, nullability, and operators')
362
+ };
363
+ }
364
+ const operators = new Set();
365
+ for (const [operatorIndex, operator] of field.operators.entries()) {
366
+ if (!isFilterOperator(operator) ||
367
+ !isOperatorScalarCompatible(field.scalar, operator) ||
368
+ operators.has(operator)) {
369
+ return {
370
+ reason: reason('invalid_artifact', ['filter', 'fields', index, 'operators', operatorIndex], 'filter operators must be unique and compatible with the field scalar')
371
+ };
372
+ }
373
+ operators.add(operator);
374
+ }
375
+ fields.set(field.field, field);
376
+ }
377
+ const relationships = new Map();
378
+ for (const [index, value] of artifact.relationships.entries()) {
379
+ const parsed = relationshipArtifact(value, [
380
+ 'filter',
381
+ 'relationships',
382
+ index
383
+ ]);
384
+ if ('reason' in parsed)
385
+ return parsed;
386
+ const relationship = parsed.value;
387
+ if (relationships.has(relationship.field)) {
388
+ return {
389
+ reason: reason('invalid_artifact', ['filter', 'relationships', index], 'filter relationship descriptors must have unique fields')
390
+ };
391
+ }
392
+ relationships.set(relationship.field, relationship);
393
+ }
394
+ return { fields, relationships };
395
+ }
396
+ export function relationshipArtifact(value, path) {
397
+ if (!isRecord(value) ||
398
+ !isName(value.field) ||
399
+ !isName(value.targetModel) ||
400
+ (value.kind !== 'has_many' &&
401
+ value.kind !== 'belongs_to' &&
402
+ value.kind !== 'many_to_many') ||
403
+ (value.maintenance !== 'local' && value.maintenance !== 'revalidate')) {
404
+ return {
405
+ reason: reason('invalid_artifact', path, 'relationship descriptor has invalid field, target, kind, or maintenance')
406
+ };
407
+ }
408
+ const dependencies = uniqueStringList(value.dependencies);
409
+ if (dependencies === undefined || dependencies.length === 0) {
410
+ return {
411
+ reason: reason('invalid_artifact', [...path, 'dependencies'], 'relationship dependencies must be a non-empty unique string list')
412
+ };
413
+ }
414
+ const keyMapping = relationshipKeyMapping(value.keyMapping, dependencies, [...path, 'keyMapping']);
415
+ if ('reason' in keyMapping)
416
+ return keyMapping;
417
+ const localMapping = keyMapping.value.kind === 'direct' || keyMapping.value.kind === 'through';
418
+ if (!localMapping && value.maintenance !== 'revalidate') {
419
+ return {
420
+ reason: reason('invalid_artifact', [...path, 'maintenance'], 'opaque and embedded mappings must use revalidation maintenance')
421
+ };
422
+ }
423
+ return {
424
+ value: Object.freeze({
425
+ field: value.field,
426
+ targetModel: value.targetModel,
427
+ kind: value.kind,
428
+ keyMapping: keyMapping.value,
429
+ maintenance: value.maintenance,
430
+ dependencies
431
+ })
432
+ };
433
+ }
434
+ export function relationshipKeyMapping(value, dependencies, path) {
435
+ if (!isRecord(value) || !isName(value.kind)) {
436
+ return {
437
+ reason: reason('invalid_artifact', path, 'relationship key mapping must declare a supported kind')
438
+ };
439
+ }
440
+ if (value.kind === 'embedded') {
441
+ return { value: Object.freeze({ kind: 'embedded' }) };
442
+ }
443
+ const local = uniqueStringList(value.local);
444
+ const remote = uniqueStringList(value.remote);
445
+ if (local === undefined ||
446
+ remote === undefined ||
447
+ local.length === 0 ||
448
+ local.length !== remote.length) {
449
+ return {
450
+ reason: reason('invalid_artifact', path, 'relationship key lists must be unique, non-empty, and equally sized')
451
+ };
452
+ }
453
+ if (value.kind === 'direct') {
454
+ return {
455
+ value: Object.freeze({ kind: 'direct', local, remote })
456
+ };
457
+ }
458
+ if (value.kind === 'through' &&
459
+ isName(value.table) &&
460
+ dependencies.includes(value.table) &&
461
+ isName(value.sourceForeignKey) &&
462
+ isName(value.targetForeignKey)) {
463
+ return {
464
+ value: Object.freeze({
465
+ kind: 'through',
466
+ local,
467
+ remote,
468
+ table: value.table,
469
+ sourceForeignKey: value.sourceForeignKey,
470
+ targetForeignKey: value.targetForeignKey
471
+ })
472
+ };
473
+ }
474
+ if (value.kind === 'through_opaque' &&
475
+ isName(value.dependency) &&
476
+ dependencies.includes(value.dependency)) {
477
+ return {
478
+ value: Object.freeze({
479
+ kind: 'through_opaque',
480
+ local,
481
+ remote,
482
+ dependency: value.dependency
483
+ })
484
+ };
485
+ }
486
+ return {
487
+ reason: reason('invalid_artifact', path, 'relationship key mapping is incomplete or references an undeclared dependency')
488
+ };
489
+ }
490
+ export function filterAnd(values) {
491
+ let firstUnknown;
492
+ for (const value of values) {
493
+ if (value.result === 'no_match')
494
+ return FILTER_NO_MATCH;
495
+ if (value.result === 'unknown')
496
+ firstUnknown ??= value.reason;
497
+ }
498
+ return firstUnknown === undefined ? FILTER_MATCH : filterUnknown(firstUnknown);
499
+ }
500
+ export function filterOr(values) {
501
+ let firstUnknown;
502
+ for (const value of values) {
503
+ if (value.result === 'match')
504
+ return FILTER_MATCH;
505
+ if (value.result === 'unknown')
506
+ firstUnknown ??= value.reason;
507
+ }
508
+ return firstUnknown === undefined ? FILTER_NO_MATCH : filterUnknown(firstUnknown);
509
+ }
510
+ export function filterNot(value) {
511
+ if (value.result === 'unknown')
512
+ return value;
513
+ return value.result === 'match' ? FILTER_NO_MATCH : FILTER_MATCH;
514
+ }
515
+ export function filterUnknown(reasonValue) {
516
+ return Object.freeze({ result: 'unknown', reason: reasonValue });
517
+ }
518
+ export function isFilterOperator(value) {
519
+ return (value === '_eq' ||
520
+ value === '_neq' ||
521
+ value === '_gt' ||
522
+ value === '_gte' ||
523
+ value === '_lt' ||
524
+ value === '_lte' ||
525
+ value === '_in' ||
526
+ value === '_nin' ||
527
+ value === '_is_null' ||
528
+ value === '_like' ||
529
+ value === '_ilike' ||
530
+ value === '_contains' ||
531
+ value === '_contained_in' ||
532
+ value === '_has_key');
533
+ }
534
+ export function isOperatorScalarCompatible(scalar, operator) {
535
+ if (operator === '_like' || operator === '_ilike') {
536
+ return scalar === 'String';
537
+ }
538
+ if (operator === '_contains' ||
539
+ operator === '_contained_in' ||
540
+ operator === '_has_key') {
541
+ return scalar === 'JSON';
542
+ }
543
+ return true;
544
+ }