@hot-updater/cloudflare 0.18.1 → 0.18.2

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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
- import { createDatabasePlugin } from "@hot-updater/plugin-core";
2
+ import { calculatePagination, createDatabasePlugin } from "@hot-updater/plugin-core";
3
3
  import Cloudflare from "cloudflare";
4
4
  import path from "path";
5
5
  import { fileURLToPath } from "node:url";
@@ -284,7 +284,7 @@ var require_lib = __commonJS({ "../../node_modules/.pnpm/pg-minify@1.6.5/node_mo
284
284
 
285
285
  //#endregion
286
286
  //#region src/d1Database.ts
287
- var import_lib = __toESM(require_lib(), 1);
287
+ var import_lib = __toESM(require_lib());
288
288
  async function resolvePage(singlePage) {
289
289
  const results = [];
290
290
  for await (const page of singlePage.iterPages()) {
@@ -293,8 +293,70 @@ async function resolvePage(singlePage) {
293
293
  }
294
294
  return results;
295
295
  }
296
+ function buildWhereClause(conditions) {
297
+ const clauses = [];
298
+ const params = [];
299
+ if (conditions.channel) {
300
+ clauses.push("channel = ?");
301
+ params.push(conditions.channel);
302
+ }
303
+ if (conditions.platform) {
304
+ clauses.push("platform = ?");
305
+ params.push(conditions.platform);
306
+ }
307
+ const whereClause = clauses.length > 0 ? ` WHERE ${clauses.join(" AND ")}` : "";
308
+ return {
309
+ sql: whereClause,
310
+ params
311
+ };
312
+ }
313
+ function transformRowToBundle(row) {
314
+ return {
315
+ id: row.id,
316
+ channel: row.channel,
317
+ enabled: Boolean(row.enabled),
318
+ shouldForceUpdate: Boolean(row.should_force_update),
319
+ fileHash: row.file_hash,
320
+ gitCommitHash: row.git_commit_hash,
321
+ message: row.message,
322
+ platform: row.platform,
323
+ targetAppVersion: row.target_app_version,
324
+ storageUri: row.storage_uri,
325
+ fingerprintHash: row.fingerprint_hash,
326
+ metadata: row?.metadata ? JSON.parse(row?.metadata) : {}
327
+ };
328
+ }
296
329
  const d1Database = (config, hooks) => {
297
330
  let bundles = [];
331
+ async function getTotalCount(context, conditions) {
332
+ const { sql: whereClause, params } = buildWhereClause(conditions);
333
+ const countSql = (0, import_lib.default)(`SELECT COUNT(*) as total FROM bundles${whereClause}`);
334
+ const countResult = await context.cf.d1.database.query(config.databaseId, {
335
+ account_id: config.accountId,
336
+ sql: countSql,
337
+ params
338
+ });
339
+ const rows = await resolvePage(countResult);
340
+ return rows[0]?.total || 0;
341
+ }
342
+ async function getPaginatedBundles(context, conditions, limit, offset) {
343
+ const { sql: whereClause, params } = buildWhereClause(conditions);
344
+ const sql = (0, import_lib.default)(`
345
+ SELECT * FROM bundles
346
+ ${whereClause}
347
+ ORDER BY id DESC
348
+ LIMIT ?
349
+ OFFSET ?
350
+ `);
351
+ params.push(limit, offset);
352
+ const result = await context.cf.d1.database.query(config.databaseId, {
353
+ account_id: config.accountId,
354
+ sql,
355
+ params
356
+ });
357
+ const rows = await resolvePage(result);
358
+ return rows.map(transformRowToBundle);
359
+ }
298
360
  return createDatabasePlugin("d1Database", {
299
361
  getContext: () => ({ cf: new Cloudflare({ apiToken: config.cloudflareApiToken }) }),
300
362
  async getBundleById(context, bundleId) {
@@ -309,67 +371,21 @@ const d1Database = (config, hooks) => {
309
371
  });
310
372
  const rows = await resolvePage(singlePage);
311
373
  if (rows.length === 0) return null;
312
- const row = rows[0];
313
- return {
314
- channel: row.channel,
315
- enabled: Boolean(row.enabled),
316
- shouldForceUpdate: Boolean(row.should_force_update),
317
- fileHash: row.file_hash,
318
- gitCommitHash: row.git_commit_hash,
319
- id: row.id,
320
- message: row.message,
321
- platform: row.platform,
322
- targetAppVersion: row.target_app_version,
323
- storageUri: row.storage_uri,
324
- fingerprintHash: row.fingerprint_hash,
325
- metadata: row?.metadata ? JSON.parse(row?.metadata) : {}
326
- };
374
+ return transformRowToBundle(rows[0]);
327
375
  },
328
376
  async getBundles(context, options) {
329
- const { where, limit, offset = 0 } = options ?? {};
330
- let sql = "SELECT * FROM bundles";
331
- const params = [];
332
- const conditions = [];
333
- if (where?.channel) {
334
- conditions.push("channel = ?");
335
- params.push(where.channel);
336
- }
337
- if (where?.platform) {
338
- conditions.push("platform = ?");
339
- params.push(where.platform);
340
- }
341
- if (conditions.length > 0) sql += ` WHERE ${conditions.join(" AND ")}`;
342
- sql += " ORDER BY id DESC";
343
- if (limit) {
344
- sql += " LIMIT ?";
345
- params.push(limit);
346
- }
347
- if (offset) {
348
- sql += " OFFSET ?";
349
- params.push(offset);
350
- }
351
- const singlePage = await context.cf.d1.database.query(config.databaseId, {
352
- account_id: config.accountId,
353
- sql: (0, import_lib.default)(sql),
354
- params
355
- });
356
- const rows = await resolvePage(singlePage);
357
- if (rows.length === 0) bundles = [];
358
- else bundles = rows.map((row) => ({
359
- id: row.id,
360
- channel: row.channel,
361
- enabled: Boolean(row.enabled),
362
- shouldForceUpdate: Boolean(row.should_force_update),
363
- fileHash: row.file_hash,
364
- gitCommitHash: row.git_commit_hash,
365
- message: row.message,
366
- platform: row.platform,
367
- targetAppVersion: row.target_app_version,
368
- storageUri: row.storage_uri,
369
- fingerprintHash: row.fingerprint_hash,
370
- metadata: row?.metadata ? JSON.parse(row?.metadata) : {}
371
- }));
372
- return bundles;
377
+ const { where = {}, limit, offset } = options;
378
+ const totalCount = await getTotalCount(context, where);
379
+ bundles = await getPaginatedBundles(context, where, limit, offset);
380
+ const paginationOptions = {
381
+ limit,
382
+ offset
383
+ };
384
+ const pagination = calculatePagination(totalCount, paginationOptions);
385
+ return {
386
+ data: bundles,
387
+ pagination
388
+ };
373
389
  },
374
390
  async getChannels(context) {
375
391
  const sql = (0, import_lib.default)(`
@@ -1136,7 +1152,7 @@ const getVerboseObject = ({ type, result, verboseInfo: { escapedCommand, command
1136
1152
  type,
1137
1153
  escapedCommand,
1138
1154
  commandId: `${commandId}`,
1139
- timestamp: new Date(),
1155
+ timestamp: /* @__PURE__ */ new Date(),
1140
1156
  piped,
1141
1157
  result,
1142
1158
  options
@@ -2257,7 +2273,7 @@ const getFromStream = (source, from = "stdout") => {
2257
2273
  if (sourceStream === null || sourceStream === void 0) throw new TypeError(getInvalidStdioOptionMessage(fdNumber, from, options, isWritable));
2258
2274
  return sourceStream;
2259
2275
  };
2260
- const SUBPROCESS_OPTIONS = new WeakMap();
2276
+ const SUBPROCESS_OPTIONS = /* @__PURE__ */ new WeakMap();
2261
2277
  const getFdNumber = (fileDescriptors, fdName, isWritable) => {
2262
2278
  const fdNumber = parseFdNumber(fdName, isWritable);
2263
2279
  validateFdNumber(fdNumber, fdName, isWritable, fileDescriptors);
@@ -2386,7 +2402,7 @@ const onDisconnect = async ({ anyProcess, channel, isSubprocess, ipcEmitter, bou
2386
2402
  ipcEmitter.connected = false;
2387
2403
  ipcEmitter.emit("disconnect");
2388
2404
  };
2389
- const INCOMING_MESSAGES = new WeakMap();
2405
+ const INCOMING_MESSAGES = /* @__PURE__ */ new WeakMap();
2390
2406
 
2391
2407
  //#endregion
2392
2408
  //#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/ipc/forward.js
@@ -2403,7 +2419,7 @@ const getIpcEmitter = (anyProcess, channel, isSubprocess) => {
2403
2419
  });
2404
2420
  return ipcEmitter;
2405
2421
  };
2406
- const IPC_EMITTERS = new WeakMap();
2422
+ const IPC_EMITTERS = /* @__PURE__ */ new WeakMap();
2407
2423
  const forwardEvents = ({ ipcEmitter, anyProcess, channel, isSubprocess }) => {
2408
2424
  const boundOnMessage = onMessage.bind(void 0, {
2409
2425
  anyProcess,
@@ -2502,7 +2518,7 @@ const RESPONSE_TYPE = "execa:ipc:response";
2502
2518
  //#endregion
2503
2519
  //#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/ipc/outgoing.js
2504
2520
  const startSendMessage = (anyProcess, wrappedMessage, strict) => {
2505
- if (!OUTGOING_MESSAGES.has(anyProcess)) OUTGOING_MESSAGES.set(anyProcess, new Set());
2521
+ if (!OUTGOING_MESSAGES.has(anyProcess)) OUTGOING_MESSAGES.set(anyProcess, /* @__PURE__ */ new Set());
2506
2522
  const outgoingMessages = OUTGOING_MESSAGES.get(anyProcess);
2507
2523
  const onMessageSent = createDeferred();
2508
2524
  const id = strict ? wrappedMessage.id : void 0;
@@ -2527,7 +2543,7 @@ const waitForOutgoingMessages = async (anyProcess, ipcEmitter, wrappedMessage) =
2527
2543
  await Promise.all(outgoingMessages.map(({ onMessageSent }) => onMessageSent));
2528
2544
  }
2529
2545
  };
2530
- const OUTGOING_MESSAGES = new WeakMap();
2546
+ const OUTGOING_MESSAGES = /* @__PURE__ */ new WeakMap();
2531
2547
  const hasMessageListeners = (anyProcess, ipcEmitter) => ipcEmitter.listenerCount("message") > getMinListenerCount(anyProcess);
2532
2548
  const getMinListenerCount = (anyProcess) => SUBPROCESS_OPTIONS.has(anyProcess) && !getFdSpecificValue(SUBPROCESS_OPTIONS.get(anyProcess).options.buffer, "ipc") ? 1 : 0;
2533
2549
 
@@ -2599,7 +2615,7 @@ const getSendMethod = (anyProcess) => {
2599
2615
  PROCESS_SEND_METHODS.set(anyProcess, sendMethod);
2600
2616
  return sendMethod;
2601
2617
  };
2602
- const PROCESS_SEND_METHODS = new WeakMap();
2618
+ const PROCESS_SEND_METHODS = /* @__PURE__ */ new WeakMap();
2603
2619
 
2604
2620
  //#endregion
2605
2621
  //#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/ipc/graceful.js
@@ -2916,14 +2932,14 @@ const CR_BINARY = CR.codePointAt(0);
2916
2932
  function isStream(stream, { checkOpen = true } = {}) {
2917
2933
  return stream !== null && typeof stream === "object" && (stream.writable || stream.readable || !checkOpen || stream.writable === void 0 && stream.readable === void 0) && typeof stream.pipe === "function";
2918
2934
  }
2919
- function isWritableStream$1(stream, { checkOpen = true } = {}) {
2935
+ function isWritableStream(stream, { checkOpen = true } = {}) {
2920
2936
  return isStream(stream, { checkOpen }) && (stream.writable || !checkOpen) && typeof stream.write === "function" && typeof stream.end === "function" && typeof stream.writable === "boolean" && typeof stream.writableObjectMode === "boolean" && typeof stream.destroy === "function" && typeof stream.destroyed === "boolean";
2921
2937
  }
2922
- function isReadableStream$1(stream, { checkOpen = true } = {}) {
2938
+ function isReadableStream(stream, { checkOpen = true } = {}) {
2923
2939
  return isStream(stream, { checkOpen }) && (stream.readable || !checkOpen) && typeof stream.read === "function" && typeof stream.readable === "boolean" && typeof stream.readableObjectMode === "boolean" && typeof stream.destroy === "function" && typeof stream.destroyed === "boolean";
2924
2940
  }
2925
2941
  function isDuplexStream(stream, options) {
2926
- return isWritableStream$1(stream, options) && isReadableStream$1(stream, options);
2942
+ return isWritableStream(stream, options) && isReadableStream(stream, options);
2927
2943
  }
2928
2944
 
2929
2945
  //#endregion
@@ -3010,7 +3026,7 @@ function h({ preventCancel: r = !1 } = {}) {
3010
3026
  //#endregion
3011
3027
  //#region ../../node_modules/.pnpm/get-stream@9.0.1/node_modules/get-stream/source/stream.js
3012
3028
  const getAsyncIterable = (stream) => {
3013
- if (isReadableStream$1(stream, { checkOpen: false }) && nodeImports.on !== void 0) return getStreamIterable(stream);
3029
+ if (isReadableStream(stream, { checkOpen: false }) && nodeImports.on !== void 0) return getStreamIterable(stream);
3014
3030
  if (typeof stream?.[Symbol.asyncIterator] === "function") return stream;
3015
3031
  if (toString.call(stream) === "[object ReadableStream]") return h.call(stream);
3016
3032
  throw new TypeError("The first argument must be a Readable, a ReadableStream, or an async iterable.");
@@ -3709,10 +3725,10 @@ const KNOWN_STDIO_STRINGS = new Set([
3709
3725
  "overlapped",
3710
3726
  "pipe"
3711
3727
  ]);
3712
- const isReadableStream = (value) => Object.prototype.toString.call(value) === "[object ReadableStream]";
3713
- const isWritableStream = (value) => Object.prototype.toString.call(value) === "[object WritableStream]";
3714
- const isWebStream = (value) => isReadableStream(value) || isWritableStream(value);
3715
- const isTransformStream = (value) => isReadableStream(value?.readable) && isWritableStream(value?.writable);
3728
+ const isReadableStream$1 = (value) => Object.prototype.toString.call(value) === "[object ReadableStream]";
3729
+ const isWritableStream$1 = (value) => Object.prototype.toString.call(value) === "[object WritableStream]";
3730
+ const isWebStream = (value) => isReadableStream$1(value) || isWritableStream$1(value);
3731
+ const isTransformStream = (value) => isReadableStream$1(value?.readable) && isWritableStream$1(value?.writable);
3716
3732
  const isAsyncIterableObject = (value) => isObject(value) && typeof value[Symbol.asyncIterator] === "function";
3717
3733
  const isIterableObject = (value) => isObject(value) && typeof value[Symbol.iterator] === "function";
3718
3734
  const isObject = (value) => typeof value === "object" && value !== null;
@@ -3876,10 +3892,10 @@ const guessStreamDirection = {
3876
3892
  iterable: alwaysInput,
3877
3893
  asyncIterable: alwaysInput,
3878
3894
  uint8Array: alwaysInput,
3879
- webStream: (value) => isWritableStream(value) ? "output" : "input",
3895
+ webStream: (value) => isWritableStream$1(value) ? "output" : "input",
3880
3896
  nodeStream(value) {
3881
- if (!isReadableStream$1(value, { checkOpen: false })) return "output";
3882
- return isWritableStream$1(value, { checkOpen: false }) ? void 0 : "input";
3897
+ if (!isReadableStream(value, { checkOpen: false })) return "output";
3898
+ return isWritableStream(value, { checkOpen: false }) ? void 0 : "input";
3883
3899
  },
3884
3900
  webTransform: anyDirection,
3885
3901
  duplex: anyDirection,
@@ -4009,7 +4025,7 @@ const handleInputOption = (input) => input === void 0 ? [] : [{
4009
4025
  optionName: "input"
4010
4026
  }];
4011
4027
  const getInputType = (input) => {
4012
- if (isReadableStream$1(input, { checkOpen: false })) return "nodeStream";
4028
+ if (isReadableStream(input, { checkOpen: false })) return "nodeStream";
4013
4029
  if (typeof input === "string") return "string";
4014
4030
  if (isUint8Array(input)) return "uint8Array";
4015
4031
  throw new Error("The `input` option must be a string, a Uint8Array or a Node.js Readable stream.");
@@ -5147,7 +5163,7 @@ var MergedStream = class extends PassThrough {
5147
5163
  #aborted = new Set([]);
5148
5164
  #onFinished;
5149
5165
  #unpipeEvent = Symbol("unpipe");
5150
- #streamPromises = new WeakMap();
5166
+ #streamPromises = /* @__PURE__ */ new WeakMap();
5151
5167
  add(stream) {
5152
5168
  validateStream(stream);
5153
5169
  if (this.#streams.has(stream)) return;
@@ -5327,7 +5343,7 @@ const abortSourceStream = (source) => {
5327
5343
  //#endregion
5328
5344
  //#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/io/output-async.js
5329
5345
  const pipeOutputAsync = (subprocess, fileDescriptors, controller) => {
5330
- const pipeGroups = new Map();
5346
+ const pipeGroups = /* @__PURE__ */ new Map();
5331
5347
  for (const [fdNumber, { stdioItems, direction }] of Object.entries(fileDescriptors)) {
5332
5348
  for (const { stream } of stdioItems.filter(({ type }) => TRANSFORM_TYPES.has(type))) pipeTransform(subprocess, stream, direction, fdNumber);
5333
5349
  for (const { stream } of stdioItems.filter(({ type }) => !TRANSFORM_TYPES.has(type))) pipeStdioItem({
@@ -5733,7 +5749,7 @@ const cleanupMergedStreamsMap = async (destinationStream) => {
5733
5749
  } catch {}
5734
5750
  MERGED_STREAMS.delete(destinationStream);
5735
5751
  };
5736
- const MERGED_STREAMS = new WeakMap();
5752
+ const MERGED_STREAMS = /* @__PURE__ */ new WeakMap();
5737
5753
  const SOURCE_LISTENERS_PER_PIPE = 2;
5738
5754
  const DESTINATION_LISTENERS_PER_PIPE = 1;
5739
5755
 
@@ -6219,9 +6235,9 @@ const throwOnSubprocessError = async (subprocess, { signal }) => {
6219
6235
  //#endregion
6220
6236
  //#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/convert/concurrent.js
6221
6237
  const initializeConcurrentStreams = () => ({
6222
- readableDestroy: new WeakMap(),
6223
- writableFinal: new WeakMap(),
6224
- writableDestroy: new WeakMap()
6238
+ readableDestroy: /* @__PURE__ */ new WeakMap(),
6239
+ writableFinal: /* @__PURE__ */ new WeakMap(),
6240
+ writableDestroy: /* @__PURE__ */ new WeakMap()
6225
6241
  });
6226
6242
  const addConcurrentStream = (concurrentStreams, stream, waitName) => {
6227
6243
  const weakMap = concurrentStreams[waitName];
@@ -8116,16 +8132,16 @@ var __classPrivateFieldGet = void 0 && (void 0).__classPrivateFieldGet || functi
8116
8132
  var _Mime_extensionToType, _Mime_typeToExtension, _Mime_typeToExtensions;
8117
8133
  var Mime = class {
8118
8134
  constructor(...args) {
8119
- _Mime_extensionToType.set(this, new Map());
8120
- _Mime_typeToExtension.set(this, new Map());
8121
- _Mime_typeToExtensions.set(this, new Map());
8135
+ _Mime_extensionToType.set(this, /* @__PURE__ */ new Map());
8136
+ _Mime_typeToExtension.set(this, /* @__PURE__ */ new Map());
8137
+ _Mime_typeToExtensions.set(this, /* @__PURE__ */ new Map());
8122
8138
  for (const arg of args) this.define(arg);
8123
8139
  }
8124
8140
  define(typeMap, force = false) {
8125
8141
  for (let [type, extensions] of Object.entries(typeMap)) {
8126
8142
  type = type.toLowerCase();
8127
8143
  extensions = extensions.map((ext) => ext.toLowerCase());
8128
- if (!__classPrivateFieldGet(this, _Mime_typeToExtensions, "f").has(type)) __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").set(type, new Set());
8144
+ if (!__classPrivateFieldGet(this, _Mime_typeToExtensions, "f").has(type)) __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").set(type, /* @__PURE__ */ new Set());
8129
8145
  const allExtensions = __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type);
8130
8146
  let first = true;
8131
8147
  for (let extension of extensions) {
@@ -8175,7 +8191,7 @@ var Mime = class {
8175
8191
  };
8176
8192
  }
8177
8193
  };
8178
- _Mime_extensionToType = new WeakMap(), _Mime_typeToExtension = new WeakMap(), _Mime_typeToExtensions = new WeakMap();
8194
+ _Mime_extensionToType = /* @__PURE__ */ new WeakMap(), _Mime_typeToExtension = /* @__PURE__ */ new WeakMap(), _Mime_typeToExtensions = /* @__PURE__ */ new WeakMap();
8179
8195
  var Mime_default = Mime;
8180
8196
 
8181
8197
  //#endregion
@@ -8202,8 +8218,8 @@ const r2Storage = (config, hooks) => (_) => {
8202
8218
  const filename = path.basename(bundlePath);
8203
8219
  const Key = [bundleId, filename].join("/");
8204
8220
  try {
8205
- const { stderr } = await wrangler("r2", "object", "put", [bucketName, Key].join("/"), "--file", bundlePath, ...contentType ? ["--content-type", contentType] : [], "--remote");
8206
- if (stderr) throw new Error(stderr);
8221
+ const { stderr, exitCode } = await wrangler("r2", "object", "put", [bucketName, Key].join("/"), "--file", bundlePath, ...contentType ? ["--content-type", contentType] : [], "--remote");
8222
+ if (exitCode !== 0 && stderr) throw new Error(stderr);
8207
8223
  } catch (error$1) {
8208
8224
  if (error$1 instanceof ExecaError) throw new Error(error$1.stderr || error$1.stdout);
8209
8225
  throw error$1;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/cloudflare",
3
3
  "type": "module",
4
- "version": "0.18.1",
4
+ "version": "0.18.2",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.js",
@@ -40,9 +40,9 @@
40
40
  ],
41
41
  "dependencies": {
42
42
  "cloudflare": "4.2.0",
43
- "@hot-updater/core": "0.18.1",
44
- "@hot-updater/js": "0.18.1",
45
- "@hot-updater/plugin-core": "0.18.1"
43
+ "@hot-updater/core": "0.18.2",
44
+ "@hot-updater/plugin-core": "0.18.2",
45
+ "@hot-updater/js": "0.18.2"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@clack/prompts": "0.10.0",
@@ -1 +1 @@
1
- This folder contains the built output assets for the worker "hot-updater" generated at 2025-05-30T15:18:41.452Z.
1
+ This folder contains the built output assets for the worker "hot-updater" generated at 2025-06-03T09:04:33.110Z.
@@ -2247,7 +2247,7 @@ var require_semver = __commonJS({ "../../node_modules/.pnpm/semver@7.7.2/node_mo
2247
2247
  rcompareIdentifiers: identifiers.rcompareIdentifiers
2248
2248
  };
2249
2249
  } });
2250
- var import_semver = __toESM(require_semver(), 1);
2250
+ var import_semver = __toESM(require_semver());
2251
2251
  var semverSatisfies = /* @__PURE__ */ __name((targetAppVersion, currentVersion) => {
2252
2252
  const currentCoerce = import_semver.default.coerce(currentVersion);
2253
2253
  if (!currentCoerce) return false;