@open-charging-cloud/wwcp-poi 0.3.0 → 0.4.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 (37) hide show
  1. package/CHANGELOG.md +1155 -836
  2. package/README.md +88 -32
  3. package/dist/common.d.ts +11 -0
  4. package/dist/common.d.ts.map +1 -1
  5. package/dist/evTaler/contractTerms.d.ts +19 -0
  6. package/dist/evTaler/contractTerms.d.ts.map +1 -1
  7. package/dist/history/apply.d.ts.map +1 -1
  8. package/dist/history/changeSet.d.ts +12 -2
  9. package/dist/history/changeSet.d.ts.map +1 -1
  10. package/dist/history/commit.d.ts +8 -2
  11. package/dist/history/commit.d.ts.map +1 -1
  12. package/dist/history/preconditions.d.ts +3 -2
  13. package/dist/history/preconditions.d.ts.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +835 -324
  17. package/dist/index.js.map +1 -1
  18. package/dist/network/evTaler.d.ts +6 -0
  19. package/dist/network/evTaler.d.ts.map +1 -1
  20. package/dist/network/poiJSON.d.ts +4 -1
  21. package/dist/network/poiJSON.d.ts.map +1 -1
  22. package/dist/network/poiValues.d.ts +9 -2
  23. package/dist/network/poiValues.d.ts.map +1 -1
  24. package/dist/network/reading.d.ts +13 -1
  25. package/dist/network/reading.d.ts.map +1 -1
  26. package/dist/network/references.d.ts +10 -3
  27. package/dist/network/references.d.ts.map +1 -1
  28. package/dist/network/roamingNetwork.d.ts +7 -1
  29. package/dist/network/roamingNetwork.d.ts.map +1 -1
  30. package/dist/network/writtenForm.d.ts +27 -1
  31. package/dist/network/writtenForm.d.ts.map +1 -1
  32. package/dist/proofs/keyStatement.d.ts.map +1 -1
  33. package/dist/verifier/grossRates.d.ts +11 -0
  34. package/dist/verifier/grossRates.d.ts.map +1 -0
  35. package/dist/verifier/sessionPrices.d.ts +3 -0
  36. package/dist/verifier/sessionPrices.d.ts.map +1 -1
  37. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -270,7 +270,7 @@ var Parser = class {
270
270
  }
271
271
  parseObject(depth) {
272
272
  const object2 = {};
273
- const names = /* @__PURE__ */ new Set();
273
+ const names2 = /* @__PURE__ */ new Set();
274
274
  this.position++;
275
275
  this.skipWhitespace();
276
276
  if (this.text[this.position] === "}") {
@@ -282,9 +282,9 @@ var Parser = class {
282
282
  if (this.text[this.position] !== '"')
283
283
  this.fail("expected a property name");
284
284
  const name = this.parseString();
285
- if (names.has(name))
285
+ if (names2.has(name))
286
286
  this.fail(`the property '${name}' is named twice`);
287
- names.add(name);
287
+ names2.add(name);
288
288
  this.skipWhitespace();
289
289
  if (this.text[this.position] !== ":")
290
290
  this.fail(`expected ':'`);
@@ -784,6 +784,11 @@ function firstText(i18n3) {
784
784
  const first2 = Object.values(i18n3)[0];
785
785
  return typeof first2 === "string" ? first2 : void 0;
786
786
  }
787
+ var withArticle = (nounPhrase, capitalized2 = true) => (/^[aeio]/iu.test(nounPhrase) ? capitalized2 ? "An " : "an " : capitalized2 ? "A " : "a ") + nounPhrase;
788
+ function numberText(value) {
789
+ const text2 = value.toString();
790
+ return text2.includes(".") ? text2.replace(/\.?0+$/u, "") : text2;
791
+ }
787
792
  var DIGEST_LENGTH = {
788
793
  sha256: 32,
789
794
  sha384: 48,
@@ -1348,6 +1353,7 @@ var CHARGING_STATION_RULES = {
1348
1353
  "address",
1349
1354
  "geoLocation",
1350
1355
  "hotline",
1356
+ "webPaymentURL",
1351
1357
  "manufacturerId",
1352
1358
  "modelId",
1353
1359
  "modelURL",
@@ -1378,6 +1384,7 @@ var CHARGING_POOL_RULES = {
1378
1384
  "parkingType",
1379
1385
  "accessibility",
1380
1386
  "hotline",
1387
+ "webPaymentURL",
1381
1388
  "gridConnectionPointId",
1382
1389
  "openingTimes",
1383
1390
  "energyMix",
@@ -1746,11 +1753,11 @@ function optionalIds(json, property, description, parser) {
1746
1753
  return parsed;
1747
1754
  });
1748
1755
  }
1749
- function optionalEnum(json, property, description, names) {
1756
+ function optionalEnum(json, property, description, names2) {
1750
1757
  const value = get(json, property);
1751
1758
  if (value === void 0)
1752
1759
  return void 0;
1753
- if (typeof value !== "string" || !names.includes(value))
1760
+ if (typeof value !== "string" || !names2.includes(value))
1754
1761
  throw new POIError(`Invalid ${description} '${tokenText(value)}'!`);
1755
1762
  return value;
1756
1763
  }
@@ -1883,7 +1890,7 @@ var sameId = (left, right) => left !== void 0 && right !== void 0 && ignoringCas
1883
1890
  var sameText = (left, right) => left === void 0 ? right === void 0 : sameId(left, right);
1884
1891
  function distinct(values, key2, description) {
1885
1892
  if (new Set(values.map(key2)).size !== values.length)
1886
- throw new POIError(`An ${description} must not be given twice!`);
1893
+ throw new POIError(`${withArticle(description)} must not be given twice!`);
1887
1894
  return [...values];
1888
1895
  }
1889
1896
  var isBlank = (text2) => trimmed(text2).length === 0;
@@ -1923,7 +1930,7 @@ function checkPartyKeys(keys, name) {
1923
1930
  for (const [list, description] of [[keys.signingKeys, "signing key"], [keys.identityKeys, "identity key"]]) {
1924
1931
  const ids2 = list.map((key2) => key2.id);
1925
1932
  if (new Set(ids2).size !== ids2.length)
1926
- return `An ${description} of ${name} must not be given twice!`;
1933
+ return `${withArticle(`${description} of ${name}`)} must not be given twice!`;
1927
1934
  }
1928
1935
  const signingIds = keys.signingKeys.map((key2) => key2.id).sort(ordinal);
1929
1936
  const identities = [...keys.identityKeys].sort((a, b) => ordinal(a.id, b.id));
@@ -2200,8 +2207,8 @@ ZW|ZWE|716|263|Zimbabwe
2200
2207
  YY|YYY|999|999|Testland
2201
2208
  `;
2202
2209
  var ALL = TABLE.trim().split("\n").map((line) => {
2203
- const [alpha2 = "", alpha3 = "", numeric = "", calling = "", ...names] = line.split("|");
2204
- return { alpha2, alpha3, numeric: Number(numeric), calling: Number(calling), names };
2210
+ const [alpha2 = "", alpha3 = "", numeric = "", calling = "", ...names2] = line.split("|");
2211
+ return { alpha2, alpha3, numeric: Number(numeric), calling: Number(calling), names: names2 };
2205
2212
  });
2206
2213
  var by = (keys) => {
2207
2214
  const countries = /* @__PURE__ */ new Map();
@@ -2230,7 +2237,7 @@ var EV_TALER_EXCHANGE_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/evTalerExchange
2230
2237
  var EV_TALER_PAYMENT_OPTION = "EVTaler";
2231
2238
  var EV_TALER_PAYMENT_MEANS = "EV_TALER";
2232
2239
  var CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
2233
- var MERCHANT_PROPERTIES = ["@context", "merchantBaseURL", "merchantPublicKey", "exchanges"];
2240
+ var MERCHANT_PROPERTIES = ["@context", "merchantBaseURL", "merchantPublicKey", "exchanges", "offersURL"];
2234
2241
  var EXCHANGE_PROPERTIES = ["@context", "masterPublicKey", "baseURL", "currency", "name"];
2235
2242
  var CURRENCIES = new Set(`EUR CHF USD CZK GBP HRK PLN RON RSD RUB SEK AED AFN ALL AMD AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF
2236
2243
  BMD BND BOB BRL BSD BTN BWP BYN BZD CAD CDF CLP CNY COP CRC CUP CVE DJF DKK DOP DZD EGP ERN ETB FJD FKP
@@ -2333,6 +2340,8 @@ function notWrittenAsRead(json, read) {
2333
2340
  return `The EV Taler merchant must not name the property '${property2}'!`;
2334
2341
  if (get(json, "merchantBaseURL") !== read.merchantBaseURL)
2335
2342
  return `The merchant base URL '${tokenText(get(json, "merchantBaseURL") ?? "")}' is written as '${read.merchantBaseURL}'!`;
2343
+ if (read.offersURL !== void 0 && get(json, "offersURL") !== read.offersURL)
2344
+ return `The offers URL '${tokenText(get(json, "offersURL") ?? "")}' is written as '${read.offersURL}'!`;
2336
2345
  return void 0;
2337
2346
  }
2338
2347
  const exchange = (error) => `Invalid exchange of an EV Taler merchant: ${error}`;
@@ -2400,9 +2409,15 @@ function evTalerMerchantOf(value) {
2400
2409
  exchanges.push(exchange);
2401
2410
  read.push([exchangeJSON, exchange]);
2402
2411
  }
2412
+ const offersURL = urlOf(value, "offersURL", "offers URL of an EV Taler merchant");
2413
+ if (typeof offersURL === "string")
2414
+ return invalid(offersURL);
2403
2415
  const urlError = invalidBaseURL(merchantBaseURL, "merchant base URL");
2404
2416
  if (urlError !== void 0)
2405
2417
  return invalid(`${urlError} (Parameter 'MerchantBaseURL')`);
2418
+ const offersURLError = offersURL === void 0 ? void 0 : invalidBaseURL(offersURL, "offers URL");
2419
+ if (offersURLError !== void 0)
2420
+ return invalid(`${offersURLError} (Parameter 'OffersURL')`);
2406
2421
  if (exchanges.length === 0)
2407
2422
  return invalid("A merchant accepts at least one exchange! (Parameter 'Exchanges')");
2408
2423
  if (new Set(exchanges.map((exchange) => exchange.masterPublicKey)).size !== exchanges.length)
@@ -2410,7 +2425,8 @@ function evTalerMerchantOf(value) {
2410
2425
  const merchant = {
2411
2426
  merchantBaseURL: merchantBaseURL.text,
2412
2427
  merchantPublicKey,
2413
- exchanges: exchanges.sort((left, right) => ordinal(left.masterPublicKey, right.masterPublicKey))
2428
+ exchanges: exchanges.sort((left, right) => ordinal(left.masterPublicKey, right.masterPublicKey)),
2429
+ ...offersURL === void 0 ? {} : { offersURL: offersURL.text }
2414
2430
  };
2415
2431
  const notWritten2 = notWrittenAsRead(value, merchant) ?? read.map(([json, exchange]) => notWrittenAsRead(json, exchange)).find((error) => error !== void 0);
2416
2432
  return notWritten2 === void 0 ? merchant : invalid(notWritten2);
@@ -2599,18 +2615,18 @@ function ids(json, property, description, parser) {
2599
2615
  }
2600
2616
  return parsed;
2601
2617
  }
2602
- function flags(json, property, description, names) {
2618
+ function flags(json, property, description, names2) {
2603
2619
  const value = get(json, property);
2604
2620
  if (value === void 0)
2605
2621
  return void 0;
2606
2622
  if (!isArray(value))
2607
2623
  return `The ${description} must be a JSON array!`;
2608
- const invalid = value.find((item) => typeof item !== "string" || !names.includes(item));
2624
+ const invalid = value.find((item) => typeof item !== "string" || !names2.includes(item));
2609
2625
  return invalid === void 0 ? void 0 : `Invalid ${description} '${interpolated(invalid)}'!`;
2610
2626
  }
2611
- function enumeration(json, property, description, names) {
2627
+ function enumeration(json, property, description, names2) {
2612
2628
  const value = get(json, property);
2613
- return value === void 0 || typeof value === "string" && names.includes(value) ? void 0 : `Invalid ${description} '${interpolated(value)}'!`;
2629
+ return value === void 0 || typeof value === "string" && names2.includes(value) ? void 0 : `Invalid ${description} '${interpolated(value)}'!`;
2614
2630
  }
2615
2631
  function texts(json, property, description) {
2616
2632
  const value = get(json, property);
@@ -2867,7 +2883,7 @@ function twice2(items) {
2867
2883
  }
2868
2884
  return void 0;
2869
2885
  }
2870
- var givenTwice2 = (items, description) => twice2(items) === void 0 ? void 0 : new Thrown(`${description} must not be given twice!`);
2886
+ var givenTwice2 = (items, description) => twice2(items) === void 0 ? void 0 : new Thrown(`${withArticle(description)} must not be given twice!`);
2871
2887
  function raise(error) {
2872
2888
  if (error !== void 0)
2873
2889
  throw error;
@@ -4800,9 +4816,9 @@ function dataLicenses(json) {
4800
4816
  const id = dataLicense(license);
4801
4817
  if (id instanceof Refused)
4802
4818
  return `Invalid data license: ${id.error}`;
4803
- if (given2.has(id))
4819
+ if (given2.has(ignoringCase(id)))
4804
4820
  return `The data license '${id}' must not be given twice!`;
4805
- given2.add(id);
4821
+ given2.add(ignoringCase(id));
4806
4822
  }
4807
4823
  }
4808
4824
  if (licenseIds !== void 0) {
@@ -4814,9 +4830,9 @@ function dataLicenses(json) {
4814
4830
  return `Invalid data license identification '${interpolated(item)}'!`;
4815
4831
  if (!KNOWN_DATA_LICENSES.includes(id.toUpperCase()))
4816
4832
  return `The data license '${id}' is not well-known, so it must be given completely!`;
4817
- if (given2.has(id))
4833
+ if (given2.has(ignoringCase(id)))
4818
4834
  return `The data license '${id}' must not be given twice!`;
4819
- given2.add(id);
4835
+ given2.add(ignoringCase(id));
4820
4836
  }
4821
4837
  }
4822
4838
  return void 0;
@@ -4885,7 +4901,7 @@ function openingTimes(json) {
4885
4901
  if (begin === void 0 || end === void 0)
4886
4902
  return `Invalid regular hours '${interpolated(period)}' on ${day}!`;
4887
4903
  if (end[0] < begin[0] && end[0] !== 0 || end[0] === begin[0] && end[1] < begin[1] && end[1] !== 0)
4888
- return `Invalid opening times: The end period '${hhmm(end)}h' must be after the start period '${hhmm(begin)}h'! (Parameter 'PeriodEnd')`;
4904
+ return `The end period '${hhmm(end)}h' must be after the start period '${hhmm(begin)}h'! (Parameter 'PeriodEnd')`;
4889
4905
  }
4890
4906
  }
4891
4907
  }
@@ -5035,10 +5051,12 @@ function keyError(id, key2, chain) {
5035
5051
  return computed === id ? void 0 : `The identification '${id}' of a measurement public key is not the SHA-256 '${computed}' of its key!`;
5036
5052
  }
5037
5053
  var transparencySoftwareAssignment = (json) => context(json, contextOf2("transparencySoftwareAssignment"), `The JSON-LD context of the transparency software status must be '${contextOf2("transparencySoftwareAssignment")}'!`) ?? (get(json, "certificateId") !== void 0 ? "A transparency software status names no certificateId: the certificates of the energy meter and of its charging station prove it!" : void 0) ?? textError(json, "transparencySoftwareId", "transparency software identification", nonEmpty) ?? textError(json, "legalStatus", "legal status", nonEmpty) ?? (get(json, "transparencySoftwareId") === void 0 || get(json, "legalStatus") === void 0 ? "A transparency software status needs its transparencySoftwareId and its legalStatus!" : void 0);
5054
+ var SECURE_DYNAMIC_QR_CODE_CHANNELS = ["BLE", "Display", "NFC"];
5055
+ var SECURE_DYNAMIC_QR_CODE = "SecureDynamicQRCode";
5038
5056
  function authenticationMode(json) {
5039
5057
  try {
5040
5058
  const type = get(json, "type");
5041
- const error = context(json, contextOf2("authenticationMode"), `The JSON-LD context of the authentication mode must be '${contextOf2("authenticationMode")}'!`) ?? text(json, "type", "authentication mode type") ?? errorOf(idsByName(json, "cardTypes", "RFID card type", ["MifareClassic", "MifareDESFire", "Calypso"])) ?? brands(json) ?? text(json, "number", "number") ?? text(json, "stationCode", "station code") ?? (type === void 0 ? "An authentication mode needs its type!" : void 0);
5059
+ const error = context(json, contextOf2("authenticationMode"), `The JSON-LD context of the authentication mode must be '${contextOf2("authenticationMode")}'!`) ?? text(json, "type", "authentication mode type") ?? errorOf(idsByName(json, "cardTypes", "RFID card type", ["MifareClassic", "MifareDESFire", "Calypso"])) ?? brands(json) ?? text(json, "number", "number") ?? text(json, "stationCode", "station code") ?? errorOf(idsByName(json, "channels", "channel of the secure dynamic QR code", SECURE_DYNAMIC_QR_CODE_CHANNELS)) ?? (type === void 0 ? "An authentication mode needs its type!" : void 0);
5042
5060
  if (error !== void 0 || typeof type !== "string")
5043
5061
  return error;
5044
5062
  if (trimmed(type) === "" || trimmed(type) !== type)
@@ -5046,6 +5064,9 @@ function authenticationMode(json) {
5046
5064
  const cardTypes = idsByName(json, "cardTypes", "RFID card type", []);
5047
5065
  if (new Set(cardTypes).size !== cardTypes.length)
5048
5066
  throw new Thrown("An RFID card type must not be given twice! (Parameter 'CardTypes')");
5067
+ const channels = idsByName(json, "channels", "channel of the secure dynamic QR code", []);
5068
+ if (new Set(channels).size !== channels.length)
5069
+ throw new Thrown("A channel of the secure dynamic QR code must not be given twice! (Parameter 'Channels')");
5049
5070
  const brandIds = (isArray(get(json, "brands")) ? get(json, "brands") : []).flatMap((item) => {
5050
5071
  const id = isObject(item) && typeof item["@id"] === "string" ? freeId(item["@id"]) : void 0;
5051
5072
  return id === void 0 ? [] : [id];
@@ -5066,6 +5087,11 @@ function authenticationMode(json) {
5066
5087
  throw new Thrown(`Only SMS and PhoneCall name a number, not '${type}'! (Parameter 'Number')`);
5067
5088
  if (get(json, "stationCode") !== void 0 && type !== "SMS")
5068
5089
  throw new Thrown(`Only SMS names a station code, not '${type}'! (Parameter 'StationCode')`);
5090
+ if (type === SECURE_DYNAMIC_QR_CODE) {
5091
+ if (channels.length === 0)
5092
+ throw new Thrown(`The authentication mode '${type}' needs the channels its URL is given over: ${SECURE_DYNAMIC_QR_CODE_CHANNELS.join(", ")}! (Parameter 'Channels')`);
5093
+ } else if (channels.length > 0)
5094
+ throw new Thrown(`Only ${SECURE_DYNAMIC_QR_CODE} names channels, not '${type}'! (Parameter 'Channels')`);
5069
5095
  return void 0;
5070
5096
  } catch (e) {
5071
5097
  if (e instanceof Thrown)
@@ -5073,13 +5099,13 @@ function authenticationMode(json) {
5073
5099
  throw e;
5074
5100
  }
5075
5101
  }
5076
- var idsByName = (json, property, description, names) => {
5102
+ var idsByName = (json, property, description, names2) => {
5077
5103
  const value = get(json, property);
5078
5104
  if (value === void 0)
5079
5105
  return [];
5080
5106
  if (!isArray(value))
5081
5107
  return new Refused(`The ${description} must be a JSON array!`);
5082
- const invalid = names.length === 0 ? void 0 : value.find((item) => typeof item !== "string" || !names.includes(item));
5108
+ const invalid = names2.length === 0 ? void 0 : value.find((item) => typeof item !== "string" || !names2.includes(item));
5083
5109
  return invalid === void 0 ? value.filter((item) => typeof item === "string") : new Refused(`Invalid ${description} '${interpolated(invalid)}'!`);
5084
5110
  };
5085
5111
  var relatedLocation = (json) => context(json, contextOf2("additionalGeoLocation"), `The JSON-LD context of the related location must be '${contextOf2("additionalGeoLocation")}'!`) ?? geoLocationError(json, "geoLocation", "geo location") ?? i18n(json, "name") ?? (get(json, "geoLocation") === void 0 ? "A related location needs its geo location!" : void 0);
@@ -5637,6 +5663,7 @@ var STATION_SINGLES = [
5637
5663
  "address",
5638
5664
  "geoLocation",
5639
5665
  "hotline",
5666
+ "webPaymentURL",
5640
5667
  "manufacturerId",
5641
5668
  "modelId",
5642
5669
  "modelURL",
@@ -5795,8 +5822,7 @@ function powerSum(station, evses, id) {
5795
5822
  const scale = Math.max(left.scale, right.scale);
5796
5823
  return new Decimal(left.mantissa * 10n ** BigInt(scale - left.scale) + right.mantissa * 10n ** BigInt(scale - right.scale), scale);
5797
5824
  }, new Decimal(0n, 0));
5798
- const watt = (value) => value.mantissa === 0n ? "0" : value.toString();
5799
- return maxPower.compareTo(sum2) > 0 ? `The maximum power of the charging station '${id}', ${watt(maxPower)} W, must not exceed the ${watt(sum2)} W of all its EVSEs together! (Parameter 'MaxPower')` : void 0;
5825
+ return maxPower.compareTo(sum2) > 0 ? `The maximum power of the charging station '${id}', ${numberText(maxPower)} W, must not exceed the ${numberText(sum2)} W of all its EVSEs together! (Parameter 'MaxPower')` : void 0;
5800
5826
  }
5801
5827
  var invalidGroupOrOffer = (operator) => whileChecking(() => groupOrOfferError(operator));
5802
5828
  function groupOrOfferError(operator) {
@@ -5999,9 +6025,16 @@ function newProviderOfferOverlap(network, before) {
5999
6025
  return void 0;
6000
6026
  }
6001
6027
  var invalidPaymentOption = (operator) => whileChecking(() => paymentOptionError(operator));
6028
+ var namesSecureDynamicQRCode = (authenticationModes) => isArray(authenticationModes) && authenticationModes.some((mode) => isObject(mode) && mode["type"] === SECURE_DYNAMIC_QR_CODE);
6002
6029
  function paymentOptionError(operator) {
6003
- if (get(operator, "evTaler") !== void 0)
6030
+ if (get(operator, "evTaler") !== void 0) {
6031
+ for (const pool of poolsOf(operator)) {
6032
+ const effective = [pool, ...templatesOf2(pool, operator, "chargingPoolTemplates", poolTemplateId)];
6033
+ if (effective.some((entity) => namesEVTaler(get(entity, "paymentOptions"))) && !effective.some((entity) => namesSecureDynamicQRCode(get(entity, "authenticationModes"))))
6034
+ return `The charging pool '${textOf2(pool, poolId)}' names the payment option '${EV_TALER_PAYMENT_OPTION}' without the authentication mode '${SECURE_DYNAMIC_QR_CODE}': an EV Taler session names the one-time password its EVSE gives right then!`;
6035
+ }
6004
6036
  return void 0;
6037
+ }
6005
6038
  const id = textOf2(operator, operatorId);
6006
6039
  for (const [property, parser, what] of [["chargingPools", poolId, "charging pool"], ["chargingPoolTemplates", poolTemplateId, "charging pool template"]])
6007
6040
  for (const entity of sortedAt(operator, property, parser))
@@ -6023,13 +6056,23 @@ function construct(operator, id, of) {
6023
6056
  if (later !== void 0)
6024
6057
  fail(`The ${description} '${later}' of the operator '${id}' must not be given twice!`, "Children");
6025
6058
  };
6026
- const operatorOf3 = (entity, parser) => textOf2(entity, parser).slice(0, 6);
6059
+ const operatorOf2 = (entity, parser) => textOf2(entity, parser).slice(0, 6);
6027
6060
  for (const pool of objectsAt(operator, "chargingPools"))
6028
- if (operatorOf3(pool, poolId) !== id)
6061
+ if (operatorOf2(pool, poolId) !== id)
6029
6062
  fail(`The charging pool '${textOf2(pool, poolId)}' does not belong to the operator '${id}'!`, "ChargingPools");
6030
6063
  ofOperator(objectsAt(operator, "chargingPools"), poolId, "charging pool");
6064
+ const stations = poolsOf(operator).flatMap(stationsOf);
6065
+ for (const [entities, parser, description] of [
6066
+ [stations, stationId, "charging station"],
6067
+ [stations.flatMap(evsesOf), evseId, "EVSE"],
6068
+ [metersOfOperator(operator), freeId, "energy meter"]
6069
+ ]) {
6070
+ const later = laterTwice(entities, parser);
6071
+ if (later !== void 0)
6072
+ fail(`The ${description} '${later}' of the operator '${id}' must not be given twice!`, "ChargingPools");
6073
+ }
6031
6074
  for (const tariff2 of objectsAt(operator, "chargingTariffs"))
6032
- if (operatorOf3(tariff2, tariffId) !== id)
6075
+ if (operatorOf2(tariff2, tariffId) !== id)
6033
6076
  fail(`The charging tariff '${textOf2(tariff2, tariffId)}' does not belong to the operator '${id}'!`, "ChargingTariffs");
6034
6077
  ofOperator(objectsAt(operator, "chargingTariffs"), tariffId, "charging tariff");
6035
6078
  const tariffs = keysAt(operator, "chargingTariffs", tariffId);
@@ -6052,7 +6095,7 @@ function construct(operator, id, of) {
6052
6095
  ]) {
6053
6096
  ofOperator(objectsAt(operator, property), parser, description);
6054
6097
  for (const group2 of sortedAt(operator, property, parser))
6055
- if (operatorOf3(group2, parser) !== id)
6098
+ if (operatorOf2(group2, parser) !== id)
6056
6099
  fail(`The group '${textOf2(group2, parser)}' does not belong to the operator '${id}'!`, parameter);
6057
6100
  }
6058
6101
  ofOperator(objectsAt(operator, "energyMeterGroups"), freeId, "energy meter group");
@@ -6072,7 +6115,7 @@ function construct(operator, id, of) {
6072
6115
  ofOperator(objectsAt(operator, "energyMeterTemplates"), freeId, "energy meter template");
6073
6116
  for (const [property, parser, , parameter] of templateKinds)
6074
6117
  for (const template of sortedAt(operator, property, parser)) {
6075
- if (operatorOf3(template, parser) !== id)
6118
+ if (operatorOf2(template, parser) !== id)
6076
6119
  fail(`The template '${textOf2(template, parser)}' does not belong to the operator '${id}'!`, parameter);
6077
6120
  const tariff2 = property === "chargingTariffTemplates" ? void 0 : idOf3(template, tariffId, "chargingTariffId");
6078
6121
  if (tariff2 !== void 0 && !tariffs.has(tariff2.key))
@@ -6632,11 +6675,11 @@ var energyMeter = (json) => caught("an energy meter", () => {
6632
6675
  if (error !== void 0)
6633
6676
  return error;
6634
6677
  const of = `the energy meter '${id.text}'`;
6635
- raise(givenTwice2(idsAt(json, "templateIds", freeId), `A template of ${of}`));
6636
- raise(givenTwice2(releasesAt(json), `A transparency software release of ${of}`));
6637
- raise(givenTwice2(namesAt(json, "signatureMethods"), `A signature method of ${of}`));
6638
- raise(givenTwice2(publicKeysAt(json), `An public key of ${of}`));
6639
- raise(givenTwice2(idsAt(json, "certificateIds", freeId), `A certificate of ${of}`));
6678
+ raise(givenTwice2(idsAt(json, "templateIds", freeId), `template of ${of}`));
6679
+ raise(givenTwice2(releasesAt(json), `transparency software release of ${of}`));
6680
+ raise(givenTwice2(namesAt(json, "signatureMethods"), `signature method of ${of}`));
6681
+ raise(givenTwice2(publicKeysAt(json), `public key of ${of}`));
6682
+ raise(givenTwice2(idsAt(json, "certificateIds", freeId), `certificate of ${of}`));
6640
6683
  return void 0;
6641
6684
  });
6642
6685
  function embeddedEnergyMeter(json) {
@@ -6654,9 +6697,9 @@ var energyMeterTemplate = (json) => caught("an energy meter template", () => {
6654
6697
  if (error !== void 0)
6655
6698
  return error;
6656
6699
  const of = `the energy meter template '${id.text}'`;
6657
- raise(givenTwice2(releasesAt(json), `A transparency software release of ${of}`));
6658
- raise(givenTwice2(namesAt(json, "signatureMethods"), `A signature method of ${of}`));
6659
- raise(givenTwice2(idsAt(json, "certificateIds", freeId), `A certificate of ${of}`));
6700
+ raise(givenTwice2(releasesAt(json), `transparency software release of ${of}`));
6701
+ raise(givenTwice2(namesAt(json, "signatureMethods"), `signature method of ${of}`));
6702
+ raise(givenTwice2(idsAt(json, "certificateIds", freeId), `certificate of ${of}`));
6660
6703
  return void 0;
6661
6704
  });
6662
6705
  var EVSE_GROUP = { what: "EVSE group", article: "an", kind: "evseGroup", id: evseGroupId, members: "EVSEIds", member: evseId };
@@ -6671,7 +6714,7 @@ var group = (kind) => (json) => caught(`${kind.article} ${kind.what}`, () => {
6671
6714
  const error = i18n(json, "name") ?? i18n(json, "description") ?? text(json, "dataSource", "data source") ?? errorOf(ids(json, kind.members, "member identification", kind.member)) ?? metadata(json);
6672
6715
  if (error !== void 0)
6673
6716
  return error;
6674
- raise(givenTwice2(idsAt(json, kind.members, kind.member), `A member of the ${kind.what} '${id.text}'`));
6717
+ raise(givenTwice2(idsAt(json, kind.members, kind.member), `member of the ${kind.what} '${id.text}'`));
6675
6718
  return void 0;
6676
6719
  });
6677
6720
  var memberGroups = (json) => children(json, "EVSEGroups", "EVSEGroupIds", "EVSEGroups", group(EVSE_GROUP)) ?? children(json, "chargingStationGroups", "chargingStationGroupIds", "ChargingStationGroups", group(CHARGING_STATION_GROUP)) ?? children(json, "chargingPoolGroups", "chargingPoolGroupIds", "ChargingPoolGroups", group(CHARGING_POOL_GROUP));
@@ -6688,9 +6731,9 @@ var chargingConnector = (json) => caught("a charging connector", () => {
6688
6731
  if (error !== void 0)
6689
6732
  return error;
6690
6733
  const of = `the charging connector '${id.text}'`;
6691
- raise(givenTwice2(namesAt(json, "standards"), `A standard of ${of}`));
6692
- raise(givenTwice2(imagesAt(json), `An image of ${of}`));
6693
- raise(givenTwice2(idsAt(json, "chargingTariffIds", tariffId), `A charging tariff of ${of}`));
6734
+ raise(givenTwice2(namesAt(json, "standards"), `standard of ${of}`));
6735
+ raise(givenTwice2(imagesAt(json), `image of ${of}`));
6736
+ raise(givenTwice2(idsAt(json, "chargingTariffIds", tariffId), `charging tariff of ${of}`));
6694
6737
  if (cableLength instanceof Decimal && signOf(cableLength) <= 0)
6695
6738
  return `The cable length of ${of} must be positive!`;
6696
6739
  if (cableWeight instanceof Decimal && signOf(cableWeight) <= 0)
@@ -6719,13 +6762,13 @@ var evse = (json) => caught("an EVSE", () => {
6719
6762
  if (error !== void 0)
6720
6763
  return error;
6721
6764
  const of = `the EVSE '${id.text}'`;
6722
- raise(givenTwice2(idsAt(json, "templateIds", evseTemplateId), `A template of ${of}`));
6765
+ raise(givenTwice2(idsAt(json, "templateIds", evseTemplateId), `template of ${of}`));
6723
6766
  evseConstruction(json, of);
6724
- raise(givenTwice2(idsAt(json, "chargingTariffIds", tariffId), `A charging tariff of ${of}`));
6767
+ raise(givenTwice2(idsAt(json, "chargingTariffIds", tariffId), `charging tariff of ${of}`));
6725
6768
  collection(objectsOf3(get(json, "brands")), freeId, "brand", of);
6726
- raise(givenTwice2(imagesAt(json), `An image of ${of}`));
6727
- raise(givenTwice2(namesAt(json, "standards"), `A standard of ${of}`));
6728
- raise(givenTwice2(idsAt(json, "acceptedEMobilityProviderIds", providerId), `A accepted e-mobility provider of ${of}`));
6769
+ raise(givenTwice2(imagesAt(json), `image of ${of}`));
6770
+ raise(givenTwice2(namesAt(json, "standards"), `standard of ${of}`));
6771
+ raise(givenTwice2(idsAt(json, "acceptedEMobilityProviderIds", providerId), `accepted e-mobility provider of ${of}`));
6729
6772
  return void 0;
6730
6773
  });
6731
6774
  var evseTemplate = (json) => caught("an EVSE template", () => {
@@ -6738,21 +6781,21 @@ var evseTemplate = (json) => caught("an EVSE template", () => {
6738
6781
  const of = `the EVSE template '${id.text}'`;
6739
6782
  evseConstruction(json, of);
6740
6783
  collection(objectsOf3(get(json, "brands")), freeId, "brand", of);
6741
- raise(givenTwice2(imagesAt(json), `An image of ${of}`));
6742
- raise(givenTwice2(namesAt(json, "standards"), `A standard of ${of}`));
6743
- raise(givenTwice2(idsAt(json, "acceptedEMobilityProviderIds", providerId), `A accepted e-mobility provider of ${of}`));
6784
+ raise(givenTwice2(imagesAt(json), `image of ${of}`));
6785
+ raise(givenTwice2(namesAt(json, "standards"), `standard of ${of}`));
6786
+ raise(givenTwice2(idsAt(json, "acceptedEMobilityProviderIds", providerId), `accepted e-mobility provider of ${of}`));
6744
6787
  return void 0;
6745
6788
  });
6746
- var stationModel = (json, serialNumber) => textError(json, "hotline", "hotline phone number", phoneNumber) ?? textError(json, "manufacturerId", "manufacturer identification", nonEmpty) ?? textError(json, "modelId", "model identification", nonEmpty) ?? textError(json, "modelURL", "model URL", url) ?? (serialNumber ? textError(json, "serialNumber", "serial number", exactText) : void 0) ?? textError(json, "hardwareRevision", "hardware revision", exactText) ?? textError(json, "firmwareVersion", "firmware version", exactText) ?? objects2(json, "transparencySoftwareStatus", "transparency software status", transparencySoftwareAssignment) ?? errorOf(ids(json, "signatureMethods", "signature method", nonEmpty));
6789
+ var stationModel = (json, serialNumber) => textError(json, "hotline", "hotline phone number", phoneNumber) ?? textError(json, "webPaymentURL", "web payment URL", url) ?? textError(json, "manufacturerId", "manufacturer identification", nonEmpty) ?? textError(json, "modelId", "model identification", nonEmpty) ?? textError(json, "modelURL", "model URL", url) ?? (serialNumber ? textError(json, "serialNumber", "serial number", exactText) : void 0) ?? textError(json, "hardwareRevision", "hardware revision", exactText) ?? textError(json, "firmwareVersion", "firmware version", exactText) ?? objects2(json, "transparencySoftwareStatus", "transparency software status", transparencySoftwareAssignment) ?? errorOf(ids(json, "signatureMethods", "signature method", nonEmpty));
6747
6790
  var stationLists = (json) => errorOf(ids(json, "vehicleTypes", "vehicle type", nonEmpty)) ?? errorOf(ids(json, "euVehicleCategories", "EU vehicle category", nonEmpty)) ?? textError(json, "accessibility", "accessibility", nonEmpty) ?? errorOf(ids(json, "features", "charging station feature", nonEmpty)) ?? textError(json, "calibrationInfo", "calibration information", url) ?? textError(json, "certificationInfo", "certification information", url) ?? errorOf(ids(json, "parkingSpaceIds", "parking space identification", freeId)) ?? errorOf(ids(json, "certificateIds", "certificate identification", freeId)) ?? errorOf(optionalDecimal2(json, "maxPower", "maximum power"));
6748
6791
  function stationListsConstruction(json, of) {
6749
6792
  collection(objectsOf3(get(json, "brands")), freeId, "brand", of);
6750
- raise(givenTwice2(imagesAt(json), `An image of ${of}`));
6751
- raise(givenTwice2(namesAt(json, "vehicleTypes"), `A vehicle type of ${of}`));
6752
- raise(givenTwice2(namesAt(json, "euVehicleCategories"), `A EU vehicle category of ${of}`));
6753
- raise(givenTwice2(namesAt(json, "features"), `A feature of ${of}`));
6754
- raise(givenTwice2(idsAt(json, "parkingSpaceIds", freeId), `A parking space of ${of}`));
6755
- raise(givenTwice2(idsAt(json, "certificateIds", freeId), `A certificate of ${of}`));
6793
+ raise(givenTwice2(imagesAt(json), `image of ${of}`));
6794
+ raise(givenTwice2(namesAt(json, "vehicleTypes"), `vehicle type of ${of}`));
6795
+ raise(givenTwice2(namesAt(json, "euVehicleCategories"), `EU vehicle category of ${of}`));
6796
+ raise(givenTwice2(namesAt(json, "features"), `feature of ${of}`));
6797
+ raise(givenTwice2(idsAt(json, "parkingSpaceIds", freeId), `parking space of ${of}`));
6798
+ raise(givenTwice2(idsAt(json, "certificateIds", freeId), `certificate of ${of}`));
6756
6799
  }
6757
6800
  function powerSumError(station, evses, id) {
6758
6801
  const maxPower = optionalDecimal2(station, "maxPower", "");
@@ -6763,8 +6806,7 @@ function powerSumError(station, evses, id) {
6763
6806
  const scale = Math.max(left.scale, right.scale);
6764
6807
  return new Decimal(left.mantissa * 10n ** BigInt(scale - left.scale) + right.mantissa * 10n ** BigInt(scale - right.scale), scale);
6765
6808
  }, new Decimal(0n, 0));
6766
- const watt = (value) => value.mantissa === 0n ? "0" : value.toString();
6767
- return maxPower.compareTo(sum2) > 0 ? thrown(`The maximum power of the charging station '${id}', ${watt(maxPower)} W, must not exceed the ${watt(sum2)} W of all its EVSEs together!`, "MaxPower") : void 0;
6809
+ return maxPower.compareTo(sum2) > 0 ? thrown(`The maximum power of the charging station '${id}', ${numberText(maxPower)} W, must not exceed the ${numberText(sum2)} W of all its EVSEs together!`, "MaxPower") : void 0;
6768
6810
  }
6769
6811
  var chargingStation = (json) => caught("a charging station", () => {
6770
6812
  const id = header(json, "charging station identification", stationId, "chargingStation", "charging station");
@@ -6776,7 +6818,7 @@ var chargingStation = (json) => caught("a charging station", () => {
6776
6818
  const of = `the charging station '${id.text}'`;
6777
6819
  const evses = objectsOf3(get(json, "EVSEs"));
6778
6820
  const operator = id.text.slice(0, 6);
6779
- raise(givenTwice2(idsAt(json, "templateIds", stationTemplateId), `A template of ${of}`));
6821
+ raise(givenTwice2(idsAt(json, "templateIds", stationTemplateId), `template of ${of}`));
6780
6822
  if (isNegative2(optionalDecimal2(json, "maxPower", "")))
6781
6823
  throw thrown(`The maximum power of ${of} must not be negative!`, "MaxPower");
6782
6824
  for (const item of evses) {
@@ -6785,9 +6827,9 @@ var chargingStation = (json) => caught("a charging station", () => {
6785
6827
  throw thrown(`The EVSE '${evseOf.text}' does not belong to the operator '${operator}' of ${of}!`, "EVSEs");
6786
6828
  }
6787
6829
  raise(powerSumError(json, evses, id.text));
6788
- raise(givenTwice2(releasesAt(json), `A transparency software release of ${of}`));
6789
- raise(givenTwice2(namesAt(json, "signatureMethods"), `A signature method of ${of}`));
6790
- raise(givenTwice2(publicKeysAt(json), `An public key of ${of}`));
6830
+ raise(givenTwice2(releasesAt(json), `transparency software release of ${of}`));
6831
+ raise(givenTwice2(namesAt(json, "signatureMethods"), `signature method of ${of}`));
6832
+ raise(givenTwice2(publicKeysAt(json), `public key of ${of}`));
6791
6833
  collection(evses, evseId, "EVSE", of);
6792
6834
  if (twice2(objectsOf3(get(json, "energyMeters")).flatMap((meter) => idOf3(meter, freeId) ?? [])) !== void 0)
6793
6835
  throw thrown(`An energy meter of ${of} must not be given twice!`, "EnergyMeters");
@@ -6804,8 +6846,8 @@ var chargingStationTemplate = (json) => caught("a charging station template", ()
6804
6846
  const of = `the charging station template '${id.text}'`;
6805
6847
  if (isNegative2(optionalDecimal2(json, "maxPower", "")))
6806
6848
  throw thrown(`The maximum power of ${of} must not be negative!`, "MaxPower");
6807
- raise(givenTwice2(releasesAt(json), `A transparency software release of ${of}`));
6808
- raise(givenTwice2(namesAt(json, "signatureMethods"), `A signature method of ${of}`));
6849
+ raise(givenTwice2(releasesAt(json), `transparency software release of ${of}`));
6850
+ raise(givenTwice2(namesAt(json, "signatureMethods"), `signature method of ${of}`));
6809
6851
  stationListsConstruction(json, of);
6810
6852
  return void 0;
6811
6853
  });
@@ -6814,19 +6856,19 @@ var nutsCode = (given2) => /^[A-Z]{2}[A-Z0-9]{0,3}$/u.test(trimmed(given2).toUpp
6814
6856
  function poolListsConstruction(json, of) {
6815
6857
  const authenticationModes = objectsOf3(get(json, "authenticationModes")).map((mode) => ({ text: asText(mode["type"]), key: asText(mode["type"]) }));
6816
6858
  const relatedLocations = objectsOf3(get(json, "relatedLocations")).map((location) => ({ text: "", key: canonicalJSON([location["geoLocation"] ?? null, location["name"] ?? null]) }));
6817
- raise(givenTwice2(imagesAt(json), `An image of ${of}`));
6859
+ raise(givenTwice2(imagesAt(json), `image of ${of}`));
6818
6860
  raise(timeZoneError(json, of));
6819
- raise(givenTwice2(namesAt(json, "facilities"), `A facility of ${of}`));
6820
- raise(givenTwice2(namesAt(json, "paymentOptions"), `A payment option of ${of}`));
6821
- raise(givenTwice2(authenticationModes, `An authentication mode of ${of}`));
6822
- raise(givenTwice2(namesAt(json, "uiFeatures"), `A user interface feature of ${of}`));
6823
- raise(givenTwice2(namesAt(json, "features"), `A feature of ${of}`));
6824
- raise(givenTwice2(keyed(idsByNameAt(json, "locationLanguages"), (name) => name), `A language of ${of}`));
6825
- raise(givenTwice2(namesAt(json, "parkingRestrictions"), `A parking restriction of ${of}`));
6826
- raise(givenTwice2(relatedLocations, `An related location of ${of}`));
6827
- raise(givenTwice2(namesAt(json, "services"), `A service of ${of}`));
6828
- raise(givenTwice2(namesAt(json, "standards"), `A standard of ${of}`));
6829
- raise(givenTwice2(keyed(idsAt(json, "nutsCodes", freeId).map((code) => code.text.toUpperCase()), (name) => name), `A NUTS code of ${of}`));
6861
+ raise(givenTwice2(namesAt(json, "facilities"), `facility of ${of}`));
6862
+ raise(givenTwice2(namesAt(json, "paymentOptions"), `payment option of ${of}`));
6863
+ raise(givenTwice2(authenticationModes, `authentication mode of ${of}`));
6864
+ raise(givenTwice2(namesAt(json, "uiFeatures"), `user interface feature of ${of}`));
6865
+ raise(givenTwice2(namesAt(json, "features"), `feature of ${of}`));
6866
+ raise(givenTwice2(keyed(idsByNameAt(json, "locationLanguages"), (name) => name), `language of ${of}`));
6867
+ raise(givenTwice2(namesAt(json, "parkingRestrictions"), `parking restriction of ${of}`));
6868
+ raise(givenTwice2(relatedLocations, `related location of ${of}`));
6869
+ raise(givenTwice2(namesAt(json, "services"), `service of ${of}`));
6870
+ raise(givenTwice2(namesAt(json, "standards"), `standard of ${of}`));
6871
+ raise(givenTwice2(keyed(idsAt(json, "nutsCodes", freeId).map((code) => code.text.toUpperCase()), (name) => name), `NUTS code of ${of}`));
6830
6872
  }
6831
6873
  function timeZoneError(json, of) {
6832
6874
  const timeZone2 = get(json, "timeZone");
@@ -6841,13 +6883,13 @@ var chargingPool = (json) => caught("a charging pool", () => {
6841
6883
  const id = header(json, "charging pool identification", poolId, "chargingPool", "charging pool");
6842
6884
  if (id instanceof Refused)
6843
6885
  return id.error;
6844
- const error = i18n(json, "name") ?? i18n(json, "description") ?? text(json, "dataSource", "data source") ?? addressError(json, "address", "address") ?? geoLocationError(json, "geoLocation", "geo location") ?? textError(json, "parkingType", "parking type", nonEmpty) ?? textError(json, "accessibility", "accessibility", nonEmpty) ?? textError(json, "hotline", "hotline phone number", phoneNumber) ?? dataLicenses(json) ?? children(json, "chargingStations", "chargingStationIds", "charging stations", chargingStation) ?? (get(json, "gridConnectionPoint") !== void 0 ? `The grid connection point of the charging pool '${id.text}' is embedded in its grid operator: name it by 'gridConnectionPointId'!` : void 0) ?? textError(json, "gridConnectionPointId", "grid connection point identification", nonEmpty) ?? energyMeters(json) ?? brands(json) ?? object(json, "openingTimes", "opening times", openingTimes) ?? object(json, "energyMix", "energy mix", energyMix) ?? objects2(json, "images", "images", image) ?? poolLists(json) ?? optionalBoolean(json, "chargingWhenClosed", "charging when closed") ?? errorOf(ids(json, "templateIds", "template identification", poolTemplateId)) ?? metadata(json);
6886
+ const error = i18n(json, "name") ?? i18n(json, "description") ?? text(json, "dataSource", "data source") ?? addressError(json, "address", "address") ?? geoLocationError(json, "geoLocation", "geo location") ?? textError(json, "parkingType", "parking type", nonEmpty) ?? textError(json, "accessibility", "accessibility", nonEmpty) ?? textError(json, "hotline", "hotline phone number", phoneNumber) ?? textError(json, "webPaymentURL", "web payment URL", url) ?? dataLicenses(json) ?? children(json, "chargingStations", "chargingStationIds", "charging stations", chargingStation) ?? (get(json, "gridConnectionPoint") !== void 0 ? `The grid connection point of the charging pool '${id.text}' is embedded in its grid operator: name it by 'gridConnectionPointId'!` : void 0) ?? textError(json, "gridConnectionPointId", "grid connection point identification", nonEmpty) ?? energyMeters(json) ?? brands(json) ?? object(json, "openingTimes", "opening times", openingTimes) ?? object(json, "energyMix", "energy mix", energyMix) ?? objects2(json, "images", "images", image) ?? poolLists(json) ?? optionalBoolean(json, "chargingWhenClosed", "charging when closed") ?? errorOf(ids(json, "templateIds", "template identification", poolTemplateId)) ?? metadata(json);
6845
6887
  if (error !== void 0)
6846
6888
  return error;
6847
6889
  const of = `the charging pool '${id.text}'`;
6848
6890
  const stations = objectsOf3(get(json, "chargingStations"));
6849
6891
  const operator = id.text.slice(0, 6);
6850
- raise(givenTwice2(idsAt(json, "templateIds", poolTemplateId), `A template of ${of}`));
6892
+ raise(givenTwice2(idsAt(json, "templateIds", poolTemplateId), `template of ${of}`));
6851
6893
  for (const station of stations) {
6852
6894
  const stationOf = idOf3(station, stationId);
6853
6895
  if (stationOf !== void 0 && stationOf.text.slice(0, 6) !== operator)
@@ -6864,7 +6906,7 @@ var chargingPoolTemplate = (json) => caught("a charging pool template", () => {
6864
6906
  const id = header(json, "charging pool template identification", poolTemplateId, "chargingPoolTemplate", "charging pool template");
6865
6907
  if (id instanceof Refused)
6866
6908
  return id.error;
6867
- const error = i18n(json, "name") ?? i18n(json, "description") ?? text(json, "dataSource", "data source") ?? textError(json, "chargingTariffId", "charging tariff identification", tariffId) ?? addressError(json, "address", "address") ?? geoLocationError(json, "geoLocation", "geo location") ?? textError(json, "parkingType", "parking type", nonEmpty) ?? textError(json, "accessibility", "accessibility", nonEmpty) ?? textError(json, "hotline", "hotline phone number", phoneNumber) ?? dataLicenses(json) ?? textError(json, "gridConnectionPointId", "grid connection point identification", nonEmpty) ?? brands(json) ?? object(json, "openingTimes", "opening times", openingTimes) ?? object(json, "energyMix", "energy mix", energyMix) ?? objects2(json, "images", "images", image) ?? poolLists(json) ?? optionalBoolean(json, "chargingWhenClosed", "charging when closed") ?? metadata(json);
6909
+ const error = i18n(json, "name") ?? i18n(json, "description") ?? text(json, "dataSource", "data source") ?? textError(json, "chargingTariffId", "charging tariff identification", tariffId) ?? addressError(json, "address", "address") ?? geoLocationError(json, "geoLocation", "geo location") ?? textError(json, "parkingType", "parking type", nonEmpty) ?? textError(json, "accessibility", "accessibility", nonEmpty) ?? textError(json, "hotline", "hotline phone number", phoneNumber) ?? textError(json, "webPaymentURL", "web payment URL", url) ?? dataLicenses(json) ?? textError(json, "gridConnectionPointId", "grid connection point identification", nonEmpty) ?? brands(json) ?? object(json, "openingTimes", "opening times", openingTimes) ?? object(json, "energyMix", "energy mix", energyMix) ?? objects2(json, "images", "images", image) ?? poolLists(json) ?? optionalBoolean(json, "chargingWhenClosed", "charging when closed") ?? metadata(json);
6868
6910
  if (error !== void 0)
6869
6911
  return error;
6870
6912
  const of = `the charging pool template '${id.text}'`;
@@ -6914,12 +6956,12 @@ function priceComponent(json) {
6914
6956
  if (stepSize instanceof Refused)
6915
6957
  return stepSize.error;
6916
6958
  if (price !== void 0 && price.mantissa < 0n)
6917
- return `The price of a ${asText(type)} price component must not be negative!`;
6959
+ return `The price of ${withArticle(`${asText(type)} price component`, false)} must not be negative!`;
6918
6960
  if (vat !== void 0 && (vat.mantissa < 0n || vat.compareTo(new Decimal(100n, 0)) > 0))
6919
- return `The VAT of a ${asText(type)} price component must be between 0 and 100 percent!`;
6961
+ return `The VAT of ${withArticle(`${asText(type)} price component`, false)} must be between 0 and 100 percent!`;
6920
6962
  if (type === "FLAT")
6921
6963
  return isGiven3(get(json, "stepSize")) ? "A FLAT price component has no step size!" : void 0;
6922
- return stepSize === void 0 || signOf(stepSize) <= 0 ? `A ${asText(type)} price component needs a positive step size!` : void 0;
6964
+ return stepSize === void 0 || signOf(stepSize) <= 0 ? `${withArticle(`${asText(type)} price component`)} needs a positive step size!` : void 0;
6923
6965
  }
6924
6966
  function exactTime(given2, format) {
6925
6967
  if (format === "HH:mm") {
@@ -7066,9 +7108,9 @@ var tariff = (template) => (json) => caught(template ? "a charging tariff templa
7066
7108
  if (error !== void 0)
7067
7109
  return error;
7068
7110
  if (!template)
7069
- raise(givenTwice2(idsAt(json, "templateIds", tariffTemplateId), `A template of the ${of}`));
7070
- raise(givenTwice2(namesAt(json, "paymentMeans"), `A payment means of the ${of}`));
7071
- raise(givenTwice2(namesAt(json, "paymentBrands"), `A payment brand of the ${of}`));
7111
+ raise(givenTwice2(idsAt(json, "templateIds", tariffTemplateId), `template of the ${of}`));
7112
+ raise(givenTwice2(namesAt(json, "paymentMeans"), `payment means of the ${of}`));
7113
+ raise(givenTwice2(namesAt(json, "paymentBrands"), `payment brand of the ${of}`));
7072
7114
  return void 0;
7073
7115
  });
7074
7116
  var chargingTariff = tariff(false);
@@ -7095,7 +7137,7 @@ function discount(json) {
7095
7137
  return error;
7096
7138
  const value = percent instanceof Decimal ? percent : new Decimal(BigInt(percent), 0);
7097
7139
  if (value.mantissa <= 0n || value.compareTo(new Decimal(100n, 0)) > 0)
7098
- return `The percentage of a discount must be above 0 and at most 100, not ${value.toString()}! (Parameter 'Percent')`;
7140
+ return `The percentage of a discount must be above 0 and at most 100, not ${numberText(value)}! (Parameter 'Percent')`;
7099
7141
  const dimensions = idsByName(json, "dimensions", "", []);
7100
7142
  return new Set(dimensions).size !== dimensions.length ? "A dimension of a discount must not be given twice! (Parameter 'Dimensions')" : void 0;
7101
7143
  }
@@ -7114,11 +7156,11 @@ var chargingTariffOffer = (json) => caught("a charging tariff offer", () => {
7114
7156
  return error;
7115
7157
  if (price === void 0 === (reduction === void 0))
7116
7158
  throw thrown(`The charging tariff offer '${id.text}' must have exactly one of a price of its own and a discount!`, "ChargingTariff");
7117
- raise(givenTwice2(idsAt(json, "EVSEGroupIds", evseGroupId), `A EVSE group of ${owner}`));
7118
- raise(givenTwice2(idsAt(json, "chargingStationGroupIds", stationGroupId), `A charging station group of ${owner}`));
7119
- raise(givenTwice2(idsAt(json, "chargingPoolGroupIds", poolGroupId), `A charging pool group of ${owner}`));
7120
- raise(givenTwice2(idsAt(json, "chargingStationOperatorIds", operatorId), `A charging station operator of ${owner}`));
7121
- raise(givenTwice2(idsAt(json, "eMobilityProviderIds", providerId), `A e-mobility provider of ${owner}`));
7159
+ raise(givenTwice2(idsAt(json, "EVSEGroupIds", evseGroupId), `EVSE group of ${owner}`));
7160
+ raise(givenTwice2(idsAt(json, "chargingStationGroupIds", stationGroupId), `charging station group of ${owner}`));
7161
+ raise(givenTwice2(idsAt(json, "chargingPoolGroupIds", poolGroupId), `charging pool group of ${owner}`));
7162
+ raise(givenTwice2(idsAt(json, "chargingStationOperatorIds", operatorId), `charging station operator of ${owner}`));
7163
+ raise(givenTwice2(idsAt(json, "eMobilityProviderIds", providerId), `e-mobility provider of ${owner}`));
7122
7164
  if (["EVSEGroupIds", "chargingStationGroupIds", "chargingPoolGroupIds", "chargingStationOperatorIds"].every((property) => idsAt(json, property, freeId).length === 0))
7123
7165
  throw thrown(`The charging tariff offer '${id.text}' must name at least one EVSE group, charging station group, charging pool group or charging station operator!`, "EVSEGroupIds");
7124
7166
  if (typeof notBefore === "number" && typeof notAfter === "number" && notAfter < notBefore)
@@ -7134,20 +7176,20 @@ var parkingSpace = (json) => caught("a parking space", () => {
7134
7176
  if (error !== void 0)
7135
7177
  return error;
7136
7178
  const of = `the parking space '${id.text}'`;
7137
- raise(givenTwice2(idsAt(json, "templateIds", freeId), `A template of ${of}`));
7138
- raise(givenTwice2(idsAt(json, "parkingSensorIds", freeId), `A parking sensor of ${of}`));
7139
- raise(givenTwice2(idsAt(json, "chargingStationIds", stationId), `A charging station of ${of}`));
7140
- raise(givenTwice2(idsAt(json, "parkingProductIds", caseId), `A parking product of ${of}`));
7141
- raise(givenTwice2(namesAt(json, "parkingRestrictions"), `A parking restriction of ${of}`));
7142
- raise(givenTwice2(namesAt(json, "euVehicleCategories"), `A EU vehicle category of ${of}`));
7179
+ raise(givenTwice2(idsAt(json, "templateIds", freeId), `template of ${of}`));
7180
+ raise(givenTwice2(idsAt(json, "parkingSensorIds", freeId), `parking sensor of ${of}`));
7181
+ raise(givenTwice2(idsAt(json, "chargingStationIds", stationId), `charging station of ${of}`));
7182
+ raise(givenTwice2(idsAt(json, "parkingProductIds", caseId), `parking product of ${of}`));
7183
+ raise(givenTwice2(namesAt(json, "parkingRestrictions"), `parking restriction of ${of}`));
7184
+ raise(givenTwice2(namesAt(json, "euVehicleCategories"), `EU vehicle category of ${of}`));
7143
7185
  if (notPositive(json, "maxVehicleWeight"))
7144
7186
  throw thrown(`The maximum vehicle weight of ${of} must be positive!`, "MaxVehicleWeight");
7145
7187
  for (const property of dimensions)
7146
7188
  if (notPositive(json, property))
7147
7189
  throw thrown(`The ${property} of ${of} must be positive!`, property.charAt(0).toUpperCase() + property.slice(1));
7148
- raise(givenTwice2(namesAt(json, "vehicleTypes"), `A vehicle type of ${of}`));
7149
- raise(givenTwice2(namesAt(json, "standards"), `A standard of ${of}`));
7150
- raise(givenTwice2(imagesAt(json), `An image of ${of}`));
7190
+ raise(givenTwice2(namesAt(json, "vehicleTypes"), `vehicle type of ${of}`));
7191
+ raise(givenTwice2(namesAt(json, "standards"), `standard of ${of}`));
7192
+ raise(givenTwice2(imagesAt(json), `image of ${of}`));
7151
7193
  return void 0;
7152
7194
  });
7153
7195
  var notPositive = (json, property) => {
@@ -7167,14 +7209,14 @@ var parkingSpaceTemplate = (json) => caught("a parking space template", () => {
7167
7209
  const of = `the parking space template '${id.text}'`;
7168
7210
  if (["maxVehicleWeight", ...dimensions].some((property) => notPositive(json, property)))
7169
7211
  throw thrown(`The vehicle limits and dimensions of ${of} must be positive!`);
7170
- raise(givenTwice2(idsAt(json, "parkingProductIds", caseId), `A parking product of ${of}`));
7171
- raise(givenTwice2(idsAt(json, "parkingSensorIds", freeId), `A parking sensor of ${of}`));
7172
- raise(givenTwice2(idsAt(json, "chargingStationIds", stationId), `A charging station of ${of}`));
7173
- raise(givenTwice2(namesAt(json, "parkingRestrictions"), `A parking restriction of ${of}`));
7174
- raise(givenTwice2(namesAt(json, "euVehicleCategories"), `A EU vehicle category of ${of}`));
7175
- raise(givenTwice2(namesAt(json, "vehicleTypes"), `A vehicle type of ${of}`));
7176
- raise(givenTwice2(namesAt(json, "standards"), `A standard of ${of}`));
7177
- raise(givenTwice2(imagesAt(json), `An image of ${of}`));
7212
+ raise(givenTwice2(idsAt(json, "parkingProductIds", caseId), `parking product of ${of}`));
7213
+ raise(givenTwice2(idsAt(json, "parkingSensorIds", freeId), `parking sensor of ${of}`));
7214
+ raise(givenTwice2(idsAt(json, "chargingStationIds", stationId), `charging station of ${of}`));
7215
+ raise(givenTwice2(namesAt(json, "parkingRestrictions"), `parking restriction of ${of}`));
7216
+ raise(givenTwice2(namesAt(json, "euVehicleCategories"), `EU vehicle category of ${of}`));
7217
+ raise(givenTwice2(namesAt(json, "vehicleTypes"), `vehicle type of ${of}`));
7218
+ raise(givenTwice2(namesAt(json, "standards"), `standard of ${of}`));
7219
+ raise(givenTwice2(imagesAt(json), `image of ${of}`));
7178
7220
  return void 0;
7179
7221
  });
7180
7222
  var parkingGarage = (json) => caught("a parking garage", () => {
@@ -7185,9 +7227,9 @@ var parkingGarage = (json) => caught("a parking garage", () => {
7185
7227
  if (error !== void 0)
7186
7228
  return error;
7187
7229
  const of = `the parking garage '${id.text}'`;
7188
- raise(givenTwice2(idsAt(json, "chargingStationIds", stationId), `A charging station of ${of}`));
7189
- raise(givenTwice2(imagesAt(json), `An image of ${of}`));
7190
- raise(givenTwice2(idsAt(json, "parkingProductIds", caseId), `A parking product of ${of}`));
7230
+ raise(givenTwice2(idsAt(json, "chargingStationIds", stationId), `charging station of ${of}`));
7231
+ raise(givenTwice2(imagesAt(json), `image of ${of}`));
7232
+ raise(givenTwice2(idsAt(json, "parkingProductIds", caseId), `parking product of ${of}`));
7191
7233
  return void 0;
7192
7234
  });
7193
7235
  var parkingSensor = (json) => caught("a parking sensor", () => {
@@ -7197,7 +7239,7 @@ var parkingSensor = (json) => caught("a parking sensor", () => {
7197
7239
  const error = i18n(json, "name") ?? i18n(json, "description") ?? text(json, "dataSource", "data source") ?? geoLocationError(json, "geoLocation", "geo location") ?? errorOf(ids(json, "chargingStationIds", "charging station identification", stationId)) ?? metadata(json);
7198
7240
  if (error !== void 0)
7199
7241
  return error;
7200
- raise(givenTwice2(idsAt(json, "chargingStationIds", stationId), `A charging station of the parking sensor '${id.text}'`));
7242
+ raise(givenTwice2(idsAt(json, "chargingStationIds", stationId), `charging station of the parking sensor '${id.text}'`));
7201
7243
  return void 0;
7202
7244
  });
7203
7245
  var parkingSpaceGroup = (json) => caught("a parking space group", () => {
@@ -7207,7 +7249,7 @@ var parkingSpaceGroup = (json) => caught("a parking space group", () => {
7207
7249
  const error = i18n(json, "name") ?? i18n(json, "description") ?? text(json, "dataSource", "data source") ?? errorOf(ids(json, "parkingSpaceIds", "member identification", freeId)) ?? metadata(json);
7208
7250
  if (error !== void 0)
7209
7251
  return error;
7210
- raise(givenTwice2(idsAt(json, "parkingSpaceIds", freeId), `A member of the parking space group '${id.text}'`));
7252
+ raise(givenTwice2(idsAt(json, "parkingSpaceIds", freeId), `member of the parking space group '${id.text}'`));
7211
7253
  return void 0;
7212
7254
  });
7213
7255
  var parkingProduct = (json) => caught("a parking product", () => {
@@ -7272,8 +7314,8 @@ var model = (meter) => (json) => caught(meter ? "an energy meter model" : "a cha
7272
7314
  const versions = versionTwice(json, of);
7273
7315
  if (versions !== void 0)
7274
7316
  throw new Thrown(versions);
7275
- raise(givenTwice2(imagesAt(json), `An image of ${of}`));
7276
- raise(givenTwice2(namesAt(json, meter ? "signatureMethods" : "connectorTypes"), `A ${meter ? "signature method" : "connector type"} of ${of}`));
7317
+ raise(givenTwice2(imagesAt(json), `image of ${of}`));
7318
+ raise(givenTwice2(namesAt(json, meter ? "signatureMethods" : "connectorTypes"), `${meter ? "signature method" : "connector type"} of ${of}`));
7277
7319
  return void 0;
7278
7320
  });
7279
7321
  var manufacturer = (meter) => (json) => caught(meter ? "an energy meter manufacturer" : "a charging station manufacturer", () => {
@@ -7465,7 +7507,7 @@ var gridConnectionPoint = (json) => caught("a grid connection point", () => {
7465
7507
  return error;
7466
7508
  const of = `the grid connection point '${id.text}'`;
7467
7509
  if (isObject(by2) && by2["entityType"] !== "ChargingStationOperator" && by2["entityType"] !== "ParkingOperator")
7468
- throw thrown(`The grid connection point '${id.text}' can only be maintained by its grid operator, a charging station operator or a parking operator, not by ${partyText(by2)}!`);
7510
+ throw thrown(`Besides its grid operator, the grid connection point '${id.text}' can only be maintained by a charging station operator or a parking operator, not by ${partyText(by2)}!`);
7469
7511
  if (["nominalVoltage", "nominalFrequency"].some((property) => notPositive(json, property)))
7470
7512
  throw thrown(`The nominal voltage and frequency of ${of} must be positive!`);
7471
7513
  const contracted = powers.map(([property]) => optionalDecimal2(json, property, property));
@@ -7579,7 +7621,7 @@ var chargingStationOperator = (of) => (json) => caught("a charging station opera
7579
7621
  if (id instanceof Refused)
7580
7622
  return id.error;
7581
7623
  const merchant = get(json, "evTaler");
7582
- const error = i18n(json, "name") ?? i18n(json, "description") ?? styxText(json, "dataSource", "data source") ?? dataLicenses(json) ?? addressError(json, "address", "address") ?? geoLocationError(json, "geoLocation", "geo location") ?? operatorLogos(json) ?? textError(json, "telephone", "telephone number", phoneNumber) ?? eMailAddressError(json, "eMailAddress", "e-mail address") ?? textError(json, "homepage", "homepage", url) ?? textError(json, "hotline", "hotline phone number", phoneNumber) ?? textError(json, "termsAndConditions", "terms and conditions URL", url) ?? (merchant === void 0 ? void 0 : (() => {
7624
+ const error = i18n(json, "name") ?? i18n(json, "description") ?? styxText(json, "dataSource", "data source") ?? dataLicenses(json) ?? addressError(json, "address", "address") ?? geoLocationError(json, "geoLocation", "geo location") ?? operatorLogos(json) ?? textError(json, "telephone", "telephone number", phoneNumber) ?? eMailAddressError(json, "eMailAddress", "e-mail address") ?? textError(json, "homepage", "homepage", url) ?? textError(json, "hotline", "hotline phone number", phoneNumber) ?? textError(json, "termsAndConditions", "terms and conditions URL", url) ?? textError(json, "webPaymentURL", "web payment URL", url) ?? (merchant === void 0 ? void 0 : (() => {
7583
7625
  const read = evTalerMerchantOf(merchant);
7584
7626
  return typeof read === "string" ? read : void 0;
7585
7627
  })()) ?? children(json, "chargingPools", "chargingPoolIds", "charging pools", chargingPool) ?? children(json, "EVSEGroups", "EVSEGroupIds", "EVSEGroups", group(EVSE_GROUP)) ?? children(json, "chargingStationGroups", "chargingStationGroupIds", "ChargingStationGroups", group(CHARGING_STATION_GROUP)) ?? children(json, "chargingPoolGroups", "chargingPoolGroupIds", "ChargingPoolGroups", group(CHARGING_POOL_GROUP)) ?? children(json, "chargingTariffGroups", "chargingTariffGroupIds", "ChargingTariffGroups", group(CHARGING_TARIFF_GROUP)) ?? energyMeterGroups(json) ?? children(json, "EVSETemplates", "EVSETemplateIds", "EVSE templates", evseTemplate) ?? children(json, "chargingStationTemplates", "chargingStationTemplateIds", "charging station templates", chargingStationTemplate) ?? children(json, "chargingPoolTemplates", "chargingPoolTemplateIds", "charging pool templates", chargingPoolTemplate) ?? children(json, "chargingTariffTemplates", "chargingTariffTemplateIds", "charging tariff templates", chargingTariffTemplate) ?? energyMeterTemplates(json) ?? children(json, "parkingSpaces", "parkingSpaceIds", "parking spaces", parkingSpace) ?? children(json, "chargingTariffOffers", "chargingTariffOfferIds", "charging tariff offers", chargingTariffOffer) ?? children(json, "brands", "brandIds", "brands", brand) ?? children(json, "chargingTariffs", "chargingTariffIds", "charging tariffs", chargingTariff) ?? keysError(json) ?? metadata(json);
@@ -7628,6 +7670,37 @@ function invalidReading(content, of = "version") {
7628
7670
  throw e;
7629
7671
  }
7630
7672
  }
7673
+ var PARTY_READERS = {
7674
+ ChargingStationManufacturer: manufacturer(false),
7675
+ EMobilityProvider: eMobilityProvider,
7676
+ GridOperator: gridOperator,
7677
+ ParkingOperator: parkingOperator,
7678
+ TransparencySoftwareManufacturer: softwareManufacturer,
7679
+ ConformityAssessmentBody: conformityAssessmentBody,
7680
+ EnergyMeterManufacturer: manufacturer(true),
7681
+ ChargingStationOperator: chargingStationOperator("change set")
7682
+ };
7683
+ function invalidParty(type, json) {
7684
+ try {
7685
+ return PARTY_READERS[type]?.(json);
7686
+ } catch (e) {
7687
+ if (e instanceof Thrown)
7688
+ return `The given JSON representation of a roaming network is invalid: ${e.message}`;
7689
+ throw e;
7690
+ }
7691
+ }
7692
+ function invalidTreeHashAlgorithms(value) {
7693
+ if (!isArray(value))
7694
+ return "The 'treeHashAlgorithms' must be a JSON array of hash algorithms!";
7695
+ const unknown = value.find((algorithm) => !isHashAlgorithm(algorithm));
7696
+ if (unknown !== void 0)
7697
+ return `Unknown tree hash algorithm '${asText(unknown)}': sha256, sha384 or sha512!`;
7698
+ if (value.length === 0)
7699
+ return "The 'treeHashAlgorithms' must name at least one hash algorithm!";
7700
+ if (new Set(value).size !== value.length)
7701
+ return "The 'treeHashAlgorithms' must not name a hash algorithm twice!";
7702
+ return void 0;
7703
+ }
7631
7704
 
7632
7705
  // src/network/writtenForm.ts
7633
7706
  var idOf4 = (value) => isObject(value) && typeof value["@id"] === "string" ? value["@id"] : "";
@@ -7666,6 +7739,7 @@ var ORDERED_TEXTS = /* @__PURE__ */ new Set([
7666
7739
  "verifiedTransparencySoftwareIds",
7667
7740
  "compatibleTransparencySoftwareIds",
7668
7741
  "cardTypes",
7742
+ "channels",
7669
7743
  "dimensions",
7670
7744
  "usages"
7671
7745
  ]);
@@ -7676,6 +7750,7 @@ var LEFT_OUT_WHEN_EMPTY = /* @__PURE__ */ new Set([
7676
7750
  "cardTypes",
7677
7751
  "certificateIds",
7678
7752
  "certificates",
7753
+ "channels",
7679
7754
  "chargingConnectors",
7680
7755
  "chargingPoolGroupIds",
7681
7756
  "chargingPoolGroups",
@@ -7757,8 +7832,13 @@ var LEFT_OUT_WHEN_EMPTY = /* @__PURE__ */ new Set([
7757
7832
  "vehicleTypes",
7758
7833
  "regularOpenings",
7759
7834
  "exceptionalOpenings",
7760
- "exceptionalClosings"
7835
+ "exceptionalClosings",
7836
+ "dataLicenses"
7761
7837
  ]);
7838
+ var FLAGS = {
7839
+ currentTypes: ["AC_OnePhase", "AC_ThreePhases", "DC"],
7840
+ chargingModes: ["Mode_1", "Mode_2", "Mode_3", "Mode_4", "CHAdeMO"]
7841
+ };
7762
7842
  var LEFT_OUT_WHEN_EMPTY_TEXT = {
7763
7843
  "": ["dataSource"],
7764
7844
  chargingStationOperators: ["dataSource"],
@@ -7886,7 +7966,8 @@ var ENTITY_PROPERTIES = {
7886
7966
  "stackTrace",
7887
7967
  "telephone",
7888
7968
  "termsAndConditions",
7889
- "uri"
7969
+ "uri",
7970
+ "webPaymentURL"
7890
7971
  ],
7891
7972
  "eMobilityProviders": [
7892
7973
  "@context",
@@ -8191,7 +8272,8 @@ var ENTITY_PROPERTIES = {
8191
8272
  "standards",
8192
8273
  "templateIds",
8193
8274
  "timeZone",
8194
- "uiFeatures"
8275
+ "uiFeatures",
8276
+ "webPaymentURL"
8195
8277
  ],
8196
8278
  "chargingPoolTemplates": [
8197
8279
  "@context",
@@ -8234,7 +8316,8 @@ var ENTITY_PROPERTIES = {
8234
8316
  "services",
8235
8317
  "standards",
8236
8318
  "timeZone",
8237
- "uiFeatures"
8319
+ "uiFeatures",
8320
+ "webPaymentURL"
8238
8321
  ],
8239
8322
  "chargingPoolGroups": [
8240
8323
  "@context",
@@ -8286,7 +8369,8 @@ var ENTITY_PROPERTIES = {
8286
8369
  "signatureMethods",
8287
8370
  "templateIds",
8288
8371
  "transparencySoftwareStatus",
8289
- "vehicleTypes"
8372
+ "vehicleTypes",
8373
+ "webPaymentURL"
8290
8374
  ],
8291
8375
  "chargingStationTemplates": [
8292
8376
  "@context",
@@ -8321,7 +8405,8 @@ var ENTITY_PROPERTIES = {
8321
8405
  "physicalReference",
8322
8406
  "signatureMethods",
8323
8407
  "transparencySoftwareStatus",
8324
- "vehicleTypes"
8408
+ "vehicleTypes",
8409
+ "webPaymentURL"
8325
8410
  ],
8326
8411
  "chargingStationGroups": [
8327
8412
  "@context",
@@ -8656,7 +8741,7 @@ var PROPERTIES_OF = {
8656
8741
  keyStatement: ["@context", "roamingNetworkId", "roamingNetworkOperatorId", "identityKeys", "minConsensus", "signedAt", "validUntil", "signatures"],
8657
8742
  signatures: ["algorithm", "keyId", "value"],
8658
8743
  signingKeyCertificates: ["@context", "roamingNetworkOperatorId", "signingKey", "signedAt", "algorithm", "keyId", "value"],
8659
- evTaler: ["@context", "merchantBaseURL", "merchantPublicKey", "exchanges"],
8744
+ evTaler: ["@context", "merchantBaseURL", "merchantPublicKey", "exchanges", "offersURL"],
8660
8745
  exchanges: ["@context", "masterPublicKey", "baseURL", "currency", "name"]
8661
8746
  };
8662
8747
  var PROPERTY_SETS = new Map(Object.entries(PROPERTIES_OF).map(([kind, properties]) => [kind, new Set(properties)]));
@@ -8669,6 +8754,7 @@ var TRIMMED = /* @__PURE__ */ new Set([
8669
8754
  "transparencySoftwareId",
8670
8755
  "version",
8671
8756
  "vulnerabilityContact",
8757
+ "hotline",
8672
8758
  "energyMeterManufacturerId",
8673
8759
  "energyMeterModel",
8674
8760
  "roamingNetworkId",
@@ -8757,8 +8843,8 @@ function writtenText(property, text2) {
8757
8843
  return LANGUAGES.has(text2.toLowerCase()) ? text2.toLowerCase() : text2;
8758
8844
  if (property === "exceptionalOpenings" || property === "exceptionalClosings")
8759
8845
  return periodOf(text2) ?? text2;
8760
- const names = NAMED_VALUES[property];
8761
- return names?.find((name) => name.toLowerCase() === trimmed(text2).toLowerCase()) ?? trimmed(text2);
8846
+ const names2 = NAMED_VALUES[property];
8847
+ return names2?.find((name) => name.toLowerCase() === trimmed(text2).toLowerCase()) ?? trimmed(text2);
8762
8848
  }
8763
8849
  function writtenTexts(items, property) {
8764
8850
  const texts3 = items.map((item) => typeof item === "string" ? writtenText(property, item) : void 0);
@@ -8767,7 +8853,16 @@ function writtenTexts(items, property) {
8767
8853
  const written2 = texts3;
8768
8854
  return ORDERED_TEXTS.has(property) ? [...written2].sort(ordinal) : written2;
8769
8855
  }
8770
- var URLS = /* @__PURE__ */ new Set(["homepage", "datasheetURL", "vulnerabilityReporting", "vulnerabilityDisclosurePolicy", "securityAdvisories", "merchantBaseURL", "baseURL"]);
8856
+ var URLS = /* @__PURE__ */ new Set([
8857
+ "homepage",
8858
+ "datasheetURL",
8859
+ "vulnerabilityReporting",
8860
+ "vulnerabilityDisclosurePolicy",
8861
+ "securityAdvisories",
8862
+ "merchantBaseURL",
8863
+ "baseURL",
8864
+ "offersURL"
8865
+ ]);
8771
8866
  var NAMED_VALUES = {
8772
8867
  role: ["battery", "evse", "grid", "gridConnectionPoint", "pool", "pv", "station", "wind"],
8773
8868
  legalStatus: ["forInformationOnly", "germanCalibrationLaw", "verified"],
@@ -8787,13 +8882,26 @@ function orderKeys(items, property) {
8787
8882
  return items.map((item) => isObject(item) ? asText(item["masterPublicKey"]) : "");
8788
8883
  if (property === "daysOfWeek")
8789
8884
  return items.map((item) => String(DAYS_OF_WEEK2.indexOf(typeof item === "string" ? item : "")).padStart(2, "0"));
8885
+ const flags2 = property === void 0 ? void 0 : FLAGS[property];
8886
+ if (flags2 !== void 0)
8887
+ return items.map((item) => String(flags2.indexOf(typeof item === "string" ? item : "")).padStart(2, "0"));
8790
8888
  if (items.length > 0 && items.every((item) => isObject(item) && typeof item["@id"] === "string"))
8791
8889
  return items.map(idOf4);
8792
8890
  if (property !== void 0 && ORDERED_TEXTS.has(property) && items.every((item) => typeof item === "string"))
8793
8891
  return items.map((item) => typeof item === "string" ? item : "");
8794
8892
  return void 0;
8795
8893
  }
8796
- var languagesOf2 = (text2) => [...new Set(Object.entries(text2).filter(([, value]) => typeof value === "string").map(([language]) => LANGUAGES.has(language.toLowerCase()) ? language.toLowerCase() : /^\d+$/u.test(language) && Number(language) < LANGUAGES.size ? `(language ${language})` : /^[+-]?\d+$/u.test(language) ? language : void 0).filter((language) => language !== void 0))];
8894
+ var LANGUAGE_NAMES = [...LANGUAGES];
8895
+ function languageOf(language) {
8896
+ if (LANGUAGES.has(language.toLowerCase()))
8897
+ return language.toLowerCase();
8898
+ if (!/^[+-]?\d+$/u.test(language))
8899
+ return void 0;
8900
+ const number = Number(language);
8901
+ return number >= 0 && number < LANGUAGE_NAMES.length ? LANGUAGE_NAMES[number] : String(number);
8902
+ }
8903
+ var textsOf2 = (text2) => Object.fromEntries(Object.entries(text2).filter(([, value]) => typeof value === "string").map(([language, value]) => [languageOf(language), value]).filter((entry) => entry[0] !== void 0));
8904
+ var languagesOf2 = (text2) => Object.keys(textsOf2(text2));
8797
8905
  function writtenValueOf(property, value, holder) {
8798
8906
  if ((property === "@id" || property === "type" && holder === "chargingConnectors") && (typeof value === "number" || typeof value === "boolean"))
8799
8907
  return String(value);
@@ -8806,9 +8914,9 @@ function writtenValueOf(property, value, holder) {
8806
8914
  return trimmed(value);
8807
8915
  if (URLS.has(property))
8808
8916
  return hermodURL(value)?.text;
8809
- const names = NAMED_VALUES[property];
8810
- if (names !== void 0)
8811
- return names.find((name) => name.toLowerCase() === trimmed(value).toLowerCase()) ?? trimmed(value);
8917
+ const names2 = NAMED_VALUES[property];
8918
+ if (names2 !== void 0)
8919
+ return names2.find((name) => name.toLowerCase() === trimmed(value).toLowerCase()) ?? trimmed(value);
8812
8920
  }
8813
8921
  if (holder === "geoLocation" && (property === "lat" || property === "lng" || property === "alt")) {
8814
8922
  const number = isNumber(value) ? Number(canonicalJSON(value)) : typeof value === "string" || typeof value === "boolean" ? doubleOf(value) : void 0;
@@ -8838,7 +8946,7 @@ function writtenValueOf(property, value, holder) {
8838
8946
  return valueText(value) ?? void 0;
8839
8947
  return void 0;
8840
8948
  }
8841
- var contentText = (value) => typeof value === "number" && !Number.isSafeInteger(value) ? String(value) : canonicalJSON(value);
8949
+ var contentText = (value) => value === void 0 ? "absent" : typeof value === "number" && !Number.isSafeInteger(value) ? String(value) : canonicalJSON(value);
8842
8950
  function asRead(value, kind) {
8843
8951
  if (isArray(value))
8844
8952
  return value.map((element) => asRead(element, kind));
@@ -8847,10 +8955,23 @@ function asRead(value, kind) {
8847
8955
  const known = PROPERTIES_OF[kind];
8848
8956
  return Object.fromEntries(Object.entries(value).filter(([key2]) => known === void 0 || known.includes(key2)).map(([key2, element]) => [key2, PROPERTIES_OF[key2] === void 0 ? element : asRead(element, key2)]));
8849
8957
  }
8958
+ function heldNumber(value) {
8959
+ if (typeof value !== "number" || Number.isSafeInteger(value))
8960
+ return value;
8961
+ try {
8962
+ return Decimal.parse(String(value));
8963
+ } catch {
8964
+ return void 0;
8965
+ }
8966
+ }
8850
8967
  function notWritten(value, property, path, of) {
8851
8968
  const eachOnce = of === "new value";
8852
8969
  const join2 = (at, key2) => at === "" ? key2 : `${at}.${key2}`;
8853
- const holdsAs = (at, written2) => ({ path: at, reason: `the content holds it as '${contentText(written2)}'` });
8970
+ const heldAt = (place, held) => ({ at: place, value: held });
8971
+ const holdsAs = (at, place, written2) => {
8972
+ const held = heldNumber(written2);
8973
+ return { path: at, reason: `the content holds it as '${contentText(written2)}'`, ...held === void 0 ? {} : { held: heldAt(place, held) } };
8974
+ };
8854
8975
  const leftOut = (key2, element, kind, keepsEmptyName) => {
8855
8976
  if (element === null)
8856
8977
  return "a property without a value is left out";
@@ -8869,22 +8990,53 @@ function notWritten(value, property, path, of) {
8869
8990
  return "the content leaves it out";
8870
8991
  return void 0;
8871
8992
  };
8872
- const check2 = (item, name, holder, at, root = false) => {
8993
+ const writtenOtherwise = (key2, element, kind, at, place) => {
8994
+ if (typeof element === "string" && TIMESTAMP_PROPERTIES.has(key2) && !TIMESTAMP4.test(element)) {
8995
+ const time = timestampOf(element);
8996
+ return {
8997
+ path: at,
8998
+ reason: "a timestamp is UTC to the millisecond, yyyy-MM-ddTHH:mm:ss.fffZ",
8999
+ ...time === void 0 ? {} : { held: heldAt(place, toTimestamp(time)) }
9000
+ };
9001
+ }
9002
+ const written2 = key2 === "customData" ? void 0 : writtenValueOf(key2, element, kind);
9003
+ if (written2 !== void 0 && (typeof written2 === "string" && typeof element === "string" ? written2 !== element : contentText(written2) !== contentText(element)))
9004
+ return holdsAs(at, place, written2);
9005
+ return void 0;
9006
+ };
9007
+ const check2 = (item, name, holder, at, place, root = false) => {
8873
9008
  if (name === "customData")
8874
9009
  return void 0;
8875
9010
  if (isArray(item)) {
8876
9011
  const written2 = name === void 0 ? void 0 : writtenTexts(item, name);
8877
9012
  const differs = written2?.findIndex((text2, index) => text2 !== item[index]) ?? -1;
8878
9013
  if (written2 !== void 0 && differs >= 0)
8879
- return holdsAs(`${at}[${String(differs)}]`, written2[differs] ?? "");
9014
+ return { ...holdsAs(`${at}[${String(differs)}]`, place, written2[differs] ?? ""), held: heldAt(place, written2) };
8880
9015
  if (name !== void 0 && ORDERED_TEXTS.has(name) && item.some((element) => typeof element === "string" && trimmed(element) !== element))
8881
- return { path: at, reason: "the texts of its elements are trimmed" };
9016
+ return {
9017
+ path: at,
9018
+ reason: "the texts of its elements are trimmed",
9019
+ held: heldAt(place, item.map((element) => typeof element === "string" ? trimmed(element) : element))
9020
+ };
8882
9021
  const keys = orderKeys(item, name);
8883
- if (keys?.some((key2, index) => index > 0 && ordinal(keys[index - 1] ?? "", key2) >= (eachOnce ? 0 : 1)) === true)
8884
- return { path: at, reason: eachOnce ? "its elements are in the order the content holds them in, each once" : "its elements are in the order the content holds them in" };
9022
+ if (keys?.some((key2, index) => index > 0 && ordinal(keys[index - 1] ?? "", key2) >= (eachOnce ? 0 : 1)) === true) {
9023
+ const seen = /* @__PURE__ */ new Set();
9024
+ const firsts = ({ key: key2 }) => {
9025
+ if (seen.has(key2))
9026
+ return false;
9027
+ seen.add(key2);
9028
+ return true;
9029
+ };
9030
+ const ordered2 = keys.map((key2, index) => ({ key: key2, index })).sort((left, right) => ordinal(left.key, right.key) || left.index - right.index).filter((entry) => !eachOnce || firsts(entry)).map(({ index }) => item[index] ?? null);
9031
+ return {
9032
+ path: at,
9033
+ reason: eachOnce ? "its elements are in the order the content holds them in, each once" : "its elements are in the order the content holds them in",
9034
+ held: heldAt(place, ordered2)
9035
+ };
9036
+ }
8885
9037
  for (const [index, element] of item.entries()) {
8886
9038
  const elementAt = `${at}[${String(index)}]`;
8887
- const found = check2(element, void 0, name, elementAt);
9039
+ const found = check2(element, void 0, name, elementAt, [...place, index]);
8888
9040
  if (found !== void 0)
8889
9041
  return found;
8890
9042
  }
@@ -8895,44 +9047,75 @@ function notWritten(value, property, path, of) {
8895
9047
  const party = name === void 0 && holder !== void 0 ? PARTY_NAMES[holder] : void 0;
8896
9048
  const keepsEmptyName = name === void 0 && holder === "chargingStationOperators";
8897
9049
  if (name !== void 0 && TEXTS_IN_LANGUAGES.has(name) && [...Object.keys(item)].sort(ordinal).join("\0") !== languagesOf2(item).sort(ordinal).join("\0"))
8898
- return { path: at, reason: "a text in languages names each language as Styx writes it, in lower case, with a text" };
9050
+ return {
9051
+ path: at,
9052
+ reason: "a text in languages names each language as Styx writes it, in lower case, with a text",
9053
+ held: heldAt(place, textsOf2(item))
9054
+ };
8899
9055
  for (const [key2, element] of Object.entries(item)) {
8900
9056
  if (key2 === "@context") {
8901
9057
  if (root)
8902
9058
  continue;
8903
- return { path: at, reason: "embedded, an object names no JSON-LD context" };
9059
+ return { path: at, reason: "embedded, an object names no JSON-LD context", held: heldAt([...place, key2], void 0) };
8904
9060
  }
8905
9061
  const elementAt = join2(at, key2);
9062
+ const elementPlace = [...place, key2];
8906
9063
  const omitted2 = leftOut(key2, element, kind, keepsEmptyName);
8907
9064
  if (omitted2 !== void 0)
8908
- return { path: elementAt, reason: omitted2 };
8909
- if (typeof element === "string" && TIMESTAMP_PROPERTIES.has(key2) && !TIMESTAMP4.test(element))
8910
- return { path: elementAt, reason: "a timestamp is UTC to the millisecond, yyyy-MM-ddTHH:mm:ss.fffZ" };
8911
- const written2 = key2 === "customData" ? void 0 : writtenValueOf(key2, element, kind);
8912
- if (written2 !== void 0 && (typeof written2 === "string" && typeof element === "string" ? written2 !== element : contentText(written2) !== contentText(element)))
8913
- return holdsAs(elementAt, written2);
8914
- const found = check2(element, key2, kind, elementAt);
9065
+ return { path: elementAt, reason: omitted2, held: heldAt(elementPlace, void 0) };
9066
+ const found = writtenOtherwise(key2, element, kind, elementAt, elementPlace) ?? check2(element, key2, kind, elementAt, elementPlace);
8915
9067
  if (found !== void 0)
8916
9068
  return found;
8917
9069
  }
8918
9070
  if (party !== void 0 && !("name" in item))
8919
- return holdsAs(join2(at, "name"), { en: `${party} '${asText(writtenValueOf("@id", item["@id"] ?? "", holder) ?? item["@id"])}'` });
9071
+ return holdsAs(join2(at, "name"), [...place, "name"], { en: `${party} '${asText(writtenValueOf("@id", item["@id"] ?? "", holder) ?? item["@id"])}'` });
8920
9072
  for (const list of name === void 0 && holder !== void 0 ? WRITTEN_ALSO_EMPTY[holder] ?? [] : [])
8921
9073
  if (!(list in item))
8922
- return holdsAs(join2(at, list), []);
9074
+ return holdsAs(join2(at, list), [...place, list], []);
8923
9075
  if (!eachOnce && typeof item["created"] === "string" && !("lastChange" in item))
8924
- return holdsAs(join2(at, "lastChange"), item["created"]);
9076
+ return holdsAs(join2(at, "lastChange"), [...place, "lastChange"], item["created"]);
8925
9077
  if (kind === "openingTimes" && !("24/7" in item)) {
8926
9078
  const regular = item["regularOpenings"];
8927
- return holdsAs(join2(at, "24/7"), !(isArray(regular) && regular.length > 0));
9079
+ return holdsAs(join2(at, "24/7"), [...place, "24/7"], !(isArray(regular) && regular.length > 0));
8928
9080
  }
8929
9081
  }
8930
9082
  return void 0;
8931
9083
  };
8932
9084
  const omitted = property === void 0 ? void 0 : leftOut(property, value, void 0, false);
8933
9085
  if (omitted !== void 0)
8934
- return { path, reason: omitted };
8935
- return check2(value, property, void 0, path, of === "version" && property === void 0);
9086
+ return { path, reason: omitted, held: heldAt([], void 0) };
9087
+ return (property === void 0 ? void 0 : writtenOtherwise(property, value, void 0, path, [])) ?? check2(value, property, void 0, path, [], of === "version" && property === void 0);
9088
+ }
9089
+ function heldAs(value, property) {
9090
+ let held = clone(value);
9091
+ for (let round = 0; round < 1e3; round++) {
9092
+ const found = notWritten(held, property, property ?? "", "new value");
9093
+ if (found === void 0)
9094
+ return { value: held };
9095
+ if (found.held === void 0)
9096
+ return void 0;
9097
+ const next = placed(held, found.held.at, found.held.value);
9098
+ if (next === void 0)
9099
+ return { value: void 0 };
9100
+ held = next;
9101
+ }
9102
+ return void 0;
9103
+ }
9104
+ function placed(value, at, held) {
9105
+ const [first2, ...rest] = at;
9106
+ if (first2 === void 0)
9107
+ return held;
9108
+ if (isArray(value) && typeof first2 === "number") {
9109
+ const element = value[first2];
9110
+ const inner = element === void 0 ? held : placed(element, rest, held);
9111
+ return inner === void 0 ? value.filter((_, index) => index !== first2) : value.map((item, index) => index === first2 ? inner : item);
9112
+ }
9113
+ if (isObject(value) && typeof first2 === "string") {
9114
+ const element = value[first2];
9115
+ const inner = element === void 0 || rest.length === 0 ? held : placed(element, rest, held);
9116
+ return inner === void 0 ? without(value, first2) : { ...value, [first2]: inner };
9117
+ }
9118
+ return value;
8936
9119
  }
8937
9120
  function notWrittenAsHeld(content) {
8938
9121
  const found = notWritten(content, void 0, "", "version");
@@ -9173,13 +9356,17 @@ var KeyStatement = class _KeyStatement {
9173
9356
  return { algorithm, keyId, value: value2 };
9174
9357
  });
9175
9358
  onlyProperties(json, ["@context", "roamingNetworkId", "roamingNetworkOperatorId", "identityKeys", "minConsensus", "signedAt", "validUntil", "signatures"], "A key statement");
9359
+ if (new Set(identityKeys.map((key2) => key2.id)).size !== identityKeys.length)
9360
+ throw new POIError("Invalid key statement: An identity key of a key statement must not be given twice!");
9176
9361
  if (identityKeys.some((key2, index) => {
9177
9362
  const before = identityKeys[index - 1];
9178
9363
  return before !== void 0 && before.id >= key2.id;
9179
9364
  }))
9180
9365
  throw new POIError("The identity keys of a key statement are written in the order of their @ids!");
9181
9366
  if (validUntil < signedAt)
9182
- throw new POIError("A key statement is not valid until before it was signed!");
9367
+ throw new POIError("Invalid key statement: A key statement is not valid until before it was signed. (Parameter 'ValidUntil')");
9368
+ if (new Set(signatures.map((signature) => signature.keyId)).size !== signatures.length)
9369
+ throw new POIError("Invalid key statement: An identity key signs a key statement once. (Parameter 'Signatures')");
9183
9370
  return new _KeyStatement(roamingNetworkId, roamingNetworkOperatorId, identityKeys, minConsensus, signedAt, validUntil, signatures, json);
9184
9371
  }
9185
9372
  /** The statement as it is signed: without `signatures`, with its `@context`, keys embedded without theirs. */
@@ -9428,12 +9615,12 @@ var RoamingNetworkOperator = class _RoamingNetworkOperator {
9428
9615
  const invalid = (error) => new POIError(`The given JSON representation of a roaming network operator is invalid: ${error}`);
9429
9616
  const certifications = certificates.map((certificate) => `${certificate.signingKey.id}/${certificate.keyId}`);
9430
9617
  if (new Set(certifications).size !== certifications.length)
9431
- throw invalid(`An certificate of a signing key of the roaming network operator '${id}' must not be given twice!`);
9618
+ throw invalid(`A certificate of a signing key of the roaming network operator '${id}' must not be given twice!`);
9432
9619
  const certified = /* @__PURE__ */ new Map();
9433
9620
  for (const certificate of certificates) {
9434
9621
  const before = certified.get(certificate.signingKey.id);
9435
9622
  if (before !== void 0 && !sameValue(without(before.json, "@context"), without(asRead(certificate.signingKey.json, "signingKey"), "@context")))
9436
- throw invalid(`An signing key of the roaming network operator '${id}' must not be given twice!`);
9623
+ throw invalid(`A signing key of the roaming network operator '${id}' must not be given twice!`);
9437
9624
  certified.set(certificate.signingKey.id, certificate.signingKey);
9438
9625
  }
9439
9626
  const signingKeys = [...certified.values()];
@@ -9483,6 +9670,18 @@ var RoamingNetworkOperator = class _RoamingNetworkOperator {
9483
9670
  return new _RoamingNetworkOperator(id, identityKeys, minConsensus, keyStatement, certificates, json);
9484
9671
  }
9485
9672
  };
9673
+ function invalidRoamingNetworkOperator(json, roamingNetworkId) {
9674
+ try {
9675
+ RoamingNetworkOperator.parse(json, roamingNetworkId);
9676
+ return void 0;
9677
+ } catch (e) {
9678
+ if (e instanceof KeyStatementError)
9679
+ return void 0;
9680
+ if (e instanceof POIError)
9681
+ return e.message;
9682
+ throw e;
9683
+ }
9684
+ }
9486
9685
  var RoamingNetwork = class _RoamingNetwork {
9487
9686
  constructor(id, content, operator, treeHashAlgorithms) {
9488
9687
  this.id = id;
@@ -9530,15 +9729,9 @@ var RoamingNetwork = class _RoamingNetwork {
9530
9729
  let treeHashAlgorithms = ["sha256"];
9531
9730
  const treeHashAlgorithmsJSON = get(json, "treeHashAlgorithms");
9532
9731
  if (treeHashAlgorithmsJSON !== void 0) {
9533
- if (!isArray(treeHashAlgorithmsJSON))
9534
- throw new POIError("The 'treeHashAlgorithms' must be a JSON array of hash algorithms!");
9535
- const unknown = treeHashAlgorithmsJSON.find((algorithm) => !isHashAlgorithm(algorithm));
9536
- if (unknown !== void 0)
9537
- throw new POIError(`Unknown tree hash algorithm '${asText(unknown)}': sha256, sha384 or sha512!`);
9538
- if (treeHashAlgorithmsJSON.length === 0)
9539
- throw new POIError("The 'treeHashAlgorithms' must name at least one hash algorithm!");
9540
- if (new Set(treeHashAlgorithmsJSON).size !== treeHashAlgorithmsJSON.length)
9541
- throw new POIError("The 'treeHashAlgorithms' must not name a hash algorithm twice!");
9732
+ const invalid = invalidTreeHashAlgorithms(treeHashAlgorithmsJSON);
9733
+ if (invalid !== void 0)
9734
+ throw new POIError(invalid);
9542
9735
  treeHashAlgorithms = treeHashAlgorithmsJSON;
9543
9736
  }
9544
9737
  if (get(json, "signingKeys") !== void 0 || get(json, "identityKeys") !== void 0)
@@ -10693,10 +10886,10 @@ function parsePreconditions(value) {
10693
10886
  const context2 = get(item, "@context");
10694
10887
  const entityType = get(item, "entityType");
10695
10888
  const entityId2 = get(item, "entityId");
10696
- if (context2 !== void 0 && context2 !== PRECONDITION_CONTEXT || typeof entityType !== "string" || entityType === "" || typeof entityId2 !== "string" || entityId2 === "")
10889
+ if (context2 !== void 0 && context2 !== PRECONDITION_CONTEXT || typeof entityType !== "string" || typeof entityId2 !== "string")
10697
10890
  throw invalid;
10698
10891
  try {
10699
- return { entityType, entityId: entityId2, eTag: parseETag(get(item, "eTag"), true) };
10892
+ return { entityType: trimmed(entityType), entityId: trimmed(entityId2), eTag: parseETag(get(item, "eTag"), true) };
10700
10893
  } catch {
10701
10894
  throw invalid;
10702
10895
  }
@@ -10832,12 +11025,119 @@ function brokenPrecondition(content, preconditions) {
10832
11025
 
10833
11026
  // src/history/changeSet.ts
10834
11027
  var CHANGE_SET_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/changeSet/v3";
11028
+ var CHANGE_CONTEXT = CONTEXT_PREFIX + "roamingNetwork/change/v1";
10835
11029
  var MERGE_CHANGE_SET_ID_PREFIX = "merge-";
10836
11030
  function changeSetIdentification(json) {
10837
- const content = without(json, "@id", "signatures");
11031
+ const content = without(asRead2(json), "@id", "signatures");
10838
11032
  set(content, "@context", CHANGE_SET_CONTEXT);
10839
11033
  return toHex(hash("sha256", canonicalJSONBytes(content)));
10840
11034
  }
11035
+ var readTrimmed = (text2) => trimmed(text2) === "" ? text2 : trimmed(text2);
11036
+ function asRead2(json) {
11037
+ const copy = { ...json };
11038
+ let changed = false;
11039
+ const networkId = get(json, "roamingNetworkId");
11040
+ if (typeof networkId === "string" && readTrimmed(networkId) !== networkId) {
11041
+ copy["roamingNetworkId"] = readTrimmed(networkId);
11042
+ changed = true;
11043
+ }
11044
+ const description = get(json, "description");
11045
+ if (description === null || isObject(description) && Object.keys(textsOf2(description)).length === 0) {
11046
+ delete copy["description"];
11047
+ changed = true;
11048
+ } else if (isObject(description) && canonicalJSON(textsOf2(description)) !== canonicalJSON(description)) {
11049
+ copy["description"] = textsOf2(description);
11050
+ changed = true;
11051
+ }
11052
+ const metadata2 = get(json, "metadata");
11053
+ if (isObject(metadata2) && Object.keys(metadata2).length === 0) {
11054
+ delete copy["metadata"];
11055
+ changed = true;
11056
+ }
11057
+ const preconditions = get(json, "preconditions");
11058
+ if (isArray(preconditions) && preconditions.length === 0) {
11059
+ delete copy["preconditions"];
11060
+ changed = true;
11061
+ } else if (isArray(preconditions)) {
11062
+ const read = preconditions.map(preconditionAsRead);
11063
+ if (read.some((precondition, index) => precondition !== preconditions[index])) {
11064
+ copy["preconditions"] = read;
11065
+ changed = true;
11066
+ }
11067
+ }
11068
+ const changes = changesAsRead(get(json, "changes"));
11069
+ if (changes !== get(json, "changes")) {
11070
+ copy["changes"] = changes ?? null;
11071
+ changed = true;
11072
+ }
11073
+ return changed ? copy : json;
11074
+ }
11075
+ var changeSetAsWritten = (json) => asRead2(json);
11076
+ function roamingNetworkIdOf(json, id) {
11077
+ const value = get(json, "roamingNetworkId");
11078
+ if (value === void 0)
11079
+ throw new POIError("Missing JSON property 'roamingNetworkId'!");
11080
+ if (value === null || typeof value === "string" && trimmed(value) === "")
11081
+ throw new POIError("Invalid 'roaming network identification'!");
11082
+ return trimmed(requireString(json, "roamingNetworkId", `The roaming network of the change set '${id}'`));
11083
+ }
11084
+ function preconditionAsRead(precondition) {
11085
+ if (!isObject(precondition))
11086
+ return precondition;
11087
+ const context2 = precondition["@context"];
11088
+ const entityType = precondition["entityType"];
11089
+ const entityId2 = precondition["entityId"];
11090
+ const eTag = precondition["eTag"];
11091
+ if (context2 !== void 0 && context2 !== PRECONDITION_CONTEXT || typeof entityType !== "string" || typeof entityId2 !== "string" || eTag === void 0)
11092
+ return precondition;
11093
+ try {
11094
+ parseETag(eTag, true);
11095
+ } catch {
11096
+ return precondition;
11097
+ }
11098
+ const read = { entityType: trimmed(entityType), entityId: trimmed(entityId2), eTag };
11099
+ return Object.keys(precondition).length === 3 && read["entityType"] === entityType && read["entityId"] === entityId2 ? precondition : read;
11100
+ }
11101
+ function changesAsRead(changes) {
11102
+ if (!isArray(changes))
11103
+ return changes;
11104
+ const read = changes.map((change) => {
11105
+ if (!isObject(change))
11106
+ return change;
11107
+ const copy = { ...change };
11108
+ let changed = false;
11109
+ for (const property of ["kind", "entityType", "entityId"]) {
11110
+ const value = change[property];
11111
+ if (typeof value === "string" && readTrimmed(value) !== value) {
11112
+ copy[property] = readTrimmed(value);
11113
+ changed = true;
11114
+ }
11115
+ }
11116
+ if (change["@context"] === CHANGE_CONTEXT) {
11117
+ delete copy["@context"];
11118
+ changed = true;
11119
+ }
11120
+ const path = change["elementPath"];
11121
+ if (isArray(path) && path.length === 0) {
11122
+ delete copy["elementPath"];
11123
+ changed = true;
11124
+ } else if (isArray(path) && path.some((segment) => isObject(segment) && (segment["@context"] === ELEMENT_PATH_SEGMENT_CONTEXT || typeof segment["propertyName"] === "string" && readTrimmed(segment["propertyName"]) !== segment["propertyName"]))) {
11125
+ copy["elementPath"] = path.map((segment) => {
11126
+ if (!isObject(segment))
11127
+ return segment;
11128
+ const segmentCopy = { ...segment };
11129
+ if (segment["@context"] === ELEMENT_PATH_SEGMENT_CONTEXT)
11130
+ delete segmentCopy["@context"];
11131
+ if (typeof segment["propertyName"] === "string")
11132
+ segmentCopy["propertyName"] = readTrimmed(segment["propertyName"]);
11133
+ return segmentCopy;
11134
+ });
11135
+ changed = true;
11136
+ }
11137
+ return changed ? copy : change;
11138
+ });
11139
+ return read.some((change, index) => change !== changes[index]) ? read : changes;
11140
+ }
10841
11141
  function checkRebasedFrom(json, id) {
10842
11142
  const metadata2 = get(json, "metadata");
10843
11143
  const rebasedFrom = isObject(metadata2) ? get(metadata2, "rebasedFrom") : void 0;
@@ -10874,7 +11174,7 @@ function checkRebasedFrom(json, id) {
10874
11174
  });
10875
11175
  const changes = get(json, "changes");
10876
11176
  (isArray(changes) ? changes : []).forEach((change, index) => {
10877
- if (isObject(change) && get(change, "oldValue") === void 0 && change["kind"] !== "Add" && change["kind"] !== "AddElement")
11177
+ if (isObject(change) && get(change, "oldValue") === void 0 && !["Add", "AddElement"].includes(readTrimmed(asText(change["kind"]))))
10878
11178
  throw new POIError(`Change ${index} of the change set '${id}', which was rebased, has no old value: the rebase may have overwritten a concurrent change!`);
10879
11179
  });
10880
11180
  }
@@ -10907,7 +11207,7 @@ function elementPathSegment(value, index) {
10907
11207
  throw new POIError(`Change ${index}: invalid element path segment: a schema property name is required!`);
10908
11208
  if (elementId !== void 0 && (typeof elementId !== "string" || blank(elementId)))
10909
11209
  throw new POIError(`Change ${index}: invalid element path segment: an element identifier must not be empty!`);
10910
- return { propertyName, elementId };
11210
+ return { propertyName: trimmed(propertyName), elementId };
10911
11211
  }
10912
11212
  function checkChange(change, index) {
10913
11213
  const refused = (reason) => {
@@ -10917,6 +11217,8 @@ function checkChange(change, index) {
10917
11217
  refused("its entity type and identification must not be empty!");
10918
11218
  if (change.parentEntityType === void 0 !== (change.parentEntityId === void 0))
10919
11219
  refused("parent type and parent identifier must be supplied together!");
11220
+ if (change.parentEntityType !== void 0 && (blank(change.parentEntityType) || blank(change.parentEntityId ?? "")))
11221
+ refused("its parent entity type and identification must not be empty!");
10920
11222
  if (change.newValue === null)
10921
11223
  refused("a new JSON value must not be null; a property is cleared by RemoveProperty!");
10922
11224
  const elementKind = ELEMENT_KINDS.includes(change.kind);
@@ -11002,37 +11304,57 @@ var ChangeSet = class _ChangeSet {
11002
11304
  "signatures"
11003
11305
  ], "A change set");
11004
11306
  const id = requireString(json, "@id", "The identification of a change set");
11005
- if (!id.startsWith(MERGE_CHANGE_SET_ID_PREFIX)) {
11006
- const contentId = changeSetIdentification(json);
11007
- if (id !== contentId)
11008
- throw new POIError(`The change set '${id}' is not identified by its content, '${contentId}'!`);
11009
- checkRebasedFrom(json, id);
11010
- }
11307
+ const roamingNetworkId = roamingNetworkIdOf(json, id);
11308
+ const createdAt = requireTimestamp(json, "createdAt", `The createdAt of the change set '${id}'`);
11011
11309
  const changes = requireArray(get(json, "changes"), `The changes of the change set '${id}'`).map((item, index) => {
11012
11310
  const change = requireObject(item, `Change ${index}`);
11013
11311
  onlyProperties(change, ["@context", "kind", "entityType", "entityId", "propertyName", "oldValue", "newValue", "parentEntityType", "parentEntityId", "elementPath"], `Change ${index}`);
11014
- const kind = requireString(change, "kind", `The kind of change ${index}`);
11312
+ const refused = (reason) => {
11313
+ throw new POIError(`Change ${index}: ${reason}`);
11314
+ };
11315
+ const context2 = get(change, "@context");
11316
+ if (context2 !== void 0 && context2 !== CHANGE_CONTEXT)
11317
+ refused(`the JSON-LD context of the roaming network change must be '${CHANGE_CONTEXT}'!`);
11318
+ const text2 = (property, description2) => {
11319
+ const value2 = get(change, property);
11320
+ return value2 === void 0 || typeof value2 === "string" ? value2 : refused(`the ${description2} must be a text!`);
11321
+ };
11322
+ const mandatory = (property, description2) => readTrimmed(text2(property, description2) ?? refused(`the ${description2} must be a text!`));
11323
+ const kind = mandatory("kind", "kind");
11015
11324
  if (!KINDS.includes(kind))
11016
- throw new POIError(`Change ${index}: unknown kind '${kind}'!`);
11325
+ refused(`unknown kind '${trimmed(kind)}'!`);
11326
+ const entityType = mandatory("entityType", "entity type");
11327
+ const entityId2 = mandatory("entityId", "entity identification");
11328
+ const propertyName = text2("propertyName", "property name");
11329
+ const parentEntityType = text2("parentEntityType", "parent entity type");
11330
+ const parentEntityId = text2("parentEntityId", "parent entity identification");
11017
11331
  const pathJSON = get(change, "elementPath");
11018
11332
  if (pathJSON !== void 0 && !isArray(pathJSON))
11019
- throw new POIError(`Change ${index}: the element path must be a JSON array!`);
11020
- const elementPath = pathJSON === void 0 || pathJSON.length === 0 ? void 0 : pathJSON.map((segment) => elementPathSegment(segment, index));
11333
+ refused("the element path must be a JSON array!");
11334
+ const elementPath = !isArray(pathJSON) || pathJSON.length === 0 ? void 0 : pathJSON.map((segment) => elementPathSegment(segment, index));
11021
11335
  const parsed = {
11022
11336
  kind,
11023
- entityType: requireString(change, "entityType", `The entity type of change ${index}`),
11024
- entityId: requireString(change, "entityId", `The entity of change ${index}`),
11025
- propertyName: optionalString(change, "propertyName", `The property of change ${index}`),
11337
+ entityType,
11338
+ entityId: entityId2,
11339
+ propertyName,
11026
11340
  oldValue: get(change, "oldValue"),
11027
11341
  newValue: get(change, "newValue"),
11028
- parentEntityType: optionalString(change, "parentEntityType", `The parent entity type of change ${index}`),
11029
- parentEntityId: optionalString(change, "parentEntityId", `The parent entity of change ${index}`),
11342
+ parentEntityType,
11343
+ parentEntityId,
11030
11344
  elementPath,
11031
11345
  json: change
11032
11346
  };
11033
11347
  checkChange(parsed, index);
11034
11348
  return parsed;
11035
11349
  });
11350
+ const beforeETags = parseETags(get(json, "beforeETags"), true, `The beforeETags of the change set '${id}'`);
11351
+ const afterETags = parseETags(get(json, "afterETags"), true, `The afterETags of the change set '${id}'`);
11352
+ const description = get(json, "description");
11353
+ if (description !== void 0 && description !== null && !isObject(description))
11354
+ throw new POIError("JSON property 'description' is not an object!");
11355
+ const metadata2 = get(json, "metadata");
11356
+ if (metadata2 !== void 0 && !isObject(metadata2))
11357
+ throw new POIError("The metadata must be a JSON object!");
11036
11358
  const preconditions = parsePreconditions(get(json, "preconditions"));
11037
11359
  const signatures = get(json, "signatures") === void 0 ? [] : requireArray(get(json, "signatures"), `The signatures of the change set '${id}'`).map((item) => {
11038
11360
  const signature = requireObject(item, "A signature of a change set");
@@ -11047,30 +11369,29 @@ var ChangeSet = class _ChangeSet {
11047
11369
  throw new POIError(`The ${property} of a signature of the change set '${id}' must not be empty!`);
11048
11370
  return read;
11049
11371
  });
11050
- return new _ChangeSet(
11051
- id,
11052
- requireString(json, "roamingNetworkId", `The roaming network of the change set '${id}'`),
11053
- requireTimestamp(json, "createdAt", `The createdAt of the change set '${id}'`),
11054
- changes,
11055
- preconditions,
11056
- parseETags(get(json, "beforeETags"), true, `The beforeETags of the change set '${id}'`),
11057
- parseETags(get(json, "afterETags"), true, `The afterETags of the change set '${id}'`),
11058
- signatures,
11059
- json
11060
- );
11372
+ if (!id.startsWith(MERGE_CHANGE_SET_ID_PREFIX)) {
11373
+ const contentId = changeSetIdentification(json);
11374
+ if (id !== contentId)
11375
+ throw new POIError(`The change set '${id}' is not identified by its content, '${contentId}'!`);
11376
+ checkRebasedFrom(json, id);
11377
+ }
11378
+ return new _ChangeSet(id, roamingNetworkId, createdAt, changes, preconditions, beforeETags, afterETags, signatures, json);
11061
11379
  }
11062
11380
  /** The description in its first language. */
11063
11381
  get description() {
11064
11382
  const description = get(this.json, "description");
11065
11383
  return isObject(description) && typeof Object.values(description)[0] === "string" ? Object.values(description)[0] : void 0;
11066
11384
  }
11067
- /** The signature input of one of its signatures (4): the context, its algorithm and key, and the change set without signatures. */
11385
+ /**
11386
+ * The signature input of one of its signatures (4): the context, its algorithm and key, and the change set without
11387
+ * signatures, as WWCP_POI3 writes it again (`ToJSON(Embedded: true, IncludeSignatures: false)`), as it is identified.
11388
+ */
11068
11389
  signingBytes(signature) {
11069
11390
  return canonicalJSONBytes({
11070
11391
  "@context": CHANGE_SET_CONTEXT,
11071
11392
  algorithm: signature.algorithm,
11072
11393
  keyId: signature.keyId,
11073
- changeSet: without(this.json, "signatures", "@context")
11394
+ changeSet: without(asRead2(this.json), "signatures", "@context")
11074
11395
  });
11075
11396
  }
11076
11397
  /** Verifies one signature with the given key. */
@@ -11224,20 +11545,26 @@ var Commit = class _Commit {
11224
11545
  throw new POIError(`The commit '${id}' of revision 0 has parents!`);
11225
11546
  return commit;
11226
11547
  }
11227
- /** What the identification is the digest of: the commit without `@id`, its signatures and those of its change set. */
11548
+ /**
11549
+ * What the identification is the digest of: the commit without `@id`, its signatures and those of its change set; its
11550
+ * change set as WWCP_POI3 writes it again, as it identifies a commit over it (RoamingNetworkCommit.TryParse).
11551
+ */
11228
11552
  identificationContent() {
11229
11553
  const content = without(this.json, "@id", "signatures", "changeSet");
11230
11554
  content["@context"] = COMMIT_CONTEXT2;
11231
11555
  if (this.changeSet !== void 0)
11232
- content["changeSet"] = without(this.changeSet, "signatures", "@context");
11556
+ content["changeSet"] = without(changeSetAsWritten(this.changeSet), "signatures", "@context");
11233
11557
  return content;
11234
11558
  }
11235
- /** The commit as a signature of it signs it: with its `@context`, its `@id` and the signatures of its change set (5.4). */
11559
+ /**
11560
+ * The commit as a signature of it signs it: with its `@context`, its `@id` and the signatures of its change set (5.4);
11561
+ * its change set as WWCP_POI3 writes it again.
11562
+ */
11236
11563
  signedContent() {
11237
11564
  const content = without(this.json, "signatures", "changeSet");
11238
11565
  content["@context"] = COMMIT_CONTEXT2;
11239
11566
  if (this.changeSet !== void 0)
11240
- content["changeSet"] = without(this.changeSet, "@context");
11567
+ content["changeSet"] = without(changeSetAsWritten(this.changeSet), "@context");
11241
11568
  return content;
11242
11569
  }
11243
11570
  /** The signature input of one of its signatures (5.4). */
@@ -11469,7 +11796,7 @@ var MANUFACTURER_PROPERTIES = [...PARTY_PROPERTIES, ...CONTACT_PROPERTIES];
11469
11796
  var RNO_PROPERTIES = [...REGISTRY_PROPERTIES, "identityKeys", "eMailAddress", "hotlineEMailAddress", "minConsensus", "keyStatement", "signingKeyCertificates"];
11470
11797
  var PARTY_REGISTRIES = {
11471
11798
  RoamingNetworkOperator: { what: "roaming network operator", article: "a", changeable: RNO_PROPERTIES },
11472
- ChargingStationOperator: { what: "charging station operator", article: "a", changeable: [...MANUFACTURER_PROPERTIES, "evTaler", "brands"] },
11799
+ ChargingStationOperator: { what: "charging station operator", article: "a", changeable: [...MANUFACTURER_PROPERTIES, "webPaymentURL", "evTaler", "brands"] },
11473
11800
  EMobilityProvider: { what: "e-mobility provider", article: "an", changeable: MANUFACTURER_PROPERTIES },
11474
11801
  GridOperator: { what: "grid operator", article: "a", changeable: PARTY_PROPERTIES },
11475
11802
  ParkingOperator: { what: "parking operator", article: "a", changeable: PARTY_PROPERTIES },
@@ -11798,6 +12125,24 @@ var OPERATOR_CHILD_ARRAYS = {
11798
12125
  };
11799
12126
  var TARIFF_TYPES = /* @__PURE__ */ new Set(["ChargingTariff", "ChargingTariffTemplate", "ChargingTariffGroup", "ChargingTariffOffer"]);
11800
12127
  var INFRASTRUCTURE_TYPES = /* @__PURE__ */ new Set([...Object.keys(OPERATOR_CHILD_ARRAYS), "ChargingPool", "ChargingStation", "EVSE"]);
12128
+ var INFRASTRUCTURE_IDS = {
12129
+ ChargingPool: poolId,
12130
+ ChargingStation: stationId,
12131
+ EVSE: evseId,
12132
+ ChargingTariff: tariffId,
12133
+ EVSEGroup: evseGroupId,
12134
+ ChargingStationGroup: stationGroupId,
12135
+ ChargingPoolGroup: poolGroupId,
12136
+ ChargingTariffGroup: tariffGroupId,
12137
+ EVSETemplate: evseTemplateId,
12138
+ ChargingStationTemplate: stationTemplateId,
12139
+ ChargingPoolTemplate: poolTemplateId,
12140
+ ChargingTariffTemplate: tariffTemplateId
12141
+ };
12142
+ function operatorIdOf(entityType, entityId2) {
12143
+ const id = INFRASTRUCTURE_IDS[entityType]?.(entityId2);
12144
+ return id === void 0 ? void 0 : operatorOf(id);
12145
+ }
11801
12146
  var POOL_TEMPLATE_PROPERTIES = [
11802
12147
  "dataLicenses",
11803
12148
  "address",
@@ -11805,6 +12150,7 @@ var POOL_TEMPLATE_PROPERTIES = [
11805
12150
  "parkingType",
11806
12151
  "accessibility",
11807
12152
  "hotline",
12153
+ "webPaymentURL",
11808
12154
  "gridConnectionPointId",
11809
12155
  "brands",
11810
12156
  "openingTimes",
@@ -11835,6 +12181,7 @@ var STATION_TEMPLATE_PROPERTIES = [
11835
12181
  "address",
11836
12182
  "geoLocation",
11837
12183
  "hotline",
12184
+ "webPaymentURL",
11838
12185
  "manufacturerId",
11839
12186
  "modelId",
11840
12187
  "modelURL",
@@ -11917,58 +12264,102 @@ var INFRASTRUCTURE = {
11917
12264
  ChargingPoolTemplate: { what: "charging pool template", changeable: [...BASE_PROPERTIES, "chargingTariffId", "customData", ...POOL_TEMPLATE_PROPERTIES] },
11918
12265
  ChargingTariffTemplate: { what: "charging tariff template", changeable: [...BASE_PROPERTIES, "customData", ...TARIFF_TEMPLATE_PROPERTIES] }
11919
12266
  };
11920
- function asGiven(value, property, where) {
11921
- const found = notWritten(value, property, property ?? "", "new value");
11922
- if (found !== void 0)
11923
- throw new POIError(`${where}: ${found.path === "" ? "it" : `'${found.path}'`} is not given as the content holds it: ${found.reason}!`);
12267
+ var heldChecks;
12268
+ function asGiven(value, property, change, index, what, heldId) {
12269
+ const check2 = () => {
12270
+ const refused = notHeldAsGiven(value, property, change, index, what, heldId);
12271
+ if (refused !== void 0)
12272
+ throw new POIError(refused);
12273
+ };
12274
+ if (heldChecks === void 0)
12275
+ check2();
12276
+ else
12277
+ heldChecks.push(check2);
11924
12278
  return clone(value);
11925
12279
  }
12280
+ function notHeldAsGiven(given2, property, change, index, what, heldId) {
12281
+ const element = change.elementChange;
12282
+ if (element !== void 0 && (element.kind === "RemoveElement" || element.kind === "RemoveElementProperty"))
12283
+ return void 0;
12284
+ const found = notWritten(given2, property, property ?? "", "new value");
12285
+ if (found === void 0 && (heldId === void 0 || !isObject(given2) || given2["@id"] === heldId))
12286
+ return void 0;
12287
+ const held = heldAs(given2, property);
12288
+ if (held === void 0)
12289
+ return found === void 0 ? void 0 : `Change ${index}, the ${what}${change.kind === "Add" ? " to add" : ""}: ${found.path === "" ? "it" : `'${found.path}'`} is not given as the content holds it: ${found.reason}!`;
12290
+ let heldValue = heldId !== void 0 && isObject(held.value) ? { ...held.value, "@id": heldId } : held.value;
12291
+ let givenValue = given2;
12292
+ let named2;
12293
+ if (element !== void 0) {
12294
+ const elementProperty = element.kind === "UpdateElementProperty";
12295
+ const removal = applyElement({ ...element, kind: elementProperty ? "RemoveElementProperty" : "RemoveElement", newValue: void 0 }, heldValue);
12296
+ named2 = `'${pathText(element.elementPath ?? [], elementProperty ? element.propertyName : void 0)}' of the ${what}`;
12297
+ givenValue = element.newValue ?? null;
12298
+ heldValue = removal.error === void 0 ? removal.current : void 0;
12299
+ } else
12300
+ named2 = change.kind === "Add" ? `the ${what} to add` : `the property '${property ?? ""}' of the ${what}`;
12301
+ if (heldValue === void 0)
12302
+ return `Change ${index}: ${named2} is given as '${contentText(givenValue)}', which the content does not hold at all!`;
12303
+ return sameValue(givenValue, heldValue) ? void 0 : `Change ${index}: ${named2} is not given as the content holds it, '${contentText(heldValue)}'!`;
12304
+ }
11926
12305
  function insertOrdered(array, entity) {
11927
12306
  const id = asText(entity["@id"]);
11928
12307
  const index = array.findIndex((item) => isObject(item) && ordinal(asText(item["@id"]), id) > 0);
11929
12308
  array.splice(index < 0 ? array.length : index, 0, entity);
11930
12309
  }
11931
12310
  var objectsOf4 = (value) => isArray(value) ? value.filter(isObject) : [];
12311
+ function names(stored, entityType, given2) {
12312
+ if (typeof stored !== "string")
12313
+ return false;
12314
+ if (stored === given2)
12315
+ return true;
12316
+ const parse = INFRASTRUCTURE_IDS[entityType];
12317
+ const left = parse?.(stored);
12318
+ const right = parse?.(given2);
12319
+ return left !== void 0 && left.key === right?.key;
12320
+ }
11932
12321
  function find(operator, entityType, entityId2) {
11933
- const operatorId2 = asText(operator["@id"]);
12322
+ const ownId = asText(operator["@id"]);
11934
12323
  const childArray = OPERATOR_CHILD_ARRAYS[entityType];
11935
12324
  if (childArray !== void 0) {
11936
12325
  const children2 = get(operator, childArray);
11937
12326
  for (const child of objectsOf4(children2))
11938
- if (child["@id"] === entityId2)
11939
- return { entity: child, container: children2, holder: operator, property: childArray, parentType: "ChargingStationOperator", parentId: operatorId2 };
12327
+ if (names(child["@id"], entityType, entityId2))
12328
+ return { entity: child, container: children2, holder: operator, property: childArray, parentType: "ChargingStationOperator", parentId: ownId };
11940
12329
  return void 0;
11941
12330
  }
11942
12331
  const pools = get(operator, "chargingPools");
11943
12332
  for (const pool of objectsOf4(pools)) {
11944
12333
  if (entityType === "ChargingPool") {
11945
- if (pool["@id"] === entityId2)
11946
- return { entity: pool, container: pools, holder: operator, property: "chargingPools", parentType: "ChargingStationOperator", parentId: operatorId2 };
12334
+ if (names(pool["@id"], entityType, entityId2))
12335
+ return { entity: pool, container: pools, holder: operator, property: "chargingPools", parentType: "ChargingStationOperator", parentId: ownId };
11947
12336
  continue;
11948
12337
  }
11949
12338
  const stations = get(pool, "chargingStations");
11950
12339
  for (const station of objectsOf4(stations)) {
11951
12340
  if (entityType === "ChargingStation") {
11952
- if (station["@id"] === entityId2)
12341
+ if (names(station["@id"], entityType, entityId2))
11953
12342
  return { entity: station, container: stations, holder: pool, property: "chargingStations", parentType: "ChargingPool", parentId: asText(pool["@id"]) };
11954
12343
  continue;
11955
12344
  }
11956
12345
  const evses = get(station, "EVSEs");
11957
12346
  for (const evse2 of objectsOf4(evses))
11958
- if (evse2["@id"] === entityId2)
12347
+ if (names(evse2["@id"], entityType, entityId2))
11959
12348
  return { entity: evse2, container: evses, holder: station, property: "EVSEs", parentType: "ChargingStation", parentId: asText(station["@id"]) };
11960
12349
  }
11961
12350
  }
11962
12351
  return void 0;
11963
12352
  }
11964
- function operatorOf2(content, entityId2) {
11965
- let found;
11966
- for (const operator of objectsOf4(get(content, "chargingStationOperators"))) {
11967
- const id = asText(operator["@id"]);
11968
- if (entityId2.startsWith(id + "*") && (found === void 0 || id.length > asText(found["@id"]).length))
11969
- found = operator;
12353
+ var operatorNamed = (content, id) => objectsOf4(get(content, "chargingStationOperators")).find((operator) => operatorId(asText(operator["@id"]))?.key === operatorId(id)?.key);
12354
+ function storedId(operator, entityType, entityId2) {
12355
+ if (entityId2 === void 0)
12356
+ return void 0;
12357
+ if (entityType === "ChargingStationOperator") {
12358
+ const given2 = operatorId(entityId2);
12359
+ return given2 !== void 0 && given2.key === operatorId(asText(operator["@id"]))?.key ? asText(operator["@id"]) : void 0;
11970
12360
  }
11971
- return found;
12361
+ const found = INFRASTRUCTURE_IDS[entityType] === void 0 ? void 0 : find(operator, entityType, entityId2);
12362
+ return found === void 0 ? void 0 : asText(found.entity["@id"]);
11972
12363
  }
11973
12364
  function touchParents(operator, parentType, parentId, lastChange) {
11974
12365
  while ((parentType === "ChargingStation" || parentType === "ChargingPool") && parentId !== void 0) {
@@ -11986,7 +12377,7 @@ function checkOldValue(change, current, index, what) {
11986
12377
  if (change.oldValue === void 0)
11987
12378
  return;
11988
12379
  if (!oldValueMatches(change.oldValue, current))
11989
- throw new POIError(change.propertyName !== void 0 && (change.kind === "UpdateProperty" || change.kind === "RemoveProperty") ? `Change ${index}: the property '${change.propertyName}' of the ${what} is no longer the expected old value!` : `Change ${index}: the ${what} is no longer the expected old value${current === void 0 ? ": it does not exist" : ""}!`);
12380
+ throw new POIError(change.propertyName !== void 0 && (change.kind === "UpdateProperty" || change.kind === "RemoveProperty") ? `Change ${index}: the property '${change.propertyName}' of the ${what} is no longer the expected old value, but '${contentText(current)}'!` : `Change ${index}: the ${what} is no longer the expected old value${current === void 0 ? ": it does not exist" : ""}!`);
11990
12381
  }
11991
12382
  function resolveElementChange(change, entity, what, index) {
11992
12383
  if (!isElementChange(change))
@@ -12000,20 +12391,21 @@ function resolveElementChange(change, entity, what, index) {
12000
12391
  if (applied.error !== void 0)
12001
12392
  throw new POIError(`Change ${index}: ${change.kind} of the ${what}: ${applied.error}`);
12002
12393
  if (change.oldValue !== void 0 && !oldValueMatches(change.oldValue, applied.current))
12003
- throw new POIError(`Change ${index}: '${pathText(change.elementPath ?? [], change.propertyName)}' of the ${what} is no longer the expected old value!`);
12394
+ throw new POIError(`Change ${index}: '${pathText(change.elementPath ?? [], change.propertyName)}' of the ${what} is no longer the expected old value, but '${contentText(applied.current)}'!`);
12004
12395
  return {
12005
12396
  ...change,
12006
12397
  kind: applied.newValue === void 0 ? "RemoveProperty" : "UpdateProperty",
12007
12398
  propertyName: property,
12008
12399
  oldValue: void 0,
12009
12400
  newValue: applied.newValue,
12010
- elementPath: void 0
12401
+ elementPath: void 0,
12402
+ elementChange: change
12011
12403
  };
12012
12404
  }
12013
12405
  function applyProperty(entity, change, index, what, lastChange) {
12014
12406
  const property = change.propertyName;
12015
12407
  if (property === void 0 || property === "@id" || property === "created" || property === "lastChange")
12016
- throw new POIError(`Change ${index}: the property '${property ?? ""}' of a ${what} can not be changed!`);
12408
+ throw new POIError(`Change ${index}: the property '${property ?? ""}' of ${withArticle(what, false)} can not be changed!`);
12017
12409
  if (change.kind === "RemoveProperty") {
12018
12410
  if (get(entity, property) === void 0)
12019
12411
  throw new POIError(`Change ${index}: the property '${property}' of the ${what} does not exist!`);
@@ -12021,27 +12413,31 @@ function applyProperty(entity, change, index, what, lastChange) {
12021
12413
  } else {
12022
12414
  if (change.newValue === void 0)
12023
12415
  throw new POIError(`Change ${index}: an UpdateProperty needs its newValue!`);
12024
- set(entity, property, asGiven(change.newValue, property, `Change ${String(index)}, the ${what}`));
12416
+ set(entity, property, asGiven(change.newValue, property, change, index, what));
12025
12417
  }
12026
12418
  if (lastChange !== void 0)
12027
12419
  entity["lastChange"] = lastChange;
12028
12420
  }
12029
- function entityToAdd(change, index, what, createdAt) {
12421
+ function entityToAdd(change, index, what, createdAt, heldId) {
12030
12422
  if (!isObject(change.newValue) || change.newValue["@id"] !== change.entityId)
12031
12423
  throw new POIError(`Change ${index}: the ${what} to add must be a JSON object with the identification '${change.entityId}'!`);
12032
- const added = asGiven(without(change.newValue, "lastChange"), void 0, `Change ${String(index)}, the ${what} to add`);
12424
+ const added = asGiven(without(change.newValue, "lastChange"), void 0, change, index, what, heldId);
12033
12425
  if (get(added, "created") === void 0)
12034
12426
  added["created"] = createdAt;
12035
12427
  added["lastChange"] = createdAt;
12036
12428
  return added;
12037
12429
  }
12038
12430
  function applyInfrastructureChange(content, change, index, createdAt) {
12039
- const what = `${INFRASTRUCTURE[change.entityType]?.what ?? change.entityType} '${change.entityId}'`;
12040
- const operator = operatorOf2(content, change.entityId);
12431
+ const description = INFRASTRUCTURE[change.entityType]?.what ?? change.entityType;
12432
+ const what = `${description} '${change.entityId}'`;
12433
+ const ownerId = operatorIdOf(change.entityType, change.entityId);
12434
+ if (ownerId === void 0)
12435
+ throw new POIError(`Change ${index}: invalid ${description} identification '${change.entityId}'!`);
12436
+ const operator = operatorNamed(content, ownerId);
12041
12437
  if (operator === void 0)
12042
- throw new POIError(`Change ${index}: the charging station operator of the ${what} does not exist!`);
12438
+ throw new POIError(`Change ${index}: the charging station operator '${ownerId}' of the ${what} does not exist!`);
12043
12439
  const found = find(operator, change.entityType, change.entityId);
12044
- if (found !== void 0 && change.parentEntityType !== void 0 && (change.parentEntityType !== found.parentType || change.parentEntityId !== found.parentId))
12440
+ if (found !== void 0 && change.parentEntityType !== void 0 && (change.parentEntityType !== found.parentType || storedId(operator, change.parentEntityType, change.parentEntityId) !== found.parentId))
12045
12441
  throw new POIError(`Change ${index}: the ${what} belongs to ${found.parentType} '${found.parentId}', not to ${change.parentEntityType} '${change.parentEntityId}'!`);
12046
12442
  change = resolveElementChange(change, found?.entity, what, index);
12047
12443
  if (change.kind !== "Add" && found === void 0)
@@ -12056,36 +12452,38 @@ function applyInfrastructureChange(content, change, index, createdAt) {
12056
12452
  case "RemoveProperty": {
12057
12453
  const infrastructure = required(INFRASTRUCTURE[change.entityType], change.entityType);
12058
12454
  if (!infrastructure.changeable.includes(change.propertyName ?? ""))
12059
- throw new POIError(`Change ${index}: the property '${change.propertyName ?? ""}' of a ${infrastructure.what} can not be changed!`);
12455
+ throw new POIError(`Change ${index}: the property '${change.propertyName ?? ""}' of ${withArticle(infrastructure.what, false)} can not be changed!`);
12060
12456
  applyProperty(existing().entity, change, index, what, createdAt);
12061
12457
  break;
12062
12458
  }
12063
12459
  case "Add": {
12064
12460
  if (found !== void 0)
12065
12461
  throw new POIError(`Change ${index}: the ${what} already exists!`);
12066
- const added = entityToAdd(change, index, what, createdAt);
12067
- parentType = change.parentEntityType ?? "ChargingStationOperator";
12068
- parentId = change.parentEntityId ?? asText(operator["@id"]);
12462
+ const added = entityToAdd(change, index, what, createdAt, INFRASTRUCTURE_IDS[change.entityType]?.(change.entityId)?.text);
12069
12463
  let holder;
12070
12464
  let property;
12071
12465
  if (OPERATOR_CHILD_ARRAYS[change.entityType] !== void 0 || change.entityType === "ChargingPool") {
12072
- if (parentType !== "ChargingStationOperator" || parentId !== operator["@id"])
12073
- throw new POIError(`Change ${index}: the ${what} is added to its charging station operator '${asText(operator["@id"])}', not to ${parentType} '${parentId}'!`);
12466
+ if (change.parentEntityType !== void 0 && (change.parentEntityType !== "ChargingStationOperator" || storedId(operator, "ChargingStationOperator", change.parentEntityId) === void 0))
12467
+ throw new POIError(`Change ${index}: ${withArticle(what, false)} belongs to ChargingStationOperator '${asText(operator["@id"])}', not to ${change.parentEntityType} '${change.parentEntityId ?? ""}'!`);
12074
12468
  holder = operator;
12075
12469
  property = OPERATOR_CHILD_ARRAYS[change.entityType] ?? "chargingPools";
12076
12470
  } else if (change.entityType === "ChargingStation") {
12077
- if (parentType !== "ChargingPool")
12078
- throw new POIError(`Change ${index}: a charging station is added to a charging pool!`);
12079
- holder = find(operator, "ChargingPool", parentId)?.entity;
12471
+ if (change.parentEntityType !== "ChargingPool" || change.parentEntityId === void 0)
12472
+ throw new POIError(`Change ${index}: adding the charging station '${change.entityId}' needs its ChargingPool as its parent!`);
12473
+ holder = find(operator, "ChargingPool", change.parentEntityId)?.entity;
12080
12474
  property = "chargingStations";
12475
+ if (holder === void 0)
12476
+ throw new POIError(`Change ${index}: the charging pool '${change.parentEntityId}' of the charging station '${change.entityId}' does not exist!`);
12081
12477
  } else {
12082
- if (parentType !== "ChargingStation")
12083
- throw new POIError(`Change ${index}: an EVSE is added to a charging station!`);
12084
- holder = find(operator, "ChargingStation", parentId)?.entity;
12478
+ if (change.parentEntityType !== "ChargingStation" || change.parentEntityId === void 0)
12479
+ throw new POIError(`Change ${index}: adding the EVSE '${change.entityId}' needs its ChargingStation as its parent!`);
12480
+ holder = find(operator, "ChargingStation", change.parentEntityId)?.entity;
12085
12481
  property = "EVSEs";
12482
+ if (holder === void 0)
12483
+ throw new POIError(`Change ${index}: the charging station '${change.parentEntityId}' of the EVSE '${change.entityId}' does not exist!`);
12086
12484
  }
12087
- if (holder === void 0)
12088
- throw new POIError(`Change ${index}: the ${parentType} '${parentId}' to add the ${what} to does not exist!`);
12485
+ parentType = change.parentEntityType ?? "ChargingStationOperator";
12486
+ parentId = storedId(operator, parentType, change.parentEntityId ?? asText(operator["@id"]));
12089
12487
  const children2 = get(holder, property);
12090
12488
  if (children2 === void 0)
12091
12489
  set(holder, property, [added]);
@@ -12103,9 +12501,10 @@ function applyInfrastructureChange(content, change, index, createdAt) {
12103
12501
  break;
12104
12502
  }
12105
12503
  default:
12106
- throw new POIError(`Change ${index}: ${change.kind} of a ${change.entityType} is not supported by this library yet!`);
12504
+ throw new POIError(`Change ${index}: ${change.kind} of ${withArticle(INFRASTRUCTURE[change.entityType]?.what ?? change.entityType, false)} is not supported by this library yet!`);
12107
12505
  }
12108
12506
  touchParents(operator, parentType, parentId, createdAt);
12507
+ return { type: "ChargingStationOperator", json: operator };
12109
12508
  }
12110
12509
  function applyNetworkArrayChange(content, change, index, createdAt) {
12111
12510
  const property = required(NETWORK_ARRAYS[change.entityType], `Change ${index}: ${change.entityType} is no party!`);
@@ -12127,7 +12526,7 @@ function applyNetworkArrayChange(content, change, index, createdAt) {
12127
12526
  case "UpdateProperty":
12128
12527
  case "RemoveProperty":
12129
12528
  applyProperty(existing(), change, index, what, createdAt);
12130
- break;
12529
+ return { type: change.entityType, json: existing() };
12131
12530
  case "Add": {
12132
12531
  if (entity !== void 0)
12133
12532
  throw new POIError(`Change ${index}: the ${what} already exists!`);
@@ -12136,7 +12535,7 @@ function applyNetworkArrayChange(content, change, index, createdAt) {
12136
12535
  set(content, property, [added]);
12137
12536
  else if (isArray(array))
12138
12537
  insertOrdered(array, added);
12139
- break;
12538
+ return { type: change.entityType, json: added };
12140
12539
  }
12141
12540
  case "Remove": {
12142
12541
  if (entity === void 0)
@@ -12145,10 +12544,10 @@ function applyNetworkArrayChange(content, change, index, createdAt) {
12145
12544
  items.splice(items.indexOf(entity), 1);
12146
12545
  if (items.length === 0)
12147
12546
  unset(content, property);
12148
- break;
12547
+ return void 0;
12149
12548
  }
12150
12549
  default:
12151
- throw new POIError(`Change ${index}: ${change.kind} of a ${change.entityType} is not supported by this library yet!`);
12550
+ throw new POIError(`Change ${index}: ${change.kind} of ${registry.article} ${registry.what} is not supported by this library yet!`);
12152
12551
  }
12153
12552
  }
12154
12553
  function partyChildOf(content, type, childId) {
@@ -12182,7 +12581,7 @@ function applyPartyChildChange(content, change, index, createdAt) {
12182
12581
  if (change.kind !== "Add" && found2 === void 0)
12183
12582
  throw new POIError(`Change ${index}: the ${what} of the ${type.inParent} '${asText(party2["@id"])}' does not exist!`);
12184
12583
  applyPartyChild(type, party2, found2, change, index, what, createdAt);
12185
- return;
12584
+ return { type: type.parentType, json: party2 };
12186
12585
  }
12187
12586
  const found = partyChildOf(content, type, change.entityId);
12188
12587
  let party;
@@ -12201,6 +12600,7 @@ function applyPartyChildChange(content, change, index, createdAt) {
12201
12600
  party = found.party;
12202
12601
  }
12203
12602
  applyPartyChild(type, party, found, change, index, what, createdAt);
12603
+ return { type: type.parentType, json: party };
12204
12604
  }
12205
12605
  function applyPartyChild(type, party, found, change, index, what, createdAt) {
12206
12606
  change = resolveElementChange(change, found?.child, what, index);
@@ -12214,7 +12614,7 @@ function applyPartyChild(type, party, found, change, index, what, createdAt) {
12214
12614
  const property = change.propertyName ?? "";
12215
12615
  const child = existing().child;
12216
12616
  if (!type.changeable.includes(property))
12217
- throw new POIError(type.keepsVersion === true && property === "version" ? `Change ${index}: the version of the ${what} can not be changed: a new version is a new release!` : `Change ${index}: the property '${property}' of a ${type.what} can not be changed!`);
12617
+ throw new POIError(type.keepsVersion === true && property === "version" ? `Change ${index}: the version of the ${what} can not be changed: a new version is a new release!` : `Change ${index}: the property '${property}' of ${withArticle(type.what, false)} can not be changed!`);
12218
12618
  if (change.kind === "RemoveProperty") {
12219
12619
  if (get(child, property) === void 0)
12220
12620
  throw new POIError(`Change ${index}: the property '${property}' of the ${what} does not exist!`);
@@ -12222,14 +12622,14 @@ function applyPartyChild(type, party, found, change, index, what, createdAt) {
12222
12622
  } else {
12223
12623
  if (change.newValue === void 0)
12224
12624
  throw new POIError(`Change ${index}: an UpdateProperty needs its newValue!`);
12225
- set(child, property, asGiven(change.newValue, property, `Change ${String(index)}, the ${what}`));
12625
+ set(child, property, asGiven(change.newValue, property, change, index, what));
12226
12626
  }
12227
12627
  break;
12228
12628
  }
12229
12629
  case "Add": {
12230
12630
  if (!isObject(change.newValue) || typeof change.newValue["@id"] !== "string" || !sameId4(change.newValue["@id"], change.entityId))
12231
12631
  throw new POIError(`Change ${index}: the ${type.what} to add must be a JSON object with the identification '${change.entityId}'!`);
12232
- const added = asGiven(without(change.newValue, "lastChange"), void 0, `Change ${String(index)}, the ${what} to add`);
12632
+ const added = asGiven(without(change.newValue, "lastChange"), void 0, change, index, what);
12233
12633
  const children2 = get(party, type.property);
12234
12634
  if (children2 === void 0)
12235
12635
  set(party, type.property, [added]);
@@ -12247,7 +12647,7 @@ function applyPartyChild(type, party, found, change, index, what, createdAt) {
12247
12647
  break;
12248
12648
  }
12249
12649
  default:
12250
- throw new POIError(`Change ${index}: ${change.kind} of a ${type.what} is not supported by this library yet!`);
12650
+ throw new POIError(`Change ${index}: ${change.kind} of ${withArticle(type.what, false)} is not supported by this library yet!`);
12251
12651
  }
12252
12652
  party["lastChange"] = createdAt;
12253
12653
  }
@@ -12259,15 +12659,17 @@ function applyEmbeddedChange(content, change, index, createdAt, type) {
12259
12659
  const what = `${type.what} '${gridPoint ? id.text : change.entityId}'`;
12260
12660
  const found = embeddedOf(content, type, change.entityId);
12261
12661
  let party;
12662
+ let partyType;
12262
12663
  let property;
12263
12664
  if (change.kind === "Add") {
12264
12665
  if (found !== void 0)
12265
12666
  throw new POIError(gridPoint ? `Change ${index}: the ${what} already exists, at the grid operator '${asText(found.party["@id"])}'!` : `Change ${index}: the ${what} already exists!`);
12266
12667
  const parentProperty = change.parentEntityType === void 0 ? void 0 : type.owners[change.parentEntityType];
12267
12668
  const parent = change.parentEntityType === void 0 || parentProperty === void 0 ? void 0 : partyNamed(content, change.parentEntityType, change.parentEntityId);
12268
- if (parent === void 0 || parentProperty === void 0)
12669
+ if (parent === void 0 || parentProperty === void 0 || change.parentEntityType === void 0)
12269
12670
  throw new POIError(`Change ${index}: the ${what} to add needs its ${type.addNeeds(change.parentEntityType)}, an existing one!`);
12270
12671
  party = parent;
12672
+ partyType = change.parentEntityType;
12271
12673
  property = parentProperty;
12272
12674
  } else {
12273
12675
  if (found === void 0)
@@ -12275,6 +12677,7 @@ function applyEmbeddedChange(content, change, index, createdAt, type) {
12275
12677
  if (change.parentEntityType !== void 0 && (change.parentEntityType !== found.partyType || change.parentEntityId === void 0 || !sameId4(change.parentEntityId, asText(found.party["@id"]))))
12276
12678
  throw new POIError(`Change ${index}: the ${what} belongs to ${found.partyType} '${asText(found.party["@id"])}', not to ${change.parentEntityType} '${change.parentEntityId ?? ""}'!`);
12277
12679
  party = found.party;
12680
+ partyType = found.partyType;
12278
12681
  property = required(type.owners[found.partyType], found.partyType);
12279
12682
  }
12280
12683
  change = resolveElementChange(change, found?.child, what, index);
@@ -12285,14 +12688,14 @@ function applyEmbeddedChange(content, change, index, createdAt, type) {
12285
12688
  case "UpdateProperty":
12286
12689
  case "RemoveProperty":
12287
12690
  if (!type.changeable.includes(change.propertyName ?? ""))
12288
- throw new POIError(`Change ${index}: the property '${change.propertyName ?? ""}' of a ${type.what} can not be changed!`);
12691
+ throw new POIError(`Change ${index}: the property '${change.propertyName ?? ""}' of ${withArticle(type.what, false)} can not be changed!`);
12289
12692
  applyProperty(existing().child, change, index, what, type.timestamps ? createdAt : void 0);
12290
12693
  break;
12291
12694
  case "Add": {
12292
12695
  const given2 = change.newValue;
12293
12696
  if (!isObject(given2) || typeof given2["@id"] !== "string" || (gridPoint ? type.id(given2["@id"])?.key !== id.key : given2["@id"] !== change.entityId))
12294
12697
  throw new POIError(`Change ${index}: the ${type.what} to add must be a JSON object with the identification '${gridPoint ? id.text : change.entityId}'!`);
12295
- const added = type.timestamps ? entityToAdd(change, index, what, createdAt) : asGiven(given2, void 0, `Change ${String(index)}, the ${what} to add`);
12698
+ const added = type.timestamps ? entityToAdd(change, index, what, createdAt) : asGiven(given2, void 0, change, index, what);
12296
12699
  const children2 = get(party, property);
12297
12700
  if (children2 === void 0)
12298
12701
  set(party, property, [added]);
@@ -12310,9 +12713,10 @@ function applyEmbeddedChange(content, change, index, createdAt, type) {
12310
12713
  break;
12311
12714
  }
12312
12715
  default:
12313
- throw new POIError(`Change ${index}: ${change.kind} of a ${type.what} is not supported by this library yet!`);
12716
+ throw new POIError(`Change ${index}: ${change.kind} of ${withArticle(type.what, false)} is not supported by this library yet!`);
12314
12717
  }
12315
12718
  party["lastChange"] = createdAt;
12719
+ return { type: partyType, json: party };
12316
12720
  }
12317
12721
  function energyMeterChildOf(content, type, childId) {
12318
12722
  for (const [partyType, parties] of [["ChargingStationOperator", "chargingStationOperators"], ["GridOperator", "gridOperators"]])
@@ -12329,19 +12733,22 @@ function applyEnergyMeterChildChange(content, change, index, createdAt) {
12329
12733
  const what = `${type.what} '${change.entityId}'`;
12330
12734
  const found = energyMeterChildOf(content, type, change.entityId);
12331
12735
  let party;
12736
+ let partyType;
12332
12737
  if (change.kind === "Add") {
12333
12738
  if (found !== void 0)
12334
12739
  throw new POIError(`Change ${index}: the ${what} already exists!`);
12335
12740
  const parent = change.parentEntityType === "ChargingStationOperator" || change.parentEntityType === "GridOperator" ? partyNamed(content, change.parentEntityType, change.parentEntityId) : void 0;
12336
- if (parent === void 0)
12741
+ if (parent === void 0 || change.parentEntityType === void 0)
12337
12742
  throw new POIError(`Change ${index}: the ${what} to add needs its ChargingStationOperator or GridOperator, an existing one!`);
12338
12743
  party = parent;
12744
+ partyType = change.parentEntityType;
12339
12745
  } else {
12340
12746
  if (found === void 0)
12341
12747
  throw new POIError(`Change ${index}: the ${what} does not exist!`);
12342
12748
  if (change.parentEntityType !== void 0 && (change.parentEntityType !== found.partyType || change.parentEntityId === void 0 || !sameId4(change.parentEntityId, asText(found.party["@id"]))))
12343
12749
  throw new POIError(`Change ${index}: the ${what} belongs to ${found.partyType} '${asText(found.party["@id"])}', not to ${change.parentEntityType} '${change.parentEntityId ?? ""}'!`);
12344
12750
  party = found.party;
12751
+ partyType = found.partyType;
12345
12752
  }
12346
12753
  change = resolveElementChange(change, found?.child, what, index);
12347
12754
  const isProperty = change.kind === "UpdateProperty" || change.kind === "RemoveProperty";
@@ -12351,7 +12758,7 @@ function applyEnergyMeterChildChange(content, change, index, createdAt) {
12351
12758
  case "UpdateProperty":
12352
12759
  case "RemoveProperty":
12353
12760
  if (!type.changeable.includes(change.propertyName ?? ""))
12354
- throw new POIError(`Change ${index}: the property '${change.propertyName ?? ""}' of an ${type.what} can not be changed!`);
12761
+ throw new POIError(`Change ${index}: the property '${change.propertyName ?? ""}' of ${withArticle(type.what, false)} can not be changed!`);
12355
12762
  applyProperty(existing().child, change, index, what, createdAt);
12356
12763
  break;
12357
12764
  case "Add": {
@@ -12373,37 +12780,77 @@ function applyEnergyMeterChildChange(content, change, index, createdAt) {
12373
12780
  break;
12374
12781
  }
12375
12782
  default:
12376
- throw new POIError(`Change ${index}: ${change.kind} of an ${type.what} is not supported by this library yet!`);
12783
+ throw new POIError(`Change ${index}: ${change.kind} of ${withArticle(type.what, false)} is not supported by this library yet!`);
12377
12784
  }
12378
12785
  party["lastChange"] = createdAt;
12786
+ return { type: partyType, json: party };
12379
12787
  }
12380
12788
  var NETWORK_PROPERTIES = ["name", "description", "dataSource", "dataLicenses", "treeHashAlgorithms", "customData"];
12789
+ function invalidNetworkValue(property, value) {
12790
+ switch (property) {
12791
+ case "name":
12792
+ case "description":
12793
+ return isObject(value) ? void 0 : `invalid ${property}: A multi-language text must be a JSON object!`;
12794
+ case "dataSource":
12795
+ return typeof value === "string" && trimmed(value) !== "" ? void 0 : "the data source must be a nonempty text!";
12796
+ case "dataLicenses":
12797
+ return dataLicenses({ dataLicenses: value });
12798
+ case "treeHashAlgorithms":
12799
+ return value === null ? "the tree hash algorithms must be an array of hash algorithms!" : invalidTreeHashAlgorithms(value);
12800
+ case "customData":
12801
+ return isObject(value) ? void 0 : "the custom data must be a JSON object!";
12802
+ case "signingKeys":
12803
+ case "identityKeys":
12804
+ return "a roaming network has no keys of its own: they are the keys of its RoamingNetworkOperator!";
12805
+ default:
12806
+ return `the property '${property}' of a roaming network can not be changed!`;
12807
+ }
12808
+ }
12381
12809
  function applyNetworkChange(content, change, index, createdAt) {
12382
12810
  const what = `roaming network '${asText(content["@id"])}'`;
12383
- const property = entityPropertyName(change) ?? "";
12384
- const held = NETWORK_PROPERTIES.includes(property) ? get(content, property) : void 0;
12385
12811
  const own = {};
12812
+ const ownValue = (property2) => NETWORK_PROPERTIES.includes(property2) ? get(content, property2) : void 0;
12813
+ const given2 = entityPropertyName(change) ?? "";
12814
+ const held = ownValue(given2);
12386
12815
  if (held !== void 0)
12387
- set(own, property, held);
12816
+ set(own, given2, held);
12388
12817
  change = resolveElementChange(change, own, what, index);
12389
- if (change.kind !== "UpdateProperty" && change.kind !== "RemoveProperty")
12390
- throw new POIError(`Change ${index}: ${change.kind} of the roaming network itself is not possible!`);
12391
- if (!NETWORK_PROPERTIES.includes(change.propertyName ?? ""))
12392
- throw new POIError(`Change ${index}: the property '${change.propertyName ?? ""}' of a roaming network can not be changed!`);
12393
- checkOldValue(change, get(content, change.propertyName ?? ""), index, what);
12818
+ const property = change.propertyName ?? "";
12819
+ const current = ownValue(property);
12820
+ if (change.oldValue !== void 0 && !oldValueMatches(change.oldValue, current))
12821
+ throw new POIError(`Change ${index}: the property '${property}' is no longer the expected old value, but '${contentText(current)}'!`);
12822
+ switch (change.kind) {
12823
+ case "UpdateProperty": {
12824
+ const invalid = change.newValue === void 0 ? void 0 : invalidNetworkValue(property, change.newValue);
12825
+ if (invalid !== void 0)
12826
+ throw new POIError(`Change ${index}: ${invalid}`);
12827
+ break;
12828
+ }
12829
+ case "RemoveProperty":
12830
+ if (current === void 0)
12831
+ throw new POIError(`Change ${index}: the property '${property}' does not exist!`);
12832
+ break;
12833
+ default:
12834
+ throw new POIError(`Change ${index}: ${change.kind} of the roaming network itself is not supported yet!`);
12835
+ }
12394
12836
  applyProperty(content, change, index, what, createdAt);
12395
12837
  }
12396
12838
  function applyOperatorChange(content, change, index, createdAt) {
12839
+ if (change.kind === "Add" || change.kind === "Remove")
12840
+ throw new POIError(`Change ${index}: the operator of a roaming network is changed, but neither added nor removed!`);
12841
+ if (change.parentEntityType !== void 0 && (change.parentEntityType !== "RoamingNetwork" || change.parentEntityId !== content["@id"]))
12842
+ throw new POIError(`Change ${index}: a roaming network operator of this roaming network belongs to RoamingNetwork '${asText(content["@id"])}', not to ${change.parentEntityType} '${change.parentEntityId}'!`);
12397
12843
  const operator = get(content, "operator");
12398
12844
  if (!isObject(operator) || operator["@id"] !== change.entityId)
12399
- throw new POIError(`Change ${index}: the roaming network operator '${change.entityId}' is not the one of this roaming network!`);
12845
+ throw new POIError(`Change ${index}: the roaming network operator '${change.entityId}' does not exist!`);
12400
12846
  change = resolveElementChange(change, operator, `roaming network operator '${change.entityId}'`, index);
12401
12847
  if (change.kind !== "UpdateProperty" && change.kind !== "RemoveProperty")
12402
- throw new POIError(`Change ${index}: the roaming network operator is neither added nor removed!`);
12848
+ throw new POIError(`Change ${index}: the operator of a roaming network is changed, but neither added nor removed!`);
12403
12849
  if (!RNO_PROPERTIES.includes(change.propertyName ?? ""))
12404
12850
  throw new POIError(`Change ${index}: the property '${change.propertyName ?? ""}' of a roaming network operator can not be changed!`);
12405
12851
  checkOldValue(change, get(operator, change.propertyName ?? ""), index, `roaming network operator '${change.entityId}'`);
12406
12852
  applyProperty(operator, change, index, `roaming network operator '${change.entityId}'`, createdAt);
12853
+ return { type: "RoamingNetworkOperator", json: operator };
12407
12854
  }
12408
12855
  function partiesOf(network) {
12409
12856
  const parties = /* @__PURE__ */ new Map();
@@ -12418,6 +12865,26 @@ function partiesOf(network) {
12418
12865
  });
12419
12866
  return parties;
12420
12867
  }
12868
+ var mayAll = (key2, usages) => usages.every((usage) => key2.maySign(usage));
12869
+ var WEB_PAYMENT_PAGES = /* @__PURE__ */ new Set(["ChargingStationOperator", "ChargingPool", "ChargingStation", "ChargingPoolTemplate", "ChargingStationTemplate"]);
12870
+ var isGiven4 = (value) => value !== void 0 && value !== null;
12871
+ function addsWebPaymentURL(change) {
12872
+ if (change.kind !== "Add" || !isObject(change.newValue))
12873
+ return false;
12874
+ const entity = change.newValue;
12875
+ const stations = entity["chargingStations"];
12876
+ switch (change.entityType) {
12877
+ case "ChargingPool":
12878
+ return isGiven4(entity["webPaymentURL"]) || isArray(stations) && stations.some((station) => isObject(station) && isGiven4(station["webPaymentURL"]));
12879
+ case "ChargingStation":
12880
+ case "ChargingPoolTemplate":
12881
+ case "ChargingStationTemplate":
12882
+ return isGiven4(entity["webPaymentURL"]);
12883
+ default:
12884
+ return false;
12885
+ }
12886
+ }
12887
+ var usagesOf = (change, usage) => WEB_PAYMENT_PAGES.has(change.entityType) && entityPropertyName(change) === "webPaymentURL" ? [KeyUsages.Payments] : usage !== KeyUsages.Payments && addsWebPaymentURL(change) ? [usage, KeyUsages.Payments] : [usage];
12421
12888
  var partyKeyOf = (party) => `${party.type}:${party.id.toUpperCase()}`;
12422
12889
  var partyRefOf = (content, type, id) => ({ type, id: asText(partyNamed(content, type, id)?.["@id"] ?? id) });
12423
12890
  var notRemoved = (earlier, party) => earlier.removedParties.has(partyKeyOf(party)) ? null : party;
@@ -12572,13 +13039,13 @@ function ownerOf(content, change, index, earlier) {
12572
13039
  if (embeddedOwners !== void 0)
12573
13040
  return embeddedOwners;
12574
13041
  if (INFRASTRUCTURE_TYPES.has(change.entityType)) {
12575
- const operator = operatorOf2(content, change.entityId);
12576
- if (operator === void 0)
12577
- throw new POIError(`Change ${index}: the charging station operator of the ${change.entityType} '${change.entityId}' does not exist!`);
13042
+ const ownerId = operatorIdOf(change.entityType, change.entityId);
13043
+ if (ownerId === void 0)
13044
+ throw new POIError(`Change ${index}: the identification '${change.entityId}' of the ${change.entityType} names no charging station operator!`);
12578
13045
  return {
12579
13046
  kind: "party",
12580
13047
  type: "ChargingStationOperator",
12581
- id: asText(operator["@id"]),
13048
+ id: ownerId,
12582
13049
  usage: TARIFF_TYPES.has(change.entityType) ? KeyUsages.Tariffs : KeyUsages.Infrastructure,
12583
13050
  keys: "signing"
12584
13051
  };
@@ -12664,24 +13131,32 @@ function checkOwnership(before, changeSet) {
12664
13131
  if (!valid2.has(key2.id))
12665
13132
  throw new POIError(`The new identity key '${key2.id}' did not sign the change set: no proof of possession!`);
12666
13133
  const signedValid = (key2) => valid2.has(key2.id) && key2.isValidAt(createdAt);
12667
- const rnoHolders = (keys, ...usages) => new Set([
12668
- ...keys !== "identity" ? rnoSigning.filter((key2) => [KeyUsages.Party, ...usages].some((usage) => key2.maySign(usage))) : [],
13134
+ const rnoHolders = (keys, usages = []) => new Set([
13135
+ ...keys !== "identity" ? rnoSigning.filter((key2) => key2.maySign(KeyUsages.Party) || usages.length > 0 && mayAll(key2, usages)) : [],
12669
13136
  ...keys !== "signing" ? rnoIdentity : []
12670
13137
  ].filter(signedValid).map((key2) => key2.holderOrUnnamed));
12671
- const signs = (owner) => {
13138
+ const signs = (owner, change) => {
12672
13139
  if (owner.kind === "rno")
12673
13140
  return rno !== void 0 && rnoHolders(owner.keys).size >= minConsensus;
12674
13141
  const party = parties.get(`${owner.type}:${owner.id}`);
12675
- return party !== void 0 && (owner.keys !== "identity" && party.signingKeys.some((key2) => key2.maySign(owner.usage) && signedValid(key2)) || owner.keys !== "signing" && party.identityKeys.some(signedValid) || owner.keys !== "signing" && rnoHolders("identity").size >= minConsensus && rnoIdentity.some(signedValid) || owner.keys === "signing" && owner.inPlace !== false && party.signingKeys.length === 0 && rnoHolders("signing", owner.usage).size >= minConsensus);
13142
+ const usages = usagesOf(change, owner.usage);
13143
+ return party !== void 0 && (owner.keys !== "identity" && party.signingKeys.some((key2) => mayAll(key2, usages) && signedValid(key2)) || owner.keys !== "signing" && party.identityKeys.some(signedValid) || owner.keys !== "signing" && rnoHolders("identity").size >= minConsensus && rnoIdentity.some(signedValid) || owner.keys === "signing" && owner.inPlace !== false && party.signingKeys.length === 0 && rnoHolders("signing", usages).size >= minConsensus);
13144
+ };
13145
+ const inPlaceOf = (owner) => owner.kind === "party" && owner.keys === "signing" && owner.inPlace !== false && parties.get(`${owner.type}:${owner.id}`)?.signingKeys.length === 0;
13146
+ const rnoInWords = rno === void 0 ? "the roaming network operator, of which this roaming network has none" : `the roaming network operator '${rno.id}'`;
13147
+ const byQuorum = (owner) => minConsensus > 1 && (owner.kind === "rno" || inPlaceOf(owner));
13148
+ const signatureOf2 = (owner) => {
13149
+ const kind = owner.keys === "identity" ? "identity" : "signing";
13150
+ return owner.kind === "rno" || inPlaceOf(owner) ? byQuorum(owner) ? `${kind} keys of ${minConsensus} holders of ${rnoInWords}` : `a${kind === "identity" ? "n" : ""} ${kind} key of ${rnoInWords}` : owner.keys === "identity" ? `an identity key of the ${owner.type} '${owner.id}'` : `a signing key of the ${owner.type} '${owner.id}'`;
12676
13151
  };
12677
- const signatureOf2 = (owner) => owner.kind === "rno" ? `${owner.keys === "identity" ? "an identity key" : "a signing key"} of the roaming network operator '${rno?.id ?? ""}'` : owner.keys === "identity" ? `an identity key of the ${owner.type} '${owner.id}'` : `a signing key of the ${owner.type} '${owner.id}'`;
13152
+ const signaturesOf = (owners) => `${owners.some(byQuorum) ? "signatures" : "a signature"} by ${[...new Set(owners.map(signatureOf2))].join(" or ")}`;
12678
13153
  changeSet.changes.forEach((change, index) => {
12679
13154
  checkPartyChange(change, index);
12680
13155
  const owned = ownerOf(before.content, change, index, earlier);
12681
13156
  const owners = isOwnerList(owned) ? owned : [owned];
12682
13157
  if (owners.length > 1) {
12683
- if (!owners.some(signs))
12684
- throw new POIError(`${describeChange(change, index)} needs a signature by ${owners.map(signatureOf2).join(" or ")}!`);
13158
+ if (!owners.some((owner2) => signs(owner2, change)))
13159
+ throw new POIError(`${describeChange(change, index)} needs ${signaturesOf(owners)}!`);
12685
13160
  return;
12686
13161
  }
12687
13162
  const owner = required(owners[0], `Change ${index} has no owner!`);
@@ -12695,14 +13170,21 @@ function checkOwnership(before, changeSet) {
12695
13170
  }
12696
13171
  const party = parties.get(`${owner.type}:${owner.id}`);
12697
13172
  if (party === void 0)
12698
- throw new POIError(`${describeChange(change, index)}: the ${owner.type} '${owner.id}' does not exist!`);
12699
- const bySigningKey = owner.keys !== "identity" && party.signingKeys.some((key2) => key2.maySign(owner.usage) && signedValid(key2));
13173
+ throw new POIError(owner.keys === "identity" ? `${describeChange(change, index)} needs a signature by an identity key of the ${owner.type} '${owner.id}'!` : `${describeChange(change, index)} needs a signature by a signing key of the ${owner.type} '${owner.id}'!`);
13174
+ const usages = usagesOf(change, owner.usage);
13175
+ const bySigningKey = owner.keys !== "identity" && party.signingKeys.some((key2) => mayAll(key2, usages) && signedValid(key2));
12700
13176
  const byIdentityKey = owner.keys !== "signing" && party.identityKeys.some(signedValid);
12701
13177
  const byRecovery = owner.keys !== "signing" && rnoHolders("identity").size >= minConsensus && rnoIdentity.some(signedValid);
12702
- const byRNO = owner.keys === "signing" && owner.inPlace !== false && party.signingKeys.length === 0 && rnoHolders("signing", owner.usage).size >= minConsensus;
13178
+ const byRNO = owner.keys === "signing" && owner.inPlace !== false && party.signingKeys.length === 0 && rnoHolders("signing", usages).size >= minConsensus;
12703
13179
  if (!bySigningKey && !byIdentityKey && !byRecovery && !byRNO) {
13180
+ const allOf = usages.map((usage) => `'${usage}'`).join(" and ");
13181
+ if (inPlaceOf(owner)) {
13182
+ const anyOf2 = usages.length === 1 && usages[0] === KeyUsages.Party ? allOf : `${allOf} or '${KeyUsages.Party}'`;
13183
+ const named2 = rnoSigning.some((key2) => signedValid(key2) && !mayAll(key2, usages) && !key2.maySign(KeyUsages.Party));
13184
+ throw new POIError(`${describeChange(change, index)} needs ${signaturesOf([owner])}${named2 ? ` that may sign ${anyOf2}` : ""}!`);
13185
+ }
12704
13186
  const mayAnyway = party.signingKeys.some(signedValid);
12705
- throw new POIError(owner.keys === "identity" ? `${describeChange(change, index)} needs a signature by an identity key of the ${owner.type} '${owner.id}'!` : mayAnyway ? `${describeChange(change, index)} needs a signature by a signing key of the ${owner.type} '${owner.id}' that may sign '${owner.usage}'!` : `${describeChange(change, index)} needs a signature by a signing key of the ${owner.type} '${owner.id}'!`);
13187
+ throw new POIError(owner.keys === "identity" ? `${describeChange(change, index)} needs a signature by an identity key of the ${owner.type} '${owner.id}'!` : mayAnyway ? `${describeChange(change, index)} needs a signature by a signing key of the ${owner.type} '${owner.id}' that may sign ${allOf}!` : `${describeChange(change, index)} needs a signature by a signing key of the ${owner.type} '${owner.id}'!`);
12706
13188
  }
12707
13189
  });
12708
13190
  }
@@ -12721,7 +13203,7 @@ function stampOf(before, changeSet) {
12721
13203
  return last !== void 0 && last > changeSet.createdAt ? last : changeSet.createdAt;
12722
13204
  }
12723
13205
  function applyChangeSet(before, changeSet, options = {}) {
12724
- if (changeSet.roamingNetworkId !== before.id)
13206
+ if (ignoringCase(changeSet.roamingNetworkId) !== ignoringCase(before.id))
12725
13207
  throw new POIError(`The change set is for the roaming network '${changeSet.roamingNetworkId}', not for '${before.id}'!`);
12726
13208
  if (options.checkOwnership ?? true)
12727
13209
  checkOwnership(before, changeSet);
@@ -12731,23 +13213,37 @@ function applyChangeSet(before, changeSet, options = {}) {
12731
13213
  throw new POIError(`The change set does not fit this version of the roaming network: ${broken}`);
12732
13214
  const content = clone(before.content);
12733
13215
  const createdAt = toTimestamp(stampOf(before, changeSet));
12734
- changeSet.changes.forEach((change, index) => {
13216
+ function applyChange(change, index) {
13217
+ let changed;
12735
13218
  if (change.entityType === "RoamingNetwork" && change.entityId === before.id)
12736
13219
  applyNetworkChange(content, change, index, createdAt);
12737
13220
  else if (change.entityType === "RoamingNetworkOperator")
12738
- applyOperatorChange(content, change, index, createdAt);
13221
+ changed = applyOperatorChange(content, change, index, createdAt);
12739
13222
  else if (NETWORK_ARRAYS[change.entityType] !== void 0)
12740
- applyNetworkArrayChange(content, change, index, createdAt);
13223
+ changed = applyNetworkArrayChange(content, change, index, createdAt);
12741
13224
  else if (PARTY_CHILD_TYPES[change.entityType] !== void 0)
12742
- applyPartyChildChange(content, change, index, createdAt);
13225
+ changed = applyPartyChildChange(content, change, index, createdAt);
12743
13226
  else if (ENERGY_METER_CHILD_TYPES[change.entityType] !== void 0)
12744
- applyEnergyMeterChildChange(content, change, index, createdAt);
13227
+ changed = applyEnergyMeterChildChange(content, change, index, createdAt);
12745
13228
  else if (embeddedTypeOf(content, change) !== void 0)
12746
- applyEmbeddedChange(content, change, index, createdAt, required(embeddedTypeOf(content, change), change.entityType));
13229
+ changed = applyEmbeddedChange(content, change, index, createdAt, required(embeddedTypeOf(content, change), change.entityType));
12747
13230
  else if (INFRASTRUCTURE_TYPES.has(change.entityType))
12748
- applyInfrastructureChange(content, change, index, createdAt);
13231
+ changed = applyInfrastructureChange(content, change, index, createdAt);
12749
13232
  else
12750
13233
  throw new POIError(`Change ${index}: changes of ${change.entityType} '${change.entityId}' are not supported by this library yet!`);
13234
+ const invalid = changed === void 0 ? void 0 : changed.type === "RoamingNetworkOperator" ? invalidRoamingNetworkOperator(changed.json, before.id) : invalidParty(changed.type, changed.json);
13235
+ if (invalid !== void 0)
13236
+ throw new POIError(`Change ${index}: ${invalid}`);
13237
+ for (const check2 of heldChecks ?? [])
13238
+ check2();
13239
+ }
13240
+ changeSet.changes.forEach((change, index) => {
13241
+ heldChecks = [];
13242
+ try {
13243
+ applyChange(change, index);
13244
+ } finally {
13245
+ heldChecks = void 0;
13246
+ }
12751
13247
  });
12752
13248
  const groupOrOffer = objectsOf4(get(content, "chargingStationOperators")).sort((left, right) => ordinal(asText(left["@id"]), asText(right["@id"]))).map((operator) => invalidGroupOrOffer(operator) ?? invalidPaymentOption(operator)).find((error) => error !== void 0);
12753
13249
  if (groupOrOffer !== void 0)
@@ -13712,7 +14208,7 @@ function manufacturerOf(network, kind, id) {
13712
14208
  }
13713
14209
  var CHARGING_STATION_ID = /^([a-zA-Z]{2}\*?[a-zA-Z0-9]{3})\*?S([a-zA-Z0-9][a-zA-Z0-9*]{0,29})$/u;
13714
14210
  var OPERATOR_ID = /^([A-Z]{2})\*?([A-Z0-9]{3})$/u;
13715
- function operatorIdOf(text2) {
14211
+ function operatorIdOf2(text2) {
13716
14212
  const match = OPERATOR_ID.exec((idOf2(text2) ?? "").toUpperCase());
13717
14213
  return match === null ? void 0 : `${match[1] ?? ""}*${match[2] ?? ""}`;
13718
14214
  }
@@ -13720,13 +14216,13 @@ function chargingStationIdOf(text2) {
13720
14216
  const match = CHARGING_STATION_ID.exec(idOf2(text2) ?? "");
13721
14217
  if (match === null)
13722
14218
  return void 0;
13723
- const operatorId2 = operatorIdOf(match[1] ?? "");
14219
+ const operatorId2 = operatorIdOf2(match[1] ?? "");
13724
14220
  return operatorId2 === void 0 ? void 0 : { operatorId: operatorId2, suffix: match[2] ?? "" };
13725
14221
  }
13726
14222
  var chargingStationIdText = (id) => `${id.operatorId}*S${id.suffix}`;
13727
14223
  var sameChargingStation = (left, right) => left.operatorId === right.operatorId && sameId(left.suffix.replaceAll("*", ""), right.suffix.replaceAll("*", ""));
13728
14224
  function effectiveChargingStationOf(network, id) {
13729
- const operator = objectsOf5(get(network.effectiveContent, "chargingStationOperators")).find((item) => operatorIdOf(entityId(item)) === id.operatorId);
14225
+ const operator = objectsOf5(get(network.effectiveContent, "chargingStationOperators")).find((item) => operatorIdOf2(entityId(item)) === id.operatorId);
13730
14226
  for (const pool of objectsOf5(get(operator ?? {}, "chargingPools")))
13731
14227
  for (const station of objectsOf5(get(pool, "chargingStations"))) {
13732
14228
  const stationId2 = chargingStationIdOf(entityId(station));
@@ -16125,6 +16621,16 @@ var EVTalerContractTerms = class _EVTalerContractTerms {
16125
16621
  };
16126
16622
  }
16127
16623
  };
16624
+ function evTalerSessionPrices(station, evseId2, priceStatements, at) {
16625
+ const tariff2 = evTalerTariff(station, evseId2);
16626
+ const current = verifyPriceStatements(station, priceStatements, { at });
16627
+ const tariffId2 = idOf7(tariff2);
16628
+ const specificity = (entityType) => entityType === "EVSE" ? 0 : entityType === "ChargingStation" ? 1 : 2;
16629
+ const [priceStatement] = [...current.values()].filter((candidate) => candidate.statement.prices.some((prices) => tariffId(textOf4(get(prices, "chargingTariffId")))?.text === tariffId2)).sort((left, right) => specificity(left.statement.entityType) - specificity(right.statement.entityType));
16630
+ const objects4 = (value) => isArray(value) ? value.filter(isObject) : [];
16631
+ const elements = priceStatement !== void 0 ? objects4(get(objects4(get(priceStatement.statement.priceStatus, "prices")).find((prices) => get(prices, "chargingTariffId") === tariffId2) ?? {}, "elements")) : objects4(get(tariff2, "elements"));
16632
+ return { tariff: tariff2, ...priceStatement === void 0 ? {} : { priceStatement }, elements };
16633
+ }
16128
16634
  function evTalerTariff(station, evseId2) {
16129
16635
  const tariffs = station.tariffs.get(evseId2) ?? [];
16130
16636
  const typeOf2 = (tariff3) => {
@@ -17118,10 +17624,33 @@ function verifyPricePublication(receipts, policy, subject) {
17118
17624
  });
17119
17625
  }
17120
17626
 
17627
+ // src/verifier/grossRates.ts
17628
+ function grossRates(currency2, elements) {
17629
+ if (elements.length !== 1) return void 0;
17630
+ const element = elements[0];
17631
+ if (element === void 0 || Object.keys(element).some((key2) => key2 !== "priceComponents" && key2 !== "restrictions")) return void 0;
17632
+ if (element["restrictions"] !== void 0 && (!isObject(element["restrictions"]) || Object.keys(element["restrictions"]).length > 0)) return void 0;
17633
+ const components = element["priceComponents"];
17634
+ if (!Array.isArray(components) || components.length === 0) return void 0;
17635
+ const rates = { currency: currency2 };
17636
+ for (const raw of components) {
17637
+ if (!isObject(raw) || Object.keys(raw).some((key2) => !["type", "price", "stepSize"].includes(key2))) return void 0;
17638
+ const type = raw["type"], value = raw["price"], step = raw["stepSize"];
17639
+ if (!isNumber(value) || Decimal.parse(asText(value)).mantissa < 0n) return void 0;
17640
+ if (type === "FLAT" ? "stepSize" in raw : typeof step !== "number" || !Number.isSafeInteger(step) || step < 1) return void 0;
17641
+ const field = type === "ENERGY" ? "energyPerKWh" : type === "FLAT" ? "flatFee" : type === "TIME" ? "timePerHour" : void 0;
17642
+ if (field === void 0 || rates[field] !== void 0) return void 0;
17643
+ rates[field] = asText(value);
17644
+ }
17645
+ return rates;
17646
+ }
17647
+
17121
17648
  // src/verifier/sessionPrices.ts
17122
17649
  var SESSION_PRICE_EVIDENCE_CONTEXT = "https://open.charging.cloud/contexts/wwcp/sessionPriceEvidence/v1";
17123
17650
  var SESSION_PRICE_AGREEMENT_CONTEXT = "https://open.charging.cloud/contexts/wwcp/sessionPriceAgreement/v1";
17124
17651
  var SIMPLE_GROSS_PRICE_PROFILE = "chargy-gross-rates/v1";
17652
+ var CONTRACT_GROSS_PRICE_PROFILE = "chargy-contract-gross-rates/v1";
17653
+ var GROSS_PRICE_PROFILES = /* @__PURE__ */ new Map([["adhoc", SIMPLE_GROSS_PRICE_PROFILE], ["contract", CONTRACT_GROSS_PRICE_PROFILE]]);
17125
17654
  var SessionPriceAgreement = class _SessionPriceAgreement {
17126
17655
  constructor(json, chargingStarted, signedAt) {
17127
17656
  this.json = json;
@@ -17172,24 +17701,6 @@ function parseSessionPriceEvidence(value) {
17172
17701
  })
17173
17702
  };
17174
17703
  }
17175
- function grossRates(currency2, elements) {
17176
- if (elements.length !== 1) return void 0;
17177
- const element = elements[0];
17178
- if (element === void 0 || Object.keys(element).some((key2) => key2 !== "priceComponents" && key2 !== "restrictions")) return void 0;
17179
- if (element["restrictions"] !== void 0 && (!isObject(element["restrictions"]) || Object.keys(element["restrictions"]).length > 0)) return void 0;
17180
- const components = element["priceComponents"];
17181
- if (!Array.isArray(components) || components.length === 0) return void 0;
17182
- const rates = { currency: currency2 };
17183
- for (const raw of components) {
17184
- if (!isObject(raw) || Object.keys(raw).some((key2) => !["type", "price", "stepSize"].includes(key2))) return void 0;
17185
- const type = raw["type"], value = raw["price"], step = raw["stepSize"];
17186
- if (!isNumber(value) || Decimal.parse(asText(value)).mantissa < 0n || typeof step !== "number" || !Number.isSafeInteger(step) || step < 1) return void 0;
17187
- const field = type === "ENERGY" ? "energyPerKWh" : type === "FLAT" ? "flatFee" : type === "TIME" ? "timePerHour" : void 0;
17188
- if (field === void 0 || rates[field] !== void 0) return void 0;
17189
- rates[field] = asText(value);
17190
- }
17191
- return rates;
17192
- }
17193
17704
  function verifySessionPriceEvidence(current, value, request, policy = {}) {
17194
17705
  if (!(current instanceof TrustContext) && !(current instanceof HistoricalTrustContext)) throw new POIError("Session prices require current local authorization");
17195
17706
  if (!Number.isFinite(request.chargingStarted) || request.chargingStarted > current.evaluatedAt || !isLowerHex(request.startDocumentSHA256, 32)) throw new POIError("Invalid original price context");
@@ -17240,7 +17751,7 @@ function verifySessionPriceEvidence(current, value, request, policy = {}) {
17240
17751
  if (selected === void 0) throw new POIError("Missing selected price");
17241
17752
  const currency2 = requireString(selected.tariff, "currency"), elements = requireArray(selected.price["elements"], "Signed price elements").map((item) => requireObject(item, "A signed price element")), rates = grossRates(currency2, elements);
17242
17753
  let applicability = agreement === void 0 ? "unbound" : "bound";
17243
- if (agreement !== void 0 && (agreement.json["chargingMode"] !== "adhoc" || agreement.json["priceBinding"] !== "fixed-at-start" || agreement.json["pricingProfile"] !== SIMPLE_GROSS_PRICE_PROFILE || rates === void 0)) {
17754
+ if (agreement !== void 0 && (agreement.json["pricingProfile"] !== GROSS_PRICE_PROFILES.get(asText(agreement.json["chargingMode"])) || agreement.json["priceBinding"] !== "fixed-at-start" || rates === void 0)) {
17244
17755
  applicability = "unsupported";
17245
17756
  diagnostics.push("EMP/contract terms, variable binding, unknown price profile or conditional elements are not established by this profile");
17246
17757
  }
@@ -17315,7 +17826,7 @@ function verifySessionPriceEvidence(current, value, request, policy = {}) {
17315
17826
  diagnostics,
17316
17827
  checkpointIds,
17317
17828
  witnessedBy: [...witnessed],
17318
- selected: { statement: selected.statement.json, digest: selected.digest, tariff: selected.tariff, currency: currency2, elements, ...rates === void 0 || agreement?.json["pricingProfile"] !== SIMPLE_GROSS_PRICE_PROFILE ? {} : { rates } }
17829
+ selected: { statement: selected.statement.json, digest: selected.digest, tariff: selected.tariff, currency: currency2, elements, ...rates === void 0 || ![...GROSS_PRICE_PROFILES.values()].includes(asText(agreement?.json["pricingProfile"])) ? {} : { rates } }
17319
17830
  };
17320
17831
  }
17321
17832
 
@@ -17761,6 +18272,6 @@ function applyVerifiedTrustTransition(before, verified, at) {
17761
18272
  }
17762
18273
  var initialTrustEpoch = () => ({ number: 1, profiles: EpochProfiles.FIRST });
17763
18274
 
17764
- export { ATTESTATION_CONTEXT, ArchiveSegment, Attestation, CHANGE_SET_CONTEXT, CHARGING_DOCUMENT_USAGE, CHECKPOINT_CONTEXT2 as CHECKPOINT_CONTEXT, CHILD_ARRAYS, COMMIT_CONTEXT2 as COMMIT_CONTEXT, CONTEXT_PREFIX, ChangeSet, ChargingStationProof, Checkpoint, CheckpointProof, Commit, DCCMeasurementResult, DCCResult, DCCStatement, DCC_PERFORMANCE_LOCATIONS, DCOC_MODULES, DDoCNotifiedBody, Decimal, DigitalCalibrationCertificate, DigitalCertificateOfConformity, DigitalDeclarationOfConformity, EPOCH_CONTEXT, EPOCH_LINK_CONTEXT, EPOCH_PROFILES_CONTEXT, EVTalerAmountChecks, EVTalerContractTerms, EVTalerCovers, EVTalerPayURI, EVTalerRates, EVTalerSettlement, EVTalerSignedMeterValue, EVTalerStep, EV_TALER_EXCHANGE_CONTEXT, EV_TALER_MERCHANT_CONTEXT, EV_TALER_PAYMENT_MEANS, EV_TALER_PAYMENT_OPTION, EV_TALER_SETTLEMENT_CONTEXT, EV_TALER_STEP_CONTEXT, EpochProfiles, FIRST_EPOCH, HISTORICAL_METER_EVIDENCE_CONTEXT, HistoricalTrustContext, History, KEY_STATEMENT_CONTEXT, KeyStatement, KeyUsages, LIVE_TOPICS, MAX_DECIMAL_SCALE, MAX_PRICE_STATEMENT_SEQUENCE, MERGE_CHANGE_SET_ID_PREFIX, MeasurementKeyError, NetDecimal, OperatorTrust, POIClient, POIError, POIPublicKey, PRICE_PUBLICATION_RECEIPT_CONTEXT, PROOF_CONTEXT, PricePublicationReceipt, PriceStatement, PriceStatementProof, QIDigitalDocument, QIDigitalItem, QIDigitalLegislation, QIDigitalManufacturer, QIDigitalModel, QIDigitalPerson, QIDigitalQuantity, QIDigitalSignature, QIDigitalStandard, QI_DIGITAL_CONFORMITIES, ROAMING_NETWORK_CONTEXT, RUNTIME_STATUS_KINDS, RoamingNetwork, RoamingNetworkOperator, RuntimeStatus, SESSION_PRICE_AGREEMENT_CONTEXT, SESSION_PRICE_EVIDENCE_CONTEXT, SIGNATURE_ALGORITHMS, SIGNING_KEY_CERTIFICATE_CONTEXT, SIMPLE_GROSS_PRICE_PROFILE, STATUS_UPDATE_CONTEXT, STYX_CANONICAL_JSON, SessionPriceAgreement, SignatureRoles, SigningKeyCertificate, TRUST_ANCHOR_CONTEXT, TRUST_ANCHOR_QR_PREFIX, TRUST_TRANSITION_CONTEXT, TrustAnchor, TrustContext, TrustTransition, VerifiedTrustTransition, WITNESS_PUBLICATION_POLICY_CONTEXT, applyChangeSet, applyVerifiedTrustTransition, approvesQualitySystem, auditPath, canonicalJSON, canonicalJSONBytes, canonicalString, cborETag, cborToJson, changeSetIdentification, check, checkOwnership, checkpointRootVersionObservation, checkpointVersionObservation, clone, commitsRoot, compareObservedVersions, decodeDeterministicCbor, describeVersion, deterministicCbor, effectiveChargingStationOperator, effectiveContent, effectiveTreeETag, emptyObservedVersions, entityDigest, etagToJSON, etagToText, evTalerExchangesAt, evTalerMerchantOf, evTalerTariff, evTalerTariffOf, fromBase45, fromBase64, fromHex, fromJavaScript, get, givesCompletely, hash, historicalOperatorContext, initialTrustEpoch, isArray, isEVTalerSessionId, isObject, isTalerPublicKey, jsonETag, jsonToCbor, keyMaySignIn, leafHash, liveRequest, merkleRoot, merkleRootOfLeafHashes, minorUnitDigits, nodeHash, observedVersionsChecksum, parseArchiveSegmentInfo, parseETag, parseETags, parseEpoch, parseEpochLink, parseEvidence, parseFrame, parseHistoricalMeterEvidence, parseJSON, parseJSONBytes, parseJSONObject, parseMeasurementPublicKey, parsePriceStatements, parseSessionPriceEvidence, parseStatusUpdate, parseTimestamp, parseTrustedWitnesses, parseWitnessPublicationPolicy, paymentOptionName, prettyJSON, pricePublicationOfferHashes, priceStatementChainOf, priceStatementsRoot, priceVersionObservation, prunedTree, publicPublicationURL, qiDigitalContext, rootFromAuditPath, roundToNearest, roundUp, sameBaseURL, sameETag, sameETags, sameEpoch, sameValue, sizeOf, skeleton, stationVersionObservation, subscribeRequest, talerAmountOf, toBase45, toBase64, toHex, toTimestamp, trustPolicyId, trustStatementDigest, verifyAuthorizedCheckpoint, verifyChargingDocumentKeys, verifyChargingStation, verifyCheckpointLine, verifyCheckpointPriceStatements, verifyCheckpointSignatures, verifyEvidence, verifyHistoricalMeterEvidence, verifyMeterKey, verifyMeterValue, verifyPricePublication, verifyPriceStatementChain, verifyPriceStatements, verifySegmentLine, verifySessionPriceEvidence, verifySignature, verifyVersion, writeAmount, writeDecimal };
18275
+ export { ATTESTATION_CONTEXT, ArchiveSegment, Attestation, CHANGE_SET_CONTEXT, CHARGING_DOCUMENT_USAGE, CHECKPOINT_CONTEXT2 as CHECKPOINT_CONTEXT, CHILD_ARRAYS, COMMIT_CONTEXT2 as COMMIT_CONTEXT, CONTEXT_PREFIX, CONTRACT_GROSS_PRICE_PROFILE, ChangeSet, ChargingStationProof, Checkpoint, CheckpointProof, Commit, DCCMeasurementResult, DCCResult, DCCStatement, DCC_PERFORMANCE_LOCATIONS, DCOC_MODULES, DDoCNotifiedBody, Decimal, DigitalCalibrationCertificate, DigitalCertificateOfConformity, DigitalDeclarationOfConformity, EPOCH_CONTEXT, EPOCH_LINK_CONTEXT, EPOCH_PROFILES_CONTEXT, EVTalerAmountChecks, EVTalerContractTerms, EVTalerCovers, EVTalerPayURI, EVTalerRates, EVTalerSettlement, EVTalerSignedMeterValue, EVTalerStep, EV_TALER_EXCHANGE_CONTEXT, EV_TALER_MERCHANT_CONTEXT, EV_TALER_PAYMENT_MEANS, EV_TALER_PAYMENT_OPTION, EV_TALER_SETTLEMENT_CONTEXT, EV_TALER_STEP_CONTEXT, EpochProfiles, FIRST_EPOCH, HISTORICAL_METER_EVIDENCE_CONTEXT, HistoricalTrustContext, History, KEY_STATEMENT_CONTEXT, KeyStatement, KeyUsages, LIVE_TOPICS, MAX_DECIMAL_SCALE, MAX_PRICE_STATEMENT_SEQUENCE, MERGE_CHANGE_SET_ID_PREFIX, MeasurementKeyError, NetDecimal, OperatorTrust, POIClient, POIError, POIPublicKey, PRICE_PUBLICATION_RECEIPT_CONTEXT, PROOF_CONTEXT, PricePublicationReceipt, PriceStatement, PriceStatementProof, QIDigitalDocument, QIDigitalItem, QIDigitalLegislation, QIDigitalManufacturer, QIDigitalModel, QIDigitalPerson, QIDigitalQuantity, QIDigitalSignature, QIDigitalStandard, QI_DIGITAL_CONFORMITIES, ROAMING_NETWORK_CONTEXT, RUNTIME_STATUS_KINDS, RoamingNetwork, RoamingNetworkOperator, RuntimeStatus, SESSION_PRICE_AGREEMENT_CONTEXT, SESSION_PRICE_EVIDENCE_CONTEXT, SIGNATURE_ALGORITHMS, SIGNING_KEY_CERTIFICATE_CONTEXT, SIMPLE_GROSS_PRICE_PROFILE, STATUS_UPDATE_CONTEXT, STYX_CANONICAL_JSON, SessionPriceAgreement, SignatureRoles, SigningKeyCertificate, TRUST_ANCHOR_CONTEXT, TRUST_ANCHOR_QR_PREFIX, TRUST_TRANSITION_CONTEXT, TrustAnchor, TrustContext, TrustTransition, VerifiedTrustTransition, WITNESS_PUBLICATION_POLICY_CONTEXT, applyChangeSet, applyVerifiedTrustTransition, approvesQualitySystem, auditPath, canonicalJSON, canonicalJSONBytes, canonicalString, cborETag, cborToJson, changeSetIdentification, check, checkOwnership, checkpointRootVersionObservation, checkpointVersionObservation, clone, commitsRoot, compareObservedVersions, decodeDeterministicCbor, describeVersion, deterministicCbor, effectiveChargingStationOperator, effectiveContent, effectiveTreeETag, emptyObservedVersions, entityDigest, etagToJSON, etagToText, evTalerExchangesAt, evTalerMerchantOf, evTalerSessionPrices, evTalerTariff, evTalerTariffOf, fromBase45, fromBase64, fromHex, fromJavaScript, get, givesCompletely, hash, historicalOperatorContext, initialTrustEpoch, isArray, isEVTalerSessionId, isObject, isTalerPublicKey, jsonETag, jsonToCbor, keyMaySignIn, leafHash, liveRequest, merkleRoot, merkleRootOfLeafHashes, minorUnitDigits, nodeHash, observedVersionsChecksum, parseArchiveSegmentInfo, parseETag, parseETags, parseEpoch, parseEpochLink, parseEvidence, parseFrame, parseHistoricalMeterEvidence, parseJSON, parseJSONBytes, parseJSONObject, parseMeasurementPublicKey, parsePriceStatements, parseSessionPriceEvidence, parseStatusUpdate, parseTimestamp, parseTrustedWitnesses, parseWitnessPublicationPolicy, paymentOptionName, prettyJSON, pricePublicationOfferHashes, priceStatementChainOf, priceStatementsRoot, priceVersionObservation, prunedTree, publicPublicationURL, qiDigitalContext, rootFromAuditPath, roundToNearest, roundUp, sameBaseURL, sameETag, sameETags, sameEpoch, sameValue, sizeOf, skeleton, stationVersionObservation, subscribeRequest, talerAmountOf, toBase45, toBase64, toHex, toTimestamp, trustPolicyId, trustStatementDigest, verifyAuthorizedCheckpoint, verifyChargingDocumentKeys, verifyChargingStation, verifyCheckpointLine, verifyCheckpointPriceStatements, verifyCheckpointSignatures, verifyEvidence, verifyHistoricalMeterEvidence, verifyMeterKey, verifyMeterValue, verifyPricePublication, verifyPriceStatementChain, verifyPriceStatements, verifySegmentLine, verifySessionPriceEvidence, verifySignature, verifyVersion, writeAmount, writeDecimal };
17765
18276
  //# sourceMappingURL=index.js.map
17766
18277
  //# sourceMappingURL=index.js.map