| /** | |
| * Schema-aware `RequestResolver` helpers for SQL-backed data loading. | |
| * | |
| * This module represents each lookup or mutation as a `SqlRequest` and batches | |
| * concurrent requests into SQL operations. Request payloads are encoded with the | |
| * request schema before `execute` is called, and returned rows are decoded with | |
| * the result schema before requests are completed. It provides ordered, | |
| * grouped, id-based, and side-effect-only resolver constructors, and keeps | |
| * batches separated by the active SQL transaction connection. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| import * as Arr from "../../Array.js"; | |
| import * as Cause from "../../Cause.js"; | |
| import * as Effect from "../../Effect.js"; | |
| import * as Equal from "../../Equal.js"; | |
| import * as Exit from "../../Exit.js"; | |
| import * as Hash from "../../Hash.js"; | |
| import * as MutableHashMap from "../../MutableHashMap.js"; | |
| import * as Request from "../../Request.js"; | |
| import * as RequestResolver from "../../RequestResolver.js"; | |
| import * as Schema from "../../Schema.js"; | |
| import * as SqlClient from "./SqlClient.js"; | |
| import { ResultLengthMismatch } from "./SqlError.js"; | |
| const SqlRequestProto = { | |
| ...Request.Class.prototype, | |
| [Equal.symbol](that) { | |
| return Equal.equals(this.payload, that.payload); | |
| }, | |
| [Hash.symbol]() { | |
| return Hash.hash(this.payload); | |
| } | |
| }; | |
| /** | |
| * Runs a payload as a `SqlRequest` through a request resolver, either directly | |
| * with a payload and resolver or curried by resolver. | |
| * | |
| * @category requests | |
| * @since 4.0.0 | |
| */ | |
| export const request = function () { | |
| if (arguments.length === 1) { | |
| const resolver = arguments[0]; | |
| return payload => Effect.request(SqlRequest(payload), resolver); | |
| } | |
| return Effect.request(SqlRequest(arguments[0]), arguments[1]); | |
| }; | |
| /** | |
| * Constructs a `SqlRequest` from a payload. Equality and hashing are based on | |
| * the payload so equal requests can be batched and deduplicated. | |
| * | |
| * @category requests | |
| * @since 4.0.0 | |
| */ | |
| export const SqlRequest = payload => { | |
| const self = Object.create(SqlRequestProto); | |
| self.payload = payload; | |
| return self; | |
| }; | |
| /** | |
| * Creates a resolver for a SQL query with a request schema and a result schema. | |
| * | |
| * **Details** | |
| * | |
| * The request schema is used to validate the input of the query, and the result | |
| * schema is used to validate the output of the query. Results are mapped to the | |
| * requests in order, so the length of the results must match the length of the | |
| * requests. | |
| * | |
| * @category resolvers | |
| * @since 4.0.0 | |
| */ | |
| export const ordered = options => { | |
| const decodeArray = Schema.decodeUnknownEffect(Schema.Array(options.Result)); | |
| return RequestResolver.makeGrouped({ | |
| key: transactionKey, | |
| resolver: Effect.fnUntraced(function* (entries) { | |
| const inputs = yield* partitionRequests(entries, options.Request); | |
| const results = yield* options.execute(inputs).pipe(Effect.provideContext(entries[0].context)); | |
| if (results.length !== inputs.length) { | |
| return yield* new ResultLengthMismatch({ | |
| expected: inputs.length, | |
| actual: results.length | |
| }); | |
| } | |
| const decodedResults = yield* decodeArray(results).pipe(Effect.provideContext(entries[0].context)); | |
| for (let i = 0; i < entries.length; i++) { | |
| entries[i].completeUnsafe(Exit.succeed(decodedResults[i])); | |
| } | |
| }) | |
| }); | |
| }; | |
| /** | |
| * Creates a batched SQL request resolver that encodes requests, decodes result | |
| * rows, groups decoded results by matching request and result keys, and fails a | |
| * request with `NoSuchElementError` when no result group exists. | |
| * | |
| * @category resolvers | |
| * @since 4.0.0 | |
| */ | |
| export const grouped = options => { | |
| const decodeResults = Schema.decodeUnknownEffect(Schema.Array(options.Result)); | |
| return RequestResolver.makeGrouped({ | |
| key: transactionKey, | |
| resolver: Effect.fnUntraced(function* (entries) { | |
| const inputs = yield* partitionRequests(entries, options.Request); | |
| const resultMap = MutableHashMap.empty(); | |
| const results = yield* options.execute(inputs).pipe(Effect.provideContext(entries[0].context)); | |
| const decodedResults = yield* decodeResults(results).pipe(Effect.provideContext(entries[0].context)); | |
| for (let i = 0, len = decodedResults.length; i < len; i++) { | |
| const result = decodedResults[i]; | |
| const key = options.ResultGroupKey(result, results[i]); | |
| const group = MutableHashMap.get(resultMap, key); | |
| if (group._tag === "None") { | |
| MutableHashMap.set(resultMap, key, [result]); | |
| } else { | |
| group.value.push(result); | |
| } | |
| } | |
| for (let i = 0, len = entries.length; i < len; i++) { | |
| const entry = entries[i]; | |
| const key = options.RequestGroupKey(entry.request.payload); | |
| const result = MutableHashMap.get(resultMap, key); | |
| entry.completeUnsafe(result._tag === "None" ? constNoSuchElement : Exit.succeed(result.value)); | |
| } | |
| }) | |
| }); | |
| }; | |
| /** | |
| * Creates a batched resolver that fetches rows for encoded ids, decodes | |
| * results, completes each matching request using `ResultId`, and fails missing | |
| * ids with `NoSuchElementError`. | |
| * | |
| * @category resolvers | |
| * @since 4.0.0 | |
| */ | |
| export const findById = options => { | |
| const decodeResults = Schema.decodeUnknownEffect(Schema.Array(options.Result)); | |
| return RequestResolver.makeGrouped({ | |
| key: transactionKey, | |
| resolver: Effect.fnUntraced(function* (entries) { | |
| const [inputs, idMap] = yield* partitionRequestsById(entries, options.Id); | |
| const results = yield* options.execute(inputs).pipe(Effect.provideContext(entries[0].context)); | |
| const decodedResults = yield* decodeResults(results).pipe(Effect.provideContext(entries[0].context)); | |
| for (let i = 0; i < decodedResults.length; i++) { | |
| const result = decodedResults[i]; | |
| const id = options.ResultId(result, results[i]); | |
| const request = MutableHashMap.get(idMap, id); | |
| if (request._tag === "None") { | |
| continue; | |
| } | |
| MutableHashMap.remove(idMap, id); | |
| request.value.completeUnsafe(Exit.succeed(result)); | |
| } | |
| if (MutableHashMap.isEmpty(idMap)) { | |
| return; | |
| } | |
| MutableHashMap.forEach(idMap, request => { | |
| request.completeUnsafe(constNoSuchElement); | |
| }); | |
| }) | |
| }); | |
| }; | |
| const void_ = options => RequestResolver.makeGrouped({ | |
| key: transactionKey, | |
| resolver: Effect.fnUntraced(function* (entries) { | |
| const inputs = yield* partitionRequests(entries, options.Request); | |
| yield* options.execute(inputs).pipe(Effect.provideContext(entries[0].context)); | |
| for (let i = 0; i < entries.length; i++) { | |
| entries[i].completeUnsafe(Exit.void); | |
| } | |
| }) | |
| }); | |
| export { | |
| /** | |
| * Create a resolver that performs side effects. | |
| * | |
| * @category resolvers | |
| * @since 4.0.0 | |
| */ | |
| void_ as void }; | |
| const constNoSuchElement = /*#__PURE__*/Exit.fail(/*#__PURE__*/new Cause.NoSuchElementError()); | |
| const partitionRequests = function* (requests, schema) { | |
| const len = requests.length; | |
| const inputs = Arr.empty(); | |
| let entry; | |
| const encode = Schema.encodeEffect(schema); | |
| const handle = Effect.matchCauseEager({ | |
| onFailure(cause) { | |
| entry.completeUnsafe(Exit.failCause(cause)); | |
| }, | |
| onSuccess(value) { | |
| inputs.push(value); | |
| } | |
| }); | |
| for (let i = 0; i < len; i++) { | |
| entry = requests[i]; | |
| yield Effect.provideContext(handle(encode(entry.request.payload)), entry.context); | |
| } | |
| return inputs; | |
| }; | |
| const partitionRequestsById = function* (requests, schema) { | |
| const len = requests.length; | |
| const inputs = Arr.empty(); | |
| const byIdMap = MutableHashMap.empty(); | |
| let entry; | |
| const encode = Schema.encodeEffect(schema); | |
| const handle = Effect.matchCauseEager({ | |
| onFailure(cause) { | |
| entry.completeUnsafe(Exit.failCause(cause)); | |
| }, | |
| onSuccess(value) { | |
| inputs.push(value); | |
| } | |
| }); | |
| for (let i = 0; i < len; i++) { | |
| entry = requests[i]; | |
| yield Effect.provideContext(handle(encode(entry.request.payload)), entry.context); | |
| MutableHashMap.set(byIdMap, entry.request.payload, entry); | |
| } | |
| return [inputs, byIdMap]; | |
| }; | |
| function transactionKey(entry) { | |
| const client = entry.context.mapUnsafe.get(SqlClient.SqlClient.key); | |
| if (!client) return undefined; | |
| const conn = entry.context.mapUnsafe.get(client.transactionService.key); | |
| if (!conn) return undefined; | |
| return Equal.byReferenceUnsafe(conn); | |
| } | |
| //# sourceMappingURL=SqlResolver.js.map |
Xet Storage Details
- Size:
- 8.36 kB
- Xet hash:
- 8c5cde3ffdd333b82a05274b09b613e4a55240722db26edd3126415c513086e2
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.