@mark1russell7/layout-contract 0.2.4 → 0.2.6

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/build/index.js CHANGED
@@ -681,6 +681,19 @@ function compareMeasure(candidate, baseline, spec) {
681
681
  if (!(Math.abs(delta) > minEffectOf(spec, candidate, baseline))) return "tie";
682
682
  return (spec.direction === "lower" ? delta < 0 : delta > 0) ? "better" : "worse";
683
683
  }
684
+ // src/utf8.ts
685
+ /** This function gives the UTF-8 bytes of a string. The package has no runtime dependencies, thus it does not use `TextEncoder`. */
686
+ function utf8(text) {
687
+ const bytes = [];
688
+ for (const ch of text) {
689
+ const c = ch.codePointAt(0);
690
+ if (c < 128) bytes.push(c);
691
+ else if (c < 2048) bytes.push(192 | c >> 6, 128 | c & 63);
692
+ else if (c < 65536) bytes.push(224 | c >> 12, 128 | c >> 6 & 63, 128 | c & 63);
693
+ else bytes.push(240 | c >> 18, 128 | c >> 12 & 63, 128 | c >> 6 & 63, 128 | c & 63);
694
+ }
695
+ return bytes;
696
+ }
684
697
  // src/random.ts
685
698
  /** This function gives a generator of random numbers in [0, 1). It is `mulberry32`, as in `docs/FORMAT.md` of `packing`. */
686
699
  function mulberry32(seed) {
@@ -698,18 +711,6 @@ function fnv1a32(text) {
698
711
  for (const b of utf8(text)) h = Math.imul(h ^ b, 16777619) >>> 0;
699
712
  return h;
700
713
  }
701
- /** The UTF-8 bytes of a string. The package has no runtime dependencies, thus it does not use `TextEncoder`. */
702
- function utf8(text) {
703
- const bytes = [];
704
- for (const ch of text) {
705
- const c = ch.codePointAt(0);
706
- if (c < 128) bytes.push(c);
707
- else if (c < 2048) bytes.push(192 | c >> 6, 128 | c & 63);
708
- else if (c < 65536) bytes.push(224 | c >> 12, 128 | c >> 6 & 63, 128 | c & 63);
709
- else bytes.push(240 | c >> 18, 128 | c >> 12 & 63, 128 | c >> 6 & 63, 128 | c & 63);
710
- }
711
- return bytes;
712
- }
713
714
  /** This function gives the generator of one step of a seeded generator: `mulberry32(fnv1a32("<seed>:<step>"))`. */
714
715
  function stepRandom(seed, step) {
715
716
  return mulberry32(fnv1a32(`${seed}:${step}`));
@@ -914,6 +915,449 @@ function edgeStep(input, params, seed = input.seed ?? 0) {
914
915
  steps: [...input.steps ?? [], stamp]
915
916
  };
916
917
  }
917
- export { DEFAULT_TOLERANCE, EDGE_DENSITY, EDGE_STEP_VERSION, EPS, JOINT_MEASURES, JOINT_MOVED_MIN, RECORD_EPS, SIDES, applyPack, boundsOf, checkInput, compareMeasure, countObstacleHits, countOutside, countOverlaps, countPortsOffBorder, edgeStep, fnv1a32, grow, marginSides, minEffectOf, mulberry32, overlapOf, packHard, placedRect, randomInt, rectInRegion, regionHitsRect, stepRandom, toLayoutRecord, toPackInput, translate, turnBox, turnMargin, turnPoint, turnPort, turnRegion, turnSide };
918
+ // src/hash.ts
919
+ /** The round constants of SHA-256. */
920
+ const K = [
921
+ 1116352408,
922
+ 1899447441,
923
+ 3049323471,
924
+ 3921009573,
925
+ 961987163,
926
+ 1508970993,
927
+ 2453635748,
928
+ 2870763221,
929
+ 3624381080,
930
+ 310598401,
931
+ 607225278,
932
+ 1426881987,
933
+ 1925078388,
934
+ 2162078206,
935
+ 2614888103,
936
+ 3248222580,
937
+ 3835390401,
938
+ 4022224774,
939
+ 264347078,
940
+ 604807628,
941
+ 770255983,
942
+ 1249150122,
943
+ 1555081692,
944
+ 1996064986,
945
+ 2554220882,
946
+ 2821834349,
947
+ 2952996808,
948
+ 3210313671,
949
+ 3336571891,
950
+ 3584528711,
951
+ 113926993,
952
+ 338241895,
953
+ 666307205,
954
+ 773529912,
955
+ 1294757372,
956
+ 1396182291,
957
+ 1695183700,
958
+ 1986661051,
959
+ 2177026350,
960
+ 2456956037,
961
+ 2730485921,
962
+ 2820302411,
963
+ 3259730800,
964
+ 3345764771,
965
+ 3516065817,
966
+ 3600352804,
967
+ 4094571909,
968
+ 275423344,
969
+ 430227734,
970
+ 506948616,
971
+ 659060556,
972
+ 883997877,
973
+ 958139571,
974
+ 1322822218,
975
+ 1537002063,
976
+ 1747873779,
977
+ 1955562222,
978
+ 2024104815,
979
+ 2227730452,
980
+ 2361852424,
981
+ 2428436474,
982
+ 2756734187,
983
+ 3204031479,
984
+ 3329325298
985
+ ];
986
+ /**
987
+ * This function gives the SHA-256 digest of the UTF-8 bytes of a string, as 64 lowercase hex characters.
988
+ * It uses no API of Node or of the browser.
989
+ */
990
+ function sha256Hex(text) {
991
+ const bytes = utf8(text);
992
+ const bits = bytes.length * 8;
993
+ const total = Math.ceil((bytes.length + 9) / 64) * 64;
994
+ const data = new Uint8Array(total);
995
+ data.set(bytes);
996
+ data[bytes.length] = 128;
997
+ const view = new DataView(data.buffer);
998
+ view.setUint32(total - 8, Math.floor(bits / 4294967296));
999
+ view.setUint32(total - 4, bits >>> 0);
1000
+ const h = [
1001
+ 1779033703,
1002
+ 3144134277,
1003
+ 1013904242,
1004
+ 2773480762,
1005
+ 1359893119,
1006
+ 2600822924,
1007
+ 528734635,
1008
+ 1541459225
1009
+ ];
1010
+ const w = /* @__PURE__ */ new Uint32Array(64);
1011
+ const rotr = (x, n) => x >>> n | x << 32 - n;
1012
+ for (let off = 0; off < total; off += 64) {
1013
+ for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + 4 * i);
1014
+ for (let i = 16; i < 64; i++) {
1015
+ const a = w[i - 15];
1016
+ const b = w[i - 2];
1017
+ const s0 = rotr(a, 7) ^ rotr(a, 18) ^ a >>> 3;
1018
+ const s1 = rotr(b, 17) ^ rotr(b, 19) ^ b >>> 10;
1019
+ w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
1020
+ }
1021
+ let [a, b, c, d, e, f, g, k] = h;
1022
+ for (let i = 0; i < 64; i++) {
1023
+ const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
1024
+ const ch = e & f ^ ~e & g;
1025
+ const t1 = k + s1 + ch + K[i] + w[i] >>> 0;
1026
+ const t2 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
1027
+ k = g;
1028
+ g = f;
1029
+ f = e;
1030
+ e = d + t1 >>> 0;
1031
+ d = c;
1032
+ c = b;
1033
+ b = a;
1034
+ a = t1 + t2 >>> 0;
1035
+ }
1036
+ h[0] = h[0] + a >>> 0;
1037
+ h[1] = h[1] + b >>> 0;
1038
+ h[2] = h[2] + c >>> 0;
1039
+ h[3] = h[3] + d >>> 0;
1040
+ h[4] = h[4] + e >>> 0;
1041
+ h[5] = h[5] + f >>> 0;
1042
+ h[6] = h[6] + g >>> 0;
1043
+ h[7] = h[7] + k >>> 0;
1044
+ }
1045
+ return h.map((x) => x.toString(16).padStart(8, "0")).join("");
1046
+ }
1047
+ /**
1048
+ * This function gives the canonical form of a value: sorted keys and no whitespace, with the numbers of `JSON.stringify`.
1049
+ * A key with the value `undefined` is not in the form. An `undefined` in an array becomes `null`.
1050
+ */
1051
+ function canonicalJson(value) {
1052
+ if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
1053
+ if (Array.isArray(value)) return `[${value.map((v) => v === void 0 ? "null" : canonicalJson(v)).join(",")}]`;
1054
+ const o = value;
1055
+ return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(o[k])}`).join(",")}}`;
1056
+ }
1057
+ /** This function gives the factor of a grid: `1 / grid`, or `undefined` for the exact numbers. */
1058
+ function gridFactor(options) {
1059
+ const grid = options.grid ?? .001;
1060
+ if (!Number.isFinite(grid) || grid < 0) throw new RangeError(`The grid ${grid} is not a finite number of 0 or more.`);
1061
+ return grid === 0 ? void 0 : 1 / grid;
1062
+ }
1063
+ /**
1064
+ * This function gives a copy of a value with each number on the grid. The value `-0` becomes `0`.
1065
+ * It throws a `RangeError` for a number that is not finite.
1066
+ */
1067
+ function onGrid(value, path, k) {
1068
+ if (typeof value === "number") {
1069
+ if (!Number.isFinite(value)) throw new RangeError(`The value has the number ${value} at ${path}. JSON has no form for it.`);
1070
+ if (k === void 0) return value;
1071
+ const r = Math.round(value * k) / k;
1072
+ if (!Number.isFinite(r)) throw new RangeError(`The number at ${path} is too large for the grid.`);
1073
+ return r === 0 ? 0 : r;
1074
+ }
1075
+ if (value === null || typeof value !== "object") return value;
1076
+ if (Array.isArray(value)) return value.map((v, i) => onGrid(v, `${path}[${i}]`, k));
1077
+ const o = value;
1078
+ const out = {};
1079
+ for (const key of Object.keys(o)) out[key] = onGrid(o[key], `${path}.${key}`, k);
1080
+ return out;
1081
+ }
1082
+ /**
1083
+ * This function gives the hash of an output: the first 16 hex characters of the SHA-256 of its canonical form.
1084
+ * Before the canonical form, each number goes to the nearest point of the grid, and `-0` becomes `0`.
1085
+ * With the default grid, the rounding is `round3` of `packing`: `Math.round(x * 1000) / 1000`.
1086
+ * The function throws a `RangeError` for `NaN` and for an infinite number.
1087
+ *
1088
+ * The grid can split two outputs. Two numbers near a rounding boundary can go to two different points.
1089
+ * The grid can also merge two outputs whose numbers go to the same points. This merge is intentional: it removes the noise of floating-point numbers.
1090
+ */
1091
+ function outputHash(value, options = {}) {
1092
+ return sha256Hex(canonicalJson(onGrid(value, "$", gridFactor(options)))).slice(0, 16);
1093
+ }
1094
+ /**
1095
+ * This function gives the hash of a document. It uses the rule of `outputHash`, but the form does not include the field `hash` of the document.
1096
+ * With the default grid, it is the input hash of `docs/FORMAT.md` of `packing`.
1097
+ */
1098
+ function documentHash(doc, options = {}) {
1099
+ const { hash: _hash, ...rest } = doc;
1100
+ return outputHash(rest, options);
1101
+ }
1102
+ /**
1103
+ * This function gives the output groups of the candidates. The key of `hashes` is the id of a candidate.
1104
+ * Its value is the list of the output hashes of the candidate, in the order of the decision inputs.
1105
+ * Candidates with equal lists make one group. The groups are in the order of their ids.
1106
+ * The function throws a `RangeError` when two lists have different lengths.
1107
+ */
1108
+ function outputGroups(hashes) {
1109
+ const byList = /* @__PURE__ */ new Map();
1110
+ let first;
1111
+ for (const id of Object.keys(hashes)) {
1112
+ const list = hashes[id];
1113
+ if (first === void 0) first = id;
1114
+ else if (list.length !== hashes[first].length) throw new RangeError(`The candidate "${id}" has ${list.length} hashes, but "${first}" has ${hashes[first].length}.`);
1115
+ const key = JSON.stringify(list);
1116
+ const members = byList.get(key);
1117
+ if (members) members.push(id);
1118
+ else byList.set(key, [id]);
1119
+ }
1120
+ return [...byList.values()].map((members) => {
1121
+ const sorted = [...members].sort();
1122
+ return {
1123
+ id: sorted[0],
1124
+ members: sorted
1125
+ };
1126
+ }).sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
1127
+ }
1128
+ /**
1129
+ * This function tells if a candidate is exercised. It gives `true` when a move count is more than 0.
1130
+ * It also gives `true` when `hashes` and `baseHashes` are different at one place or more.
1131
+ * Without evidence, it gives `false`.
1132
+ * The function throws a `RangeError` when only one of the two lists is given, or when their lengths are different.
1133
+ */
1134
+ function exercised(evidence) {
1135
+ const { moves, hashes, baseHashes } = evidence;
1136
+ if (hashes === void 0 !== (baseHashes === void 0)) throw new RangeError("The evidence must have both hashes and baseHashes, or neither of them.");
1137
+ if (hashes && baseHashes && hashes.length !== baseHashes.length) throw new RangeError(`The candidate has ${hashes.length} hashes, but its base has ${baseHashes.length}.`);
1138
+ if (moves && Object.values(moves).some((n) => n > 0)) return true;
1139
+ if (hashes && baseHashes) return hashes.some((h, i) => h !== baseHashes[i]);
1140
+ return false;
1141
+ }
1142
+ // src/portfolio.ts
1143
+ /**
1144
+ * This function picks the best candidate. For each level, it finds the best value of the candidates that stay in.
1145
+ * It keeps each candidate that is not worse than that best value by `compareMeasure`.
1146
+ * The winner is the first candidate, in the given order, that stays in after the last level.
1147
+ * Without levels, the winner is the first candidate and `kept` is empty.
1148
+ *
1149
+ * The reason for the rule: a scan that compares each candidate with the current winner is not transitive, because of the `minEffect` ties.
1150
+ * Such a scan can drift away from the best value. An example: "a" is lower with the `minEffect` 0.005, and "b" is higher.
1151
+ * The candidates A (0, 1), B (0.004, 2) and C (0.008, 3) give the winner B.
1152
+ * The scan picks C, but A is better than C on "a".
1153
+ *
1154
+ * The function throws a `RangeError` for an empty list of candidates, for a missing value, and for a value that is not finite.
1155
+ */
1156
+ function selectBest(candidates, levels) {
1157
+ if (candidates.length === 0) throw new RangeError("selectBest: the list of candidates is empty.");
1158
+ for (const level of levels) for (const c of candidates) {
1159
+ const v = c.values[level.id];
1160
+ if (typeof v !== "number") throw new RangeError(`selectBest: the candidate "${c.id}" has no value for the level "${level.id}".`);
1161
+ if (!Number.isFinite(v)) throw new RangeError(`selectBest: the candidate "${c.id}" has the value ${v} for the level "${level.id}".`);
1162
+ }
1163
+ let alive = candidates;
1164
+ const kept = [];
1165
+ for (const level of levels) {
1166
+ const at = (c) => c.values[level.id];
1167
+ let best = at(alive[0]);
1168
+ for (const c of alive) best = level.spec.direction === "lower" ? Math.min(best, at(c)) : Math.max(best, at(c));
1169
+ alive = alive.filter((c) => compareMeasure(at(c), best, level.spec) !== "worse");
1170
+ kept.push(alive.map((c) => c.id));
1171
+ }
1172
+ return {
1173
+ winner: alive[0].id,
1174
+ kept
1175
+ };
1176
+ }
1177
+ /** This function gives the move `pick` of a pick: 1 when `picked` is true, else 0. A record keeps it as the trace measure `move.pick`. */
1178
+ function pickMoves(trace) {
1179
+ return { pick: trace.picked ? 1 : 0 };
1180
+ }
1181
+ /** This function gives the default clock: `performance.now`, or `Date.now` without `performance`. */
1182
+ function defaultClock() {
1183
+ const perf = globalThis.performance;
1184
+ return perf ? () => perf.now() : () => Date.now();
1185
+ }
1186
+ /** This function gives the message of an error value. */
1187
+ function messageOf(e) {
1188
+ return e instanceof Error ? e.message : String(e);
1189
+ }
1190
+ /** This function throws a `RangeError` for an empty list of members or for two members with the same id. */
1191
+ function checkMembers(id, members) {
1192
+ if (members.length === 0) throw new RangeError(`The portfolio "${id}" has no members.`);
1193
+ const seen = /* @__PURE__ */ new Set();
1194
+ for (const m of members) {
1195
+ if (seen.has(m.id)) throw new RangeError(`The portfolio "${id}" has two members with the id "${m.id}".`);
1196
+ seen.add(m.id);
1197
+ }
1198
+ }
1199
+ /**
1200
+ * This function makes a portfolio. Its `pick` starts the members in their order. `selectBest` picks the winner of the members with the status `ran`.
1201
+ * The first level is the hard level, then the levels of the key follow.
1202
+ * `members` is a list, or a function of the input that gives the list.
1203
+ *
1204
+ * A member that throws, or that gives a value that is not finite, gets the status `error`. It does not win.
1205
+ * When each member has an error, `pick` throws the error of the first member.
1206
+ * `docs/PORTFOLIO.md` tells the rules of the time limit, the trace and `picked`.
1207
+ *
1208
+ * The function throws a `RangeError` for a level with the id `hard`, for two levels with the same id and for a bad option.
1209
+ * It also throws a `RangeError` for an empty list of members, or for two members with the same id.
1210
+ */
1211
+ function portfolio(id, members, key, options = {}) {
1212
+ const levelIds = /* @__PURE__ */ new Set();
1213
+ for (const level of key.levels) {
1214
+ if (level.id === "hard") throw new RangeError(`The portfolio "${id}" has a level with the reserved id "hard".`);
1215
+ if (levelIds.has(level.id)) throw new RangeError(`The portfolio "${id}" has two levels with the id "${level.id}".`);
1216
+ levelIds.add(level.id);
1217
+ }
1218
+ const { budgetMs, maxMembers } = options;
1219
+ if (budgetMs !== void 0 && !(budgetMs >= 0)) throw new RangeError(`The portfolio "${id}" has the budgetMs ${budgetMs}. It must be 0 or more.`);
1220
+ if (maxMembers !== void 0 && !(Number.isInteger(maxMembers) && maxMembers >= 1)) throw new RangeError(`The portfolio "${id}" has the maxMembers ${maxMembers}. It must be a whole number of 1 or more.`);
1221
+ if (typeof members !== "function") checkMembers(id, members);
1222
+ const levels = [{
1223
+ id: "hard",
1224
+ spec: {
1225
+ direction: "lower",
1226
+ minEffect: 0
1227
+ }
1228
+ }, ...key.levels.map((l) => ({
1229
+ id: l.id,
1230
+ spec: l.spec
1231
+ }))];
1232
+ const now = options.now ?? defaultClock();
1233
+ const evaluate = (m, input, output) => {
1234
+ const values = {};
1235
+ const measure = (level, f) => {
1236
+ let v;
1237
+ try {
1238
+ v = f();
1239
+ } catch (e) {
1240
+ throw new RangeError(`The level "${level}" of the member "${m.id}" of the portfolio "${id}" throws: ${messageOf(e)}`, { cause: e });
1241
+ }
1242
+ if (typeof v !== "number" || !Number.isFinite(v)) throw new RangeError(`The member "${m.id}" of the portfolio "${id}" has the value ${v} for the level "${level}".`);
1243
+ values[level] = v;
1244
+ };
1245
+ try {
1246
+ measure("hard", () => key.hard(input, output));
1247
+ for (const level of key.levels) measure(level.id, () => level.value(input, output));
1248
+ let hash;
1249
+ try {
1250
+ hash = key.hash ? key.hash(output) : outputHash(output);
1251
+ } catch (e) {
1252
+ throw new RangeError(`The hash of the member "${m.id}" of the portfolio "${id}" throws: ${messageOf(e)}`, { cause: e });
1253
+ }
1254
+ return {
1255
+ ok: true,
1256
+ output,
1257
+ values,
1258
+ hash
1259
+ };
1260
+ } catch (e) {
1261
+ return {
1262
+ ok: false,
1263
+ error: e
1264
+ };
1265
+ }
1266
+ };
1267
+ const pick = (input) => {
1268
+ const list = typeof members === "function" ? members(input) : members;
1269
+ if (typeof members === "function") checkMembers(id, list);
1270
+ const start = now();
1271
+ const traces = [];
1272
+ const outputs = /* @__PURE__ */ new Map();
1273
+ const ran = [];
1274
+ let firstError;
1275
+ let cut = 0;
1276
+ let outOfTime = false;
1277
+ for (let i = 0; i < list.length; i++) {
1278
+ const m = list[i];
1279
+ if (maxMembers !== void 0 && i >= maxMembers) {
1280
+ traces.push({
1281
+ id: m.id,
1282
+ status: "cut"
1283
+ });
1284
+ cut++;
1285
+ continue;
1286
+ }
1287
+ if (i > 0 && budgetMs !== void 0 && (outOfTime || now() - start >= budgetMs)) {
1288
+ outOfTime = true;
1289
+ traces.push({
1290
+ id: m.id,
1291
+ status: "cut"
1292
+ });
1293
+ cut++;
1294
+ continue;
1295
+ }
1296
+ const t0 = now();
1297
+ let result;
1298
+ let ms;
1299
+ try {
1300
+ const output = m.run(input);
1301
+ ms = now() - t0;
1302
+ result = evaluate(m, input, output);
1303
+ } catch (e) {
1304
+ ms = now() - t0;
1305
+ result = {
1306
+ ok: false,
1307
+ error: e
1308
+ };
1309
+ }
1310
+ if (result.ok) {
1311
+ traces.push({
1312
+ id: m.id,
1313
+ status: "ran",
1314
+ values: result.values,
1315
+ ms,
1316
+ outputHash: result.hash
1317
+ });
1318
+ outputs.set(m.id, result.output);
1319
+ ran.push({
1320
+ id: m.id,
1321
+ values: result.values
1322
+ });
1323
+ } else {
1324
+ traces.push({
1325
+ id: m.id,
1326
+ status: "error",
1327
+ ms,
1328
+ error: messageOf(result.error)
1329
+ });
1330
+ firstError ??= { error: result.error };
1331
+ }
1332
+ }
1333
+ if (ran.length === 0) throw firstError.error;
1334
+ const { winner, kept } = selectBest(ran, levels);
1335
+ const winnerIndex = traces.findIndex((t) => t.id === winner);
1336
+ const base = traces[0];
1337
+ const trace = {
1338
+ members: traces,
1339
+ winner,
1340
+ winnerIndex,
1341
+ picked: winnerIndex > 0 && (base.status !== "ran" || traces[winnerIndex].outputHash !== base.outputHash),
1342
+ totalMs: now() - start,
1343
+ ...budgetMs !== void 0 ? { budgetMs } : {},
1344
+ ...maxMembers !== void 0 ? { maxMembers } : {},
1345
+ cut,
1346
+ deterministic: !outOfTime,
1347
+ kept
1348
+ };
1349
+ return {
1350
+ output: outputs.get(winner),
1351
+ winner,
1352
+ trace
1353
+ };
1354
+ };
1355
+ return {
1356
+ id,
1357
+ pick,
1358
+ run: (input) => pick(input).output
1359
+ };
1360
+ }
1361
+ export { DEFAULT_TOLERANCE, EDGE_DENSITY, EDGE_STEP_VERSION, EPS, JOINT_MEASURES, JOINT_MOVED_MIN, RECORD_EPS, SIDES, applyPack, boundsOf, canonicalJson, checkInput, compareMeasure, countObstacleHits, countOutside, countOverlaps, countPortsOffBorder, documentHash, edgeStep, exercised, fnv1a32, grow, marginSides, minEffectOf, mulberry32, outputGroups, outputHash, overlapOf, packHard, pickMoves, placedRect, portfolio, randomInt, rectInRegion, regionHitsRect, selectBest, sha256Hex, stepRandom, toLayoutRecord, toPackInput, translate, turnBox, turnMargin, turnPoint, turnPort, turnRegion, turnSide };
918
1362
 
919
1363
  //# sourceMappingURL=index.js.map