@peabody-soft/mbs-model-ts 1.5.0 → 1.6.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 (52) hide show
  1. package/dist/Address.js +8 -11
  2. package/dist/Attachment.js +7 -8
  3. package/dist/AttachmentBaseUrl.js +10 -10
  4. package/dist/AuthTokens.js +12 -7
  5. package/dist/Business.js +19 -19
  6. package/dist/BusinessIdentifier.js +2 -2
  7. package/dist/BusinessLink.js +16 -16
  8. package/dist/BusinessProfile.js +4 -4
  9. package/dist/CartEditor.js +137 -166
  10. package/dist/Catalog.js +19 -25
  11. package/dist/Constants.js +10 -10
  12. package/dist/ContainerNames.js +10 -11
  13. package/dist/Entity.js +11 -9
  14. package/dist/Enums.js +2 -2
  15. package/dist/Exceptions.js +2 -2
  16. package/dist/GeneralSettings.js +22 -21
  17. package/dist/HttpStatusCodes.js +10 -11
  18. package/dist/Ids.js +24 -30
  19. package/dist/Item.js +6 -7
  20. package/dist/LatLong.js +3 -3
  21. package/dist/LiveData.js +6 -6
  22. package/dist/Measures.js +2 -2
  23. package/dist/Message.js +6 -6
  24. package/dist/MockDatabase.js +2 -2
  25. package/dist/Model.js +292 -359
  26. package/dist/Order.js +20 -17
  27. package/dist/Package.js +7 -8
  28. package/dist/PackageIdentifier.js +2 -2
  29. package/dist/PaymentMethodDetail.js +4 -4
  30. package/dist/Photo.js +6 -7
  31. package/dist/Price.js +8 -9
  32. package/dist/PriceList.js +2 -2
  33. package/dist/Product.js +17 -22
  34. package/dist/ProductCategory.js +3 -4
  35. package/dist/Profile.js +26 -27
  36. package/dist/ProtoMap.d.ts +3 -0
  37. package/dist/ProtoMap.js +288 -294
  38. package/dist/Routes.js +3 -3
  39. package/dist/ServiceExceptions.js +11 -12
  40. package/dist/ServiceProxy.js +19 -23
  41. package/dist/ShoppingList.js +112 -130
  42. package/dist/SyncCache.d.ts +3 -0
  43. package/dist/SyncCache.js +92 -75
  44. package/dist/SyncKeys.js +8 -8
  45. package/dist/SyncState.js +5 -5
  46. package/dist/Tax.js +16 -21
  47. package/dist/TaxHelper.js +22 -30
  48. package/dist/TaxType.js +8 -9
  49. package/dist/UserProfile.js +4 -4
  50. package/dist/WorkingDays.js +15 -15
  51. package/dist/index.js +9 -9
  52. package/package.json +1 -1
package/dist/Catalog.js CHANGED
@@ -1,24 +1,22 @@
1
1
  import { Constants as proto_Constants } from "./generated/Constants";
2
2
  import { EntityHeader } from "./Entity";
3
3
  import ProductCategory from "./ProductCategory";
4
- // The catalog of a business: its categories, and every product in it. Not an entity and not on
5
- // the wire - the sync layer rebuilds this hierarchy from the flat item entities of a sync response.
4
+ // The catalog of a business: its categories and the products in them. Not an entity and not on the
5
+ // wire. The sync layer rebuilds it from the flat item entities of a sync response.
6
6
  class Catalog {
7
7
  constructor(categories, products, _priceList) {
8
8
  this._priceList = _priceList;
9
9
  this._categoryMap = new Map();
10
10
  this._productMap = new Map();
11
11
  this._packageMap = new Map();
12
- // A dummy category standing in for a product whose parentItemId is Constants.ENTITY_ID_NULL.
13
- // Its own id is that same ENTITY_ID_NULL, so getCategory(product.productCategoryId) resolves
14
- // it exactly like a real category - including being left out below, the same as any other
15
- // category, when no product ends up in it.
12
+ // A dummy category for products whose parentItemId is Constants.ENTITY_ID_NULL. Its id is also
13
+ // ENTITY_ID_NULL, so getCategory(product.productCategoryId) resolves it like a real category.
14
+ // It is left out below like any other category when it has no products.
16
15
  const uncategorized = new ProductCategory(new EntityHeader(BigInt(proto_Constants.ENTITY_ID_NULL), 0n, 0n, 0n, 0n), "", null, BigInt(proto_Constants.ENTITY_ID_NULL), false);
17
- // Only a buyable product - itself enabled, not one that the business sells only offline, with at
18
- // least one buyable package, so one a customer can actually order - is part of a catalog. Any other is left out of products, getProduct and
19
- // its categories, and so are its packages: they are not reachable by id either. This is
20
- // enforced here, and not left to whoever builds the catalog to have filtered, so that no way
21
- // of making one can expose a product that cannot be bought.
16
+ // Only a buyable product is part of a catalog: enabled, not sold only offline, with at least one
17
+ // buyable package. Any other is left out of products, getProduct and its categories, and its
18
+ // packages are unreachable by id. Enforced here so no builder can expose a product that cannot
19
+ // be bought.
22
20
  const buyableProducts = [];
23
21
  const productsByCategory = new Map();
24
22
  for (const product of products) {
@@ -35,10 +33,8 @@ class Catalog {
35
33
  this._packageMap.set(pkg.id, pkg);
36
34
  }
37
35
  this._products = Object.freeze(buyableProducts);
38
- // A category with nothing a customer can order, which is one that no product reached above, is
39
- // left out rather than shown empty - there is no point showing it to a customer. Nothing here
40
- // is special-cased for the dummy: it is left out exactly the same way when nothing buyable is
41
- // uncategorized.
36
+ // A category that no buyable product reached is left out rather than shown empty. This covers
37
+ // the dummy too.
42
38
  const nonEmptyCategories = [];
43
39
  for (const category of [...categories, uncategorized]) {
44
40
  const categoryProducts = productsByCategory.get(category.id);
@@ -55,14 +51,13 @@ class Catalog {
55
51
  get categories() {
56
52
  return this._categories;
57
53
  }
58
- // Every buyable product of the business - itself enabled, with at least one buyable package -
59
- // whether or not it is in a category, in no particular order. A product that is not is not here.
54
+ // Every buyable product of the business, in a category or not, in no particular order
60
55
  get products() {
61
56
  return this._products;
62
57
  }
63
- // The one price list a storefront sells from - MeraBills Online - and null until it arrives.
64
- // Every price in this catalog belongs to it; a price in any other list is not sold here and is
65
- // left out. A shopping list names this list, or falls back to its id when it has none set.
58
+ // The one price list a storefront sells from (MeraBills Online). Null until it arrives. Every
59
+ // price in this catalog belongs to it, and prices in other lists are left out. A shopping list
60
+ // names this list, or falls back to its id when it has none set.
66
61
  get priceList() {
67
62
  return this._priceList;
68
63
  }
@@ -79,14 +74,13 @@ class Catalog {
79
74
  getProduct(productId) {
80
75
  return this._productMap.get(productId) ?? null;
81
76
  }
82
- // Returns null for the four reserved package ids a shopping list uses for its charge and
83
- // summary lines (see Ids.isReservedPackageId), and for a package whose product was deleted or is
84
- // not buyable
77
+ // Null for the four reserved package ids a shopping list uses for its charge and summary lines
78
+ // (see Ids.isReservedPackageId), and for a package whose product was deleted or is not buyable
85
79
  getPackage(packageId) {
86
80
  return this._packageMap.get(packageId) ?? null;
87
81
  }
88
- // Display comparator for packages, matching the merchant app's PackageComparator (minus clauses
89
- // a storefront can never exercise: disabled packages and the purchase price list, neither sent)
82
+ // Display comparator for packages, matching the merchant app's PackageComparator minus the
83
+ // clauses a storefront cannot exercise (disabled packages and the purchase price list, neither sent)
90
84
  static comparePackages(left, right) {
91
85
  const byName = Catalog.compareNames(left.name, right.name);
92
86
  if (byName !== 0)
package/dist/Constants.js CHANGED
@@ -1,22 +1,22 @@
1
1
  import { Constants as proto_Constants } from "./generated/Constants";
2
2
  import Ids from "./Ids";
3
3
  // The protocol's limits and scaling factors, and the wire-to-UI value conversions.
4
- // Limits are re-exported rather than redeclared, so a Java constant has only one place to be wrong.
4
+ // Limits are re-exported so a Java constant has only one place to be wrong.
5
5
  export { Constants } from "./generated/Constants";
6
6
  // The id of a built-in price list, from the low part Constants carries for it:
7
7
  // DEFAULT_SALE_PRICE_LIST, DEFAULT_PURCHASE_PRICE_LIST, MERABILLS_ONLINE_PRICE_LIST,
8
8
  // AMAZON_SELLER_PRICE_LIST, FACEBOOK_MARKETPLACE_PRICE_LIST or WHATSAPP_BUSINESS_PRICE_LIST. These
9
- // are the six ids of Java PriceListIds, built the way it builds them: the low part is a constant
10
- // of the protocol, the id is not, 64 bits not fitting a protobuf enum value.
9
+ // are the six ids of Java PriceListIds, built the same way. Only the low part is a protocol
10
+ // constant, since a 64-bit id does not fit a protobuf enum value.
11
11
  export function buildPriceListId(priceList) {
12
12
  return Ids.buildId(proto_Constants.PRICE_LIST_ENTITY_TYPE_ID, BigInt(priceList));
13
13
  }
14
- // The only price list this library reads. A business prices the same package in several lists -
15
- // what it sells at over the counter, what a marketplace it also sells on charges - and only this
16
- // one is its storefront's. A price in any other list is passed over; see ProtoMap.buildBusiness.
14
+ // The only price list this library reads. A business prices a package in several lists (over the
15
+ // counter, on a marketplace), and only this one is the storefront's. Prices in other lists are
16
+ // skipped, see ProtoMap.buildBusiness.
17
17
  export const MERABILLS_ONLINE_PRICE_LIST_ID = buildPriceListId(proto_Constants.MERABILLS_ONLINE_PRICE_LIST);
18
- // The largest amount the protocol can carry (Java Transaction.MAX_AMOUNT); kept here since it doesn't fit
19
- // a protobuf enum value. Unscaled it's well below 2^53, so an amount is always safe as a number.
18
+ // The largest amount the protocol can carry (Java Transaction.MAX_AMOUNT). Kept here because it does
19
+ // not fit a protobuf enum value. Unscaled it is well below 2^53, so an amount is safe as a number.
20
20
  export const MAX_SCALED_AMOUNT = 17592186044415n;
21
21
  // An amount, price or discount travels multiplied by Constants.AMOUNT_FACTOR, bounded by
22
22
  // MAX_SCALED_AMOUNT, so it's safe as a number even though it arrives as an int64.
@@ -33,8 +33,8 @@ export function quantityFromScaledValue(scaledQuantity) {
33
33
  export function quantityToScaledValue(quantity) {
34
34
  return Math.round(quantity * proto_Constants.QUANTITY_FACTOR);
35
35
  }
36
- // A tax rate travels multiplied by Constants.TAX_RATE_FACTOR, so 18% arrives as 1800. What comes
37
- // back is a percentage and not a fraction: 18, not 0.18.
36
+ // A tax rate travels multiplied by Constants.TAX_RATE_FACTOR, so 18% arrives as 1800. The result is
37
+ // a percentage (18, not 0.18).
38
38
  export function taxRateFromScaledValue(scaledTaxRate) {
39
39
  return scaledTaxRate / (proto_Constants.TAX_RATE_FACTOR / 100);
40
40
  }
@@ -1,21 +1,20 @@
1
- // What each container of the local store is called: a business container prefixes Business.businessId,
2
- // a profile container prefixes the subject id. The prefix keeps the two id spaces from ever colliding.
1
+ // Names of the local store's containers. A business container prefixes Business.businessId, a
2
+ // profile container the subject id. The prefixes keep the two id spaces from colliding.
3
3
  // One business, holding that business and everything in its business database
4
4
  export function forBusiness(nameSuffix) {
5
5
  return `${VERSION_PREFIX}${BUSINESS_PREFIX}${nameSuffix}`;
6
6
  }
7
- // One signed in subject, holding their profile database and the tokens that sign their requests.
8
- // Several can coexist so signing back in after a sign-out is incremental rather than a fresh download.
7
+ // One signed in subject: their profile database and the tokens that sign their requests. Several
8
+ // can coexist, so signing back in after a sign-out is incremental.
9
9
  export function forProfile(nameSuffix) {
10
10
  return `${VERSION_PREFIX}${PROFILE_PREFIX}${nameSuffix}`;
11
11
  }
12
- // Every name carries the layout version of what is inside it, so that a change to how this library
13
- // stores a container does not have to be migrated: the new version simply reads and writes names
14
- // nothing has written before, and what the old one left behind is inert until it is deleted.
15
- // Bump this whenever a stored blob stops meaning what it used to - a caller cannot be relied on to
16
- // clear a store it did not know had changed, and a half-understood container is worse than no cache.
12
+ // Every name carries the layout version of its contents, so a change in how a container is stored
13
+ // needs no migration: the new version uses names nothing has written, and the old containers stay
14
+ // inert until deleted. Bump it whenever a stored blob changes meaning, since callers cannot be
15
+ // relied on to clear a store and a half-understood container is worse than no cache.
17
16
  const VERSION_PREFIX = "V1.";
18
- // Neither id space is this library's to constrain, so a subject id that happened to equal a business
19
- // id would otherwise put a profile and a catalog in one container
17
+ // Neither id space is this library's to constrain, so without a prefix a subject id equal to a
18
+ // business id would put a profile and a catalog in one container
20
19
  const BUSINESS_PREFIX = "b.";
21
20
  const PROFILE_PREFIX = "p.";
package/dist/Entity.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Constants as proto_Constants } from "./generated/Constants";
2
2
  import Ids from "./Ids";
3
- // Something the protocol syncs one at a time: an id, create/update times, an update number, and
4
- // the database it was last updated in. Rebuilt from the wire rather than edited in place, so the
5
- // only mutable field is parent, set once containment is rebuilt after a sync (nothing nests on the wire).
3
+ // Something the protocol syncs one at a time: an id, create and update times, an update number, and
4
+ // the database it was last updated in. Rebuilt from the wire, never edited in place. The only
5
+ // mutable field is parent, set once containment is rebuilt after a sync (nothing nests on the wire).
6
6
  class Entity {
7
7
  constructor(header) {
8
8
  this._parent = null;
@@ -17,12 +17,13 @@ class Entity {
17
17
  get updatedAt() {
18
18
  return new Date(Number(this._header.updatedAt));
19
19
  }
20
- // Monotonically increasing, and only comparable within the same entity type and database. Basis of a sync cursor.
20
+ // Increases monotonically, comparable only within one entity type and database. Basis of a sync cursor.
21
21
  get updateNumber() {
22
22
  return this._header.updateNumber;
23
23
  }
24
- // The database this entity was last updated in (Constants.MASTER_DB_ID = the server); sync out uses it
25
- // to avoid echoing a store's own changes back. Business entities only - always ENTITY_ID_NULL on profile entities.
24
+ // The database this entity was last updated in (Constants.MASTER_DB_ID is the server). Sync out
25
+ // uses it to avoid echoing a store's own changes back. Business entities only; always
26
+ // ENTITY_ID_NULL on profile entities.
26
27
  get updateInfo() {
27
28
  return this._header.updateInfo;
28
29
  }
@@ -34,7 +35,8 @@ class Entity {
34
35
  get updateType() {
35
36
  return Number(this._header.updateNumber & Entity.UPDATE_TYPE_MASK);
36
37
  }
37
- // A deleted entity is a tombstone: only the header fields are meaningful. A UI never reaches one.
38
+ // A deleted entity is a tombstone: it carries what the entity was when deleted. A UI never reaches
39
+ // one, except an order whose message is deleted, which is an order the business has completed.
38
40
  get isDeleted() {
39
41
  return this.updateType === proto_Constants.UPDATE_TYPE_DELETED;
40
42
  }
@@ -49,8 +51,8 @@ class Entity {
49
51
  }
50
52
  Entity.UPDATE_TYPE_MASK = BigInt(proto_Constants.UPDATE_TYPE_MASK);
51
53
  export default Entity;
52
- // The fields of the Entity message every entity carries, passed as one value since they're all
53
- // bigint and would otherwise transpose silently at a call site (the protocol has moved one before).
54
+ // The fields of the Entity message every entity carries. Passed as one value because they are all
55
+ // bigint and could be transposed silently at a call site.
54
56
  export class EntityHeader {
55
57
  constructor(id, createdAt, updatedAt, updateNumber, updateInfo) {
56
58
  this.id = id;
package/dist/Enums.js CHANGED
@@ -1,5 +1,5 @@
1
- // Every enum of the protocol, re-exported under its own name so their Java-defined numeric ids
2
- // have only one place to be wrong, and this file records which enums are part of the library's surface.
1
+ // Every enum of the protocol, re-exported so their Java-defined numeric ids have only one place to
2
+ // be wrong. The list also records which enums are part of the library's surface.
3
3
  // TaxType is the exception: the protocol does not carry it, so it is defined in TaxType.ts.
4
4
  export { AddressType } from "./generated/AddressType";
5
5
  export { AttachmentType } from "./generated/AttachmentType";
@@ -1,5 +1,5 @@
1
- // The two failures this library raises; both are programming errors, not conditions a UI recovers
2
- // from (a sync failure surfaces as a rejected promise instead).
1
+ // The two failures this library raises. Both are programming errors that a UI does not recover
2
+ // from. A sync failure surfaces as a rejected promise.
3
3
  // A caller passed something this library cannot work with
4
4
  export class InvalidArgumentException extends Error {
5
5
  }
@@ -3,10 +3,10 @@ import { amountFromScaledValue } from "./Constants";
3
3
  import WorkingDays from "./WorkingDays";
4
4
  // The publicly visible settings of a business: tagline, contact info, location, fulfillment,
5
5
  // payment and delivery options, and how its online store is presented.
6
- // Not an entity - it's the value unwrapped from the one public Configuration a storefront is sent; merchant-only fields are excluded.
6
+ // Not an entity: it is unwrapped from the one public Configuration a storefront is sent, so
7
+ // merchant-only fields are absent.
7
8
  class GeneralSettings {
8
- // Only ProtoMap calls this: it is the one place that sees amounts still multiplied by
9
- // Constants.AMOUNT_FACTOR.
9
+ // For ProtoMap, which sees amounts still multiplied by Constants.AMOUNT_FACTOR.
10
10
  static fromScaledValues(paymentMethodDetails, customerServicePhone, isWhatsAppSupported, isSmsSupported, businessAddresses, businessIdentifiers, fulfillmentModes, scaledDeliveryCharge, deliveryChargeTaxes, scaledFreeDeliveryTotal, deliveryRadiusKilometres, storeTagline, workingDays, primaryStoreLocation, googleBusinessCategoryId, googleBusinessCategoryName, onlineStoreThemeId, onlineShoppingEnabled) {
11
11
  return new GeneralSettings(paymentMethodDetails, customerServicePhone, isWhatsAppSupported, isSmsSupported, businessAddresses, businessIdentifiers, fulfillmentModes, amountFromScaledValue(scaledDeliveryCharge), deliveryChargeTaxes, amountFromScaledValue(scaledFreeDeliveryTotal), deliveryRadiusKilometres, storeTagline, workingDays, primaryStoreLocation, googleBusinessCategoryId, googleBusinessCategoryName, onlineStoreThemeId, onlineShoppingEnabled);
12
12
  }
@@ -53,18 +53,18 @@ class GeneralSettings {
53
53
  get fulfillmentModes() {
54
54
  return this._fulfillmentModes;
55
55
  }
56
- // What delivery costs, INCLUDING the taxes in deliveryChargeTaxes, as the price of a package
57
- // includes the taxes of its product. CartEditor takes them out when it puts the charge in a list.
56
+ // What delivery costs, including the taxes in deliveryChargeTaxes, as a package price includes
57
+ // its product's taxes. CartEditor takes them out when it puts the charge in a list.
58
58
  get deliveryCharge() {
59
59
  return this._deliveryCharge;
60
60
  }
61
- // The taxes included in the delivery charge, which are the business's own and not a product's -
62
- // at most Constants.MAX_TAXES of them. Empty means the charge includes no tax.
61
+ // The taxes included in the delivery charge. They are the business's own, at most
62
+ // Constants.MAX_TAXES. Empty means the charge includes no tax.
63
63
  get deliveryChargeTaxes() {
64
64
  return this._deliveryChargeTaxes;
65
65
  }
66
- // Order total at or above which delivery is free, measured on the goods with their taxes in
67
- // them and without the delivery charge. Zero means never free, not always free.
66
+ // Order total at or above which delivery is free. Measured on the goods including taxes, without
67
+ // the delivery charge. Zero means delivery is never free.
68
68
  get freeDeliveryTotal() {
69
69
  return this._freeDeliveryTotal;
70
70
  }
@@ -75,36 +75,37 @@ class GeneralSettings {
75
75
  get storeTagline() {
76
76
  return this._storeTagline;
77
77
  }
78
- // Absent working days default to WorkingDays.ALL_DAYS (Java's default), never left null here.
78
+ // Absent working days default to WorkingDays.ALL_DAYS (Java's default), so this is never null.
79
79
  get workingDays() {
80
80
  return this._workingDays;
81
81
  }
82
- // Where this business is: the coordinates of the first of businessAddresses. Null when the
83
- // business has not published them.
82
+ // Where this business is: the coordinates of the first of businessAddresses. Null when not
83
+ // published.
84
84
  get primaryStoreLocation() {
85
85
  return this._primaryStoreLocation;
86
86
  }
87
- // What kind of business this is, as its Google Business Profile category - "Bakery", say. The
88
- // name is the one to show; the id is the stable one to match on.
87
+ // What kind of business this is, as its Google Business Profile category (for example "Bakery").
88
+ // Show the name, match on the stable id.
89
89
  get googleBusinessCategoryId() {
90
90
  return this._googleBusinessCategoryId;
91
91
  }
92
92
  get googleBusinessCategoryName() {
93
93
  return this._googleBusinessCategoryName;
94
94
  }
95
- // Which of a storefront's own themes this business picked. The themes are the storefront's, so
96
- // what an id means is its business and not this library's; null when none was picked.
95
+ // Which of the storefront's own themes this business picked. What an id means is up to the
96
+ // storefront, not this library. Null when none was picked.
97
97
  get onlineStoreThemeId() {
98
98
  return this._onlineStoreThemeId;
99
99
  }
100
- // Whether the business has turned online shopping on. The server does not refuse a business that has not: Model does,
101
- // with a NotFoundException, once it has read everything it is sent and finds that the settings it then holds do not
102
- // say so, or that it holds none - see Model.setBusinessId. Also false of a business that has made no settings, which
103
- // is what GeneralSettings.EMPTY is.
100
+ // Whether the business has turned online shopping on. The server does not refuse a business that
101
+ // has not. Model does, with a NotFoundException, after reading everything it is sent, when the
102
+ // settings it holds do not say so or it holds none (see Model.setBusinessId). Also false for
103
+ // GeneralSettings.EMPTY.
104
104
  get onlineShoppingEnabled() {
105
105
  return this._onlineShoppingEnabled;
106
106
  }
107
107
  }
108
- // A business with no published public settings, used instead of null so every field is already the protocol's empty value.
108
+ // A business with no published public settings. Used instead of null so every field is already
109
+ // the protocol's empty value.
109
110
  GeneralSettings.EMPTY = new GeneralSettings(Object.freeze([]), null, false, false, AddressList.EMPTY, Object.freeze([]), Object.freeze([]), 0, Object.freeze([]), 0, 0, null, WorkingDays.ALL_DAYS, null, null, null, null, false);
110
111
  export default GeneralSettings;
@@ -1,16 +1,15 @@
1
1
  import { BadRequestException, ConflictException, InternalServerException, MethodNotAllowedException, NotFoundException, ServiceException, ServiceUnavailableException, UnauthorizedException, UpgradeRequiredException, } from "./ServiceExceptions";
2
- // Which failure a status code stands for, and nothing else - the classes themselves are in
3
- // ServiceExceptions.ts. Split the same way Java splits HttpStatusCodes from its exception package,
4
- // so that the one table saying "401 means this" has a single home in each client.
5
- // Everything from 300 up, this being Java's HttpURLConnection.HTTP_MULT_CHOICE. A redirect counts as
6
- // a failure because this client asks for one route and follows nothing, so being sent elsewhere
7
- // means the call did not happen.
2
+ // Which failure a status code stands for. The classes are in ServiceExceptions.ts. The split matches
3
+ // Java's HttpStatusCodes and exception package, so the table of what each code means has one home
4
+ // in each client.
5
+ // Everything from 300 up (Java's HttpURLConnection.HTTP_MULT_CHOICE). A redirect counts as a
6
+ // failure because this client follows none, so being sent elsewhere means the call did not happen.
8
7
  export function isError(httpStatusCode) {
9
8
  return httpStatusCode >= LOWEST_ERROR_STATUS_CODE;
10
9
  }
11
- // Raises the failure a status code stands for, and does nothing at all for one that is not an error.
12
- // A code with no class of its own is still a ServiceException carrying the code, so a server that
13
- // starts answering something new cannot slip past as a success.
10
+ // Raises the failure a status code stands for, and does nothing for a code that is not an error.
11
+ // A code with no class of its own becomes a ServiceException carrying the code, so a new server
12
+ // response cannot pass as a success.
14
13
  export function throwOnError(httpStatusCode, message) {
15
14
  if (!isError(httpStatusCode))
16
15
  return;
@@ -20,8 +19,8 @@ export function throwOnError(httpStatusCode, message) {
20
19
  throw create(message);
21
20
  }
22
21
  const LOWEST_ERROR_STATUS_CODE = 300;
23
- // Java builds this by reflection over a map of classes; here each entry constructs its own, because
24
- // a constructor reached through a type is what keeps this checked at compile time.
22
+ // Java builds this by reflection over a map of classes. Here each entry constructs its own class,
23
+ // which keeps it checked at compile time.
25
24
  const EXCEPTIONS = new Map([
26
25
  [BadRequestException.STATUS_CODE, (message) => new BadRequestException(message)],
27
26
  [UnauthorizedException.STATUS_CODE, (message) => new UnauthorizedException(message)],
package/dist/Ids.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Constants as proto_Constants } from "./generated/Constants";
2
2
  import { ReservedPackage as proto_ReservedPackage } from "./generated/ReservedPackage";
3
- // Everything this library knows about entity id layout. Ids are built/read only here, since a 64-bit shift written in number arithmetic silently loses the entity type.
4
- // Some ids below are not entity ids but local-store blob ids; they're negative, which can never collide with a real id (whose sign bit is always clear).
3
+ // All knowledge of entity id layout lives here, since a 64-bit shift in number arithmetic silently loses the entity type.
4
+ // Some ids below are negative local-store blob ids, which cannot collide with a real id (its sign bit is clear).
5
5
  class Ids {
6
6
  // The most significant byte of an id: one of the *_ENTITY_TYPE_ID values of Constants.
7
7
  static getEntityTypeId(id) {
@@ -14,20 +14,16 @@ class Ids {
14
14
  static buildId(entityTypeId, lowPart) {
15
15
  return (BigInt(entityTypeId) << Ids.ENTITY_TYPE_BIT_SHIFT) | (lowPart & Ids.ID_MASK);
16
16
  }
17
- // Where the cart of one membership lives: the business link id with the entity type swapped for
18
- // the message one, which is Java Message.getPartyPreferencesMessageId. Derived rather than
19
- // assigned so that a client always knows the id of its own cart without having been told it - a
20
- // sync in answers with an update number and no id, so a cart the server named could not be stored
21
- // here at all. One link therefore has one cart, and creating it twice is impossible by
22
- // construction rather than by a check.
17
+ // The cart's message id: the business link id with the entity type swapped for the message one
18
+ // (Java Message.getPartyPreferencesMessageId). Derived because a sync in answers with an update
19
+ // number and no id, so a cart the server named could not be stored. One link has one cart.
23
20
  static getPartyPreferencesMessageId(businessLinkId) {
24
21
  return Ids.buildId(proto_Constants.MESSAGE_ENTITY_TYPE_ID, Ids.getLowPart(businessLinkId));
25
22
  }
26
- // A fresh id for an entity this client is creating, to the rules of Java
27
- // Entity.getRandomLongForId: random, and never in 0-255, which every entity type reserves. Only
28
- // the low part is drawn, since buildId masks the rest off anyway, and it is drawn from the
29
- // cryptographic generator rather than Math.random - an id is guessable otherwise, and a message id
30
- // is a thing a caller could be handed.
23
+ // A fresh id for an entity this client creates (Java Entity.getRandomLongForId): random, never
24
+ // in 0-255, which every entity type reserves. Only the low part is drawn, as buildId masks the
25
+ // rest off. It uses the cryptographic generator because a Math.random id is guessable and a
26
+ // message id can be handed to a caller.
31
27
  static getNewId(entityTypeId) {
32
28
  for (;;) {
33
29
  const bytes = new Uint8Array(8);
@@ -50,22 +46,20 @@ class Ids {
50
46
  static toStorageHexString(id) {
51
47
  return (id & Ids.UNSIGNED_64_BIT_MASK).toString(16).toUpperCase().padStart(16, "0");
52
48
  }
53
- // The number an order is shown under: the least significant 32 bits of the id of the message
54
- // carrying it, as upper-case hex, not zero-padded. That is Java MessageBuilder.formatId, and it
55
- // has to match character for character - the merchant app shows the same order under the same
56
- // number, and a customer quoting one over the phone is the whole point of having it. An order
57
- // number is therefore derived and not a field of the protocol.
58
- // Java narrows with a cast to signed int and formats with %X, which prints the unsigned 32-bit
59
- // twos complement; masking here answers the same string without the sign getting in the way.
60
- // The merchant app prefixes the word "Order"; whether to do that is a UI's decision, not this one.
49
+ // The number an order is shown under: the low 32 bits of the id of the message carrying it, as
50
+ // upper-case hex without padding. Must match Java MessageBuilder.formatId character for
51
+ // character, since the merchant app shows the same number and a customer may quote it by phone.
52
+ // Derived, not a protocol field. Java casts to signed int and formats with %X, which prints the
53
+ // unsigned 32-bit two's complement, so masking gives the same string.
54
+ // The "Order" prefix the merchant app adds is a UI decision.
61
55
  static toOrderNumberString(orderId) {
62
56
  return (orderId & Ids.UNSIGNED_32_BIT_MASK).toString(16).toUpperCase();
63
57
  }
64
- // Where a container keeps how far its reads of one entity type have got: the cursor the next read
65
- // sends for it, which is the eTag of the blob and the whole of it. The store keeps the greater of two
66
- // eTags, so a cursor is never lowered, by a page that is stale or sent again or by another tab
67
- // writing at the same time. Written in the batch of the page that moved it, so that it cannot get
68
- // ahead of the entities it describes. key is one of SyncKeys: the table an entity type counts under.
58
+ // Where a container keeps how far its reads of one entity type have got. The blob's eTag is the
59
+ // cursor the next read sends, and the blob holds nothing else. The store keeps the greater eTag,
60
+ // so a cursor never drops on a stale or repeated page or a write from another tab. Written in
61
+ // the batch of the page that moved it, so it cannot get ahead of the entities it describes. key
62
+ // is one of SyncKeys.
69
63
  static getSyncCursorBlobId(key) {
70
64
  return Ids.SYNC_CURSOR_BLOB_ID_BASE - BigInt(key);
71
65
  }
@@ -75,13 +69,13 @@ Ids.ENTITY_TYPE_BIT_SHIFT = BigInt(proto_Constants.ENTITY_TYPE_BIT_SHIFT);
75
69
  Ids.ID_MASK = (1n << BigInt(proto_Constants.ENTITY_TYPE_BIT_SHIFT)) - 1n;
76
70
  // The configuration holding a business's public settings - the only one sent to a storefront (Java Configuration.MIN_PUBLIC_CONFIGURATION_ID).
77
71
  Ids.GENERAL_SETTINGS_CONFIGURATION_ID = 0x0d10000000000000n;
78
- // A price list id is built by Constants.buildPriceListId rather than named here: which built-in
79
- // lists exist is a fact of the protocol, and only their low parts are constants of it.
72
+ // A price list id is built by Constants.buildPriceListId, not named here. Only the low parts of
73
+ // the built-in lists are protocol constants.
80
74
  // Where a business container keeps that business's attachment base url; its eTag is that url's expiry.
81
75
  Ids.ATTACHMENT_BASE_URL_BLOB_ID = -1n;
82
- // Where a container keeps the credentials that sign its calls - see AuthTokens. Written with a null eTag, since a reissued token is not a later version of an earlier one: the latest simply wins.
76
+ // Where a container keeps the credentials that sign its calls (see AuthTokens). Written with a null eTag, since a reissued token is not a later version of the earlier one. The latest wins.
83
77
  Ids.AUTH_TOKENS_BLOB_ID = -2n;
84
- // The first of a block of fixed blob ids, one per key - see getSyncCursorBlobId. A key is an entity type id, which is below 0x80, so the block runs from -0x100 down to -0x17F and cannot meet the other fixed blobs at -1 and -2.
78
+ // The first of a block of fixed blob ids, one per key (see getSyncCursorBlobId). A key is an entity type id, below 0x80, so the block runs from -0x100 down to -0x17F and cannot meet the fixed blobs at -1 and -2.
85
79
  Ids.SYNC_CURSOR_BLOB_ID_BASE = -0x100n;
86
80
  Ids.UNSIGNED_64_BIT_MASK = (1n << 64n) - 1n;
87
81
  Ids.UNSIGNED_32_BIT_MASK = (1n << 32n) - 1n;
package/dist/Item.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import Entity from "./Entity";
2
- // Something a business buys or sells, or a category or a price thereof - mirrors the protocol's Item, embedded in each of the five real types.
3
- // parentItemId is the wire parent (e.g. a product's category), distinct from Entity.parent, the sync layer's containment parent (e.g. the business).
2
+ // Something a business buys or sells, or a category or price of one. Mirrors the protocol's Item, embedded in each of the five real types.
3
+ // parentItemId is the wire parent (a product's category). Entity.parent is the sync layer's containment parent (the business).
4
4
  export default class Item extends Entity {
5
- // Public rather than protected since ProductCategory/PriceList add nothing of their own; the class stays abstract, so still uninstantiable directly.
5
+ // Public because ProductCategory and PriceList add nothing of their own. The class is still abstract.
6
6
  constructor(header, _name, _description, _parentItemId, _isDisabled) {
7
7
  super(header);
8
8
  this._name = _name;
@@ -20,10 +20,9 @@ export default class Item extends Entity {
20
20
  get parentItemId() {
21
21
  return this._parentItemId;
22
22
  }
23
- // True when the business has disabled this item. Unlike isPublic and Product.isDraft, this can
24
- // change after an item exists, so it is sent rather than filtered out once and for all - a
25
- // product-level filter (Catalog.getCustomerVisibleProducts) does not, by itself, guarantee that
26
- // every package of an otherwise visible product is itself enabled.
23
+ // True when the business has disabled this item. Unlike isPublic and Product.isDraft, it can
24
+ // change after an item exists, so it is sent rather than filtered out. A product-level check does
25
+ // not guarantee that every package of an otherwise visible product is enabled.
27
26
  get isDisabled() {
28
27
  return this._isDisabled;
29
28
  }
package/dist/LatLong.js CHANGED
@@ -1,6 +1,6 @@
1
- // A point on Earth. Not an entity - a value on GeneralSettings, where it is the business's own
2
- // location. Degrees, and nothing here converts or formats them: whether that becomes a map pin, a
3
- // distance or a link is a UI's decision.
1
+ // A point on Earth, in degrees. Not an entity: a value on GeneralSettings, where it is the
2
+ // business's own location. Converting or formatting it (a map pin, a distance, a link) is a UI
3
+ // decision.
4
4
  export default class LatLong {
5
5
  constructor(_latitude, _longitude) {
6
6
  this._latitude = _latitude;
package/dist/LiveData.js CHANGED
@@ -1,6 +1,6 @@
1
- // A value that notifies its observers whenever it is set - the Android LiveData of this library, what every storefront UI binds to.
2
- // Deliberately skips LifecycleOwner and onActive/onInactive (no web equivalent / no need), and differs from mbl-model's Observations:
3
- // observe() returns its own unobserve function, holder gives a stable snapshot for React, observers are a Map for O(1) removal, and a value set mid-notification restarts rather than interleaves.
1
+ // A value that notifies its observers whenever it is set. This library's version of Android LiveData, which every storefront UI binds to.
2
+ // Skips LifecycleOwner and onActive/onInactive (no web equivalent, no need). Differs from mbl-model's Observations:
3
+ // observe() returns its own unobserve function, holder gives a stable snapshot for React, observers are a Map for O(1) removal, and a value set mid-notification restarts the notification instead of interleaving.
4
4
  class LiveData {
5
5
  constructor(value) {
6
6
  this._observers = new Map();
@@ -12,14 +12,14 @@ class LiveData {
12
12
  get value() {
13
13
  return this._holder.value;
14
14
  }
15
- // Private - only the sync layer (via an UpdatableLiveData cast) can set the value; a UI can only read/observe.
15
+ // Private. Only the sync layer (through an UpdatableLiveData cast) can set the value; a UI can only read and observe.
16
16
  set value(value) {
17
- // Always replaces the holder and notifies, even with the same reference - the interior may have changed and this class can't tell.
17
+ // Always replaces the holder and notifies, even for the same reference, since the interior may have changed and this class cannot tell.
18
18
  ++this._version;
19
19
  this._holder = new StateHolder(value);
20
20
  this.notifyObservers();
21
21
  }
22
- // The current value, wrapped so its identity changes exactly once per notification; use this instead of value for reference-equality snapshot comparisons.
22
+ // The current value, wrapped so its identity changes once per notification. Use this instead of value for reference-equality snapshot comparisons.
23
23
  get holder() {
24
24
  return this._holder;
25
25
  }
package/dist/Measures.js CHANGED
@@ -1,5 +1,5 @@
1
- // How big a package is, and how much it weighs - grouped together since a UI showing one usually shows both. Neither is an entity; both are values on the Package row.
2
- // Units are the generated proto enums, so a value displays in whatever unit the merchant entered it in; nothing here converts between units.
1
+ // How big a package is and how much it weighs, grouped since a UI showing one usually shows both. Neither is an entity; both are values on the Package row.
2
+ // Units are the generated proto enums. A value displays in the unit the merchant entered it in, and nothing here converts between units.
3
3
  export class Dimensions {
4
4
  constructor(_unit, _length, _width, _height) {
5
5
  this._unit = _unit;
package/dist/Message.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import Entity from "./Entity";
2
- // Something a customer sent to a business: an order (sales order) or a shopping cart (party preferences) - the two payloads this library understands.
3
- // A business entity despite being customer-authored, since Java authorizes the message table via Right.MESSAGES_MODIFY against the business.
2
+ // Something a customer sent to a business: an order (sales order) or a shopping cart (party preferences), the two payloads this library understands.
3
+ // A business entity despite being customer-authored, since Java authorizes the message table through Right.MESSAGES_MODIFY against the business.
4
4
  export default class Message extends Entity {
5
5
  constructor(header, _senderId, _messageType, _taskStatus, _senderName, _senderPhone) {
6
6
  super(header);
@@ -10,16 +10,16 @@ export default class Message extends Entity {
10
10
  this._senderName = _senderName;
11
11
  this._senderPhone = _senderPhone;
12
12
  }
13
- // The customer's party id at the business (BusinessLink.partyId), and not the id of their User. A business sync out
14
- // sends a customer only the messages whose sender is their own party. senderName/senderPhone below say who it was
15
- // without resolving the party.
13
+ // The customer's party id at the business (BusinessLink.partyId), not the id of their User. A
14
+ // business sync out sends a customer only messages from their own party. senderName and
15
+ // senderPhone say who it was without resolving the party.
16
16
  get senderId() {
17
17
  return this._senderId;
18
18
  }
19
19
  get messageType() {
20
20
  return this._messageType;
21
21
  }
22
- // How far the business has got with this message. Named after its enum, not the wire field "status", to avoid clashing with Order.status.
22
+ // How far the business has got with this message. Named after its enum rather than the wire field "status", which would clash with Order.status.
23
23
  get taskStatus() {
24
24
  return this._taskStatus;
25
25
  }
@@ -1,5 +1,5 @@
1
1
  // A Database that holds everything in a Map, for tests and training mode to run against with no
2
- // network. Enforces the eTag rule for real (write only if the new eTag is greater), so a test proves the sync is actually idempotent.
2
+ // network. Enforces the eTag rule (write only if the new eTag is greater), so a test proves the sync is idempotent.
3
3
  export default class MockDatabase {
4
4
  constructor() {
5
5
  this._containers = new Map();
@@ -51,7 +51,7 @@ export class MockContainer {
51
51
  this._blobs.set(blob.id, blob);
52
52
  ++appliedCount;
53
53
  }
54
- // Unconditional, and after the writes, exactly as the Database contract says
54
+ // After the writes, as the Database contract says
55
55
  for (const id of deletes)
56
56
  this._blobs.delete(id);
57
57
  return appliedCount;