@peabody-soft/mbs-model-ts 1.4.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 (91) 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.d.ts +10 -5
  10. package/dist/CartEditor.js +258 -180
  11. package/dist/Catalog.js +31 -29
  12. package/dist/Constants.js +10 -10
  13. package/dist/ContainerNames.js +10 -11
  14. package/dist/Entity.js +11 -9
  15. package/dist/Enums.d.ts +1 -1
  16. package/dist/Enums.js +4 -3
  17. package/dist/Exceptions.js +2 -2
  18. package/dist/GeneralSettings.d.ts +4 -2
  19. package/dist/GeneralSettings.js +30 -18
  20. package/dist/HttpStatusCodes.js +10 -11
  21. package/dist/Ids.d.ts +2 -0
  22. package/dist/Ids.js +28 -24
  23. package/dist/Item.js +6 -7
  24. package/dist/LatLong.js +3 -3
  25. package/dist/LiveData.js +6 -6
  26. package/dist/Measures.js +2 -2
  27. package/dist/Message.js +6 -5
  28. package/dist/MockDatabase.js +2 -2
  29. package/dist/Model.d.ts +5 -0
  30. package/dist/Model.js +400 -254
  31. package/dist/Order.js +20 -17
  32. package/dist/Package.js +7 -8
  33. package/dist/PackageIdentifier.js +2 -2
  34. package/dist/PaymentMethodDetail.js +4 -4
  35. package/dist/Photo.js +6 -7
  36. package/dist/Price.js +9 -6
  37. package/dist/PriceList.js +2 -2
  38. package/dist/Product.d.ts +5 -3
  39. package/dist/Product.js +26 -19
  40. package/dist/ProductCategory.js +3 -4
  41. package/dist/Profile.d.ts +1 -0
  42. package/dist/Profile.js +94 -8
  43. package/dist/ProtoMap.d.ts +18 -8
  44. package/dist/ProtoMap.js +451 -331
  45. package/dist/Routes.js +3 -3
  46. package/dist/ServiceExceptions.js +11 -12
  47. package/dist/ServiceProxy.js +19 -23
  48. package/dist/ShoppingList.d.ts +30 -1
  49. package/dist/ShoppingList.js +339 -47
  50. package/dist/SyncCache.d.ts +12 -0
  51. package/dist/SyncCache.js +136 -56
  52. package/dist/SyncKeys.js +8 -8
  53. package/dist/SyncState.js +5 -5
  54. package/dist/Tax.d.ts +12 -4
  55. package/dist/Tax.js +37 -13
  56. package/dist/TaxHelper.d.ts +12 -0
  57. package/dist/TaxHelper.js +85 -0
  58. package/dist/TaxType.d.ts +7 -0
  59. package/dist/TaxType.js +31 -0
  60. package/dist/UserProfile.js +4 -4
  61. package/dist/WorkingDays.js +15 -15
  62. package/dist/generated/AttachmentType.d.ts +19 -1
  63. package/dist/generated/AttachmentType.js +18 -0
  64. package/dist/generated/AuthTokens.d.ts +3 -1
  65. package/dist/generated/BusinessLink.d.ts +4 -1
  66. package/dist/generated/BusinessSyncInRequest.d.ts +11 -3
  67. package/dist/generated/BusinessSyncInResponse.d.ts +5 -4
  68. package/dist/generated/BusinessSyncOutRequest.d.ts +37 -10
  69. package/dist/generated/BusinessSyncOutRequest.js +42 -29
  70. package/dist/generated/BusinessSyncOutResponse.d.ts +14 -1
  71. package/dist/generated/Configuration.d.ts +2 -1
  72. package/dist/generated/CreateUserRequest.d.ts +8 -2
  73. package/dist/generated/Entity.d.ts +7 -3
  74. package/dist/generated/GeneralSettings.d.ts +14 -4
  75. package/dist/generated/GeneralSettings.js +9 -1
  76. package/dist/generated/Message.d.ts +4 -2
  77. package/dist/generated/Product.d.ts +10 -1
  78. package/dist/generated/Product.js +9 -1
  79. package/dist/generated/ProfileSyncOutRequest.d.ts +17 -1
  80. package/dist/generated/ProfileSyncOutResponse.d.ts +8 -3
  81. package/dist/generated/SalesOrderPayload.d.ts +7 -2
  82. package/dist/generated/ShoppingList.d.ts +6 -3
  83. package/dist/generated/ShoppingListItem.d.ts +21 -6
  84. package/dist/generated/Tax.d.ts +4 -4
  85. package/dist/generated/Tax.js +7 -8
  86. package/dist/generated/WorkingHoursRange.d.ts +2 -2
  87. package/dist/index.d.ts +2 -2
  88. package/dist/index.js +11 -10
  89. package/package.json +55 -55
  90. package/dist/generated/TaxType.d.ts +0 -25
  91. package/dist/generated/TaxType.js +0 -30
package/dist/Catalog.js CHANGED
@@ -1,39 +1,40 @@
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 nothing buyable 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
- this._products = Object.freeze([...products]);
18
- // Only a buyable product - one a customer can actually order - counts towards a category. A
19
- // product that cannot be bought right now keeps its place in Catalog.products and stays
20
- // reachable by getProduct, but does not keep a category alive on its own.
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.
20
+ const buyableProducts = [];
21
21
  const productsByCategory = new Map();
22
- for (const product of this._products) {
22
+ for (const product of products) {
23
+ if (!product.isBuyable)
24
+ continue;
25
+ buyableProducts.push(product);
23
26
  this._productMap.set(product.id, product);
24
- if (product.isBuyable) {
25
- const productsOfCategory = productsByCategory.get(product.productCategoryId);
26
- if (productsOfCategory === undefined)
27
- productsByCategory.set(product.productCategoryId, [product]);
28
- else
29
- productsOfCategory.push(product);
30
- }
27
+ const productsOfCategory = productsByCategory.get(product.productCategoryId);
28
+ if (productsOfCategory === undefined)
29
+ productsByCategory.set(product.productCategoryId, [product]);
30
+ else
31
+ productsOfCategory.push(product);
31
32
  for (const pkg of product.packages)
32
33
  this._packageMap.set(pkg.id, pkg);
33
34
  }
34
- // A category with nothing a customer can order is left out rather than shown empty - there is
35
- // no point showing it to a customer. Nothing here is special-cased for the dummy: it is left
36
- // out exactly the same way when nothing buyable is uncategorized.
35
+ this._products = Object.freeze(buyableProducts);
36
+ // A category that no buyable product reached is left out rather than shown empty. This covers
37
+ // the dummy too.
37
38
  const nonEmptyCategories = [];
38
39
  for (const category of [...categories, uncategorized]) {
39
40
  const categoryProducts = productsByCategory.get(category.id);
@@ -50,13 +51,13 @@ class Catalog {
50
51
  get categories() {
51
52
  return this._categories;
52
53
  }
53
- // Every product of the business, whether or not it is in a category, in no particular order
54
+ // Every buyable product of the business, in a category or not, in no particular order
54
55
  get products() {
55
56
  return this._products;
56
57
  }
57
- // The one price list a storefront sells from - MeraBills Online - and null until it arrives.
58
- // Every price in this catalog belongs to it; a price in any other list is not sold here and is
59
- // 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.
60
61
  get priceList() {
61
62
  return this._priceList;
62
63
  }
@@ -69,16 +70,17 @@ class Catalog {
69
70
  getCategory(productCategoryId) {
70
71
  return this._categoryMap.get(productCategoryId) ?? null;
71
72
  }
73
+ // Null for an unknown id, and for a product that is not buyable, which this catalog does not hold
72
74
  getProduct(productId) {
73
75
  return this._productMap.get(productId) ?? null;
74
76
  }
75
- // Returns null for the four reserved package ids a shopping list uses for its charge and
76
- // summary lines (see Ids.isReservedPackageId), and for a package whose product was deleted
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
77
79
  getPackage(packageId) {
78
80
  return this._packageMap.get(packageId) ?? null;
79
81
  }
80
- // Display comparator for packages, matching the merchant app's PackageComparator (minus clauses
81
- // 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)
82
84
  static comparePackages(left, right) {
83
85
  const byName = Catalog.compareNames(left.name, right.name);
84
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.d.ts CHANGED
@@ -12,5 +12,5 @@ export { ProductType } from "./generated/ProductType";
12
12
  export { ReservedPackage } from "./generated/ReservedPackage";
13
13
  export { Role } from "./generated/Role";
14
14
  export { TaskStatus } from "./generated/TaskStatus";
15
- export { TaxType } from "./generated/TaxType";
15
+ export { TaxType } from "./TaxType";
16
16
  export { WeightUnit } from "./generated/WeightUnit";
package/dist/Enums.js CHANGED
@@ -1,5 +1,6 @@
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
+ // TaxType is the exception: the protocol does not carry it, so it is defined in TaxType.ts.
3
4
  export { AddressType } from "./generated/AddressType";
4
5
  export { AttachmentType } from "./generated/AttachmentType";
5
6
  export { AuthenticationMethod } from "./generated/AuthenticationMethod";
@@ -14,5 +15,5 @@ export { ProductType } from "./generated/ProductType";
14
15
  export { ReservedPackage } from "./generated/ReservedPackage";
15
16
  export { Role } from "./generated/Role";
16
17
  export { TaskStatus } from "./generated/TaskStatus";
17
- export { TaxType } from "./generated/TaxType";
18
+ export { TaxType } from "./TaxType";
18
19
  export { WeightUnit } from "./generated/WeightUnit";
@@ -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
  }
@@ -23,8 +23,9 @@ export default class GeneralSettings {
23
23
  private readonly _googleBusinessCategoryId;
24
24
  private readonly _googleBusinessCategoryName;
25
25
  private readonly _onlineStoreThemeId;
26
- static fromScaledValues(paymentMethodDetails: ReadonlyArray<PaymentMethodDetail>, customerServicePhone: string | null, isWhatsAppSupported: boolean, isSmsSupported: boolean, businessAddresses: AddressList, businessIdentifiers: ReadonlyArray<BusinessIdentifier>, fulfillmentModes: ReadonlyArray<proto_FulfillmentMode>, scaledDeliveryCharge: bigint, deliveryChargeTaxes: ReadonlyArray<Tax>, scaledFreeDeliveryTotal: bigint, deliveryRadiusKilometres: number, storeTagline: string | null, workingDays: WorkingDays, primaryStoreLocation: LatLong | null, googleBusinessCategoryId: string | null, googleBusinessCategoryName: string | null, onlineStoreThemeId: string | null): GeneralSettings;
27
- constructor(_paymentMethodDetails: ReadonlyArray<PaymentMethodDetail>, _customerServicePhone: string | null, _isWhatsAppSupported: boolean, _isSmsSupported: boolean, _businessAddresses: AddressList, _businessIdentifiers: ReadonlyArray<BusinessIdentifier>, _fulfillmentModes: ReadonlyArray<proto_FulfillmentMode>, _deliveryCharge: number, _deliveryChargeTaxes: ReadonlyArray<Tax>, _freeDeliveryTotal: number, _deliveryRadiusKilometres: number, _storeTagline: string | null, _workingDays: WorkingDays, _primaryStoreLocation: LatLong | null, _googleBusinessCategoryId: string | null, _googleBusinessCategoryName: string | null, _onlineStoreThemeId: string | null);
26
+ private readonly _onlineShoppingEnabled;
27
+ static fromScaledValues(paymentMethodDetails: ReadonlyArray<PaymentMethodDetail>, customerServicePhone: string | null, isWhatsAppSupported: boolean, isSmsSupported: boolean, businessAddresses: AddressList, businessIdentifiers: ReadonlyArray<BusinessIdentifier>, fulfillmentModes: ReadonlyArray<proto_FulfillmentMode>, scaledDeliveryCharge: bigint, deliveryChargeTaxes: ReadonlyArray<Tax>, scaledFreeDeliveryTotal: bigint, deliveryRadiusKilometres: number, storeTagline: string | null, workingDays: WorkingDays, primaryStoreLocation: LatLong | null, googleBusinessCategoryId: string | null, googleBusinessCategoryName: string | null, onlineStoreThemeId: string | null, onlineShoppingEnabled: boolean): GeneralSettings;
28
+ constructor(_paymentMethodDetails: ReadonlyArray<PaymentMethodDetail>, _customerServicePhone: string | null, _isWhatsAppSupported: boolean, _isSmsSupported: boolean, _businessAddresses: AddressList, _businessIdentifiers: ReadonlyArray<BusinessIdentifier>, _fulfillmentModes: ReadonlyArray<proto_FulfillmentMode>, _deliveryCharge: number, _deliveryChargeTaxes: ReadonlyArray<Tax>, _freeDeliveryTotal: number, _deliveryRadiusKilometres: number, _storeTagline: string | null, _workingDays: WorkingDays, _primaryStoreLocation: LatLong | null, _googleBusinessCategoryId: string | null, _googleBusinessCategoryName: string | null, _onlineStoreThemeId: string | null, _onlineShoppingEnabled: boolean);
28
29
  get paymentMethodDetails(): ReadonlyArray<PaymentMethodDetail>;
29
30
  get customerServicePhone(): string | null;
30
31
  get isWhatsAppSupported(): boolean;
@@ -42,5 +43,6 @@ export default class GeneralSettings {
42
43
  get googleBusinessCategoryId(): string | null;
43
44
  get googleBusinessCategoryName(): string | null;
44
45
  get onlineStoreThemeId(): string | null;
46
+ get onlineShoppingEnabled(): boolean;
45
47
  static readonly EMPTY: GeneralSettings;
46
48
  }
@@ -3,14 +3,14 @@ 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.
10
- static fromScaledValues(paymentMethodDetails, customerServicePhone, isWhatsAppSupported, isSmsSupported, businessAddresses, businessIdentifiers, fulfillmentModes, scaledDeliveryCharge, deliveryChargeTaxes, scaledFreeDeliveryTotal, deliveryRadiusKilometres, storeTagline, workingDays, primaryStoreLocation, googleBusinessCategoryId, googleBusinessCategoryName, onlineStoreThemeId) {
11
- return new GeneralSettings(paymentMethodDetails, customerServicePhone, isWhatsAppSupported, isSmsSupported, businessAddresses, businessIdentifiers, fulfillmentModes, amountFromScaledValue(scaledDeliveryCharge), deliveryChargeTaxes, amountFromScaledValue(scaledFreeDeliveryTotal), deliveryRadiusKilometres, storeTagline, workingDays, primaryStoreLocation, googleBusinessCategoryId, googleBusinessCategoryName, onlineStoreThemeId);
9
+ // For ProtoMap, which sees amounts still multiplied by Constants.AMOUNT_FACTOR.
10
+ static fromScaledValues(paymentMethodDetails, customerServicePhone, isWhatsAppSupported, isSmsSupported, businessAddresses, businessIdentifiers, fulfillmentModes, scaledDeliveryCharge, deliveryChargeTaxes, scaledFreeDeliveryTotal, deliveryRadiusKilometres, storeTagline, workingDays, primaryStoreLocation, googleBusinessCategoryId, googleBusinessCategoryName, onlineStoreThemeId, onlineShoppingEnabled) {
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
  }
13
- constructor(_paymentMethodDetails, _customerServicePhone, _isWhatsAppSupported, _isSmsSupported, _businessAddresses, _businessIdentifiers, _fulfillmentModes, _deliveryCharge, _deliveryChargeTaxes, _freeDeliveryTotal, _deliveryRadiusKilometres, _storeTagline, _workingDays, _primaryStoreLocation, _googleBusinessCategoryId, _googleBusinessCategoryName, _onlineStoreThemeId) {
13
+ constructor(_paymentMethodDetails, _customerServicePhone, _isWhatsAppSupported, _isSmsSupported, _businessAddresses, _businessIdentifiers, _fulfillmentModes, _deliveryCharge, _deliveryChargeTaxes, _freeDeliveryTotal, _deliveryRadiusKilometres, _storeTagline, _workingDays, _primaryStoreLocation, _googleBusinessCategoryId, _googleBusinessCategoryName, _onlineStoreThemeId, _onlineShoppingEnabled) {
14
14
  this._paymentMethodDetails = _paymentMethodDetails;
15
15
  this._customerServicePhone = _customerServicePhone;
16
16
  this._isWhatsAppSupported = _isWhatsAppSupported;
@@ -28,6 +28,7 @@ class GeneralSettings {
28
28
  this._googleBusinessCategoryId = _googleBusinessCategoryId;
29
29
  this._googleBusinessCategoryName = _googleBusinessCategoryName;
30
30
  this._onlineStoreThemeId = _onlineStoreThemeId;
31
+ this._onlineShoppingEnabled = _onlineShoppingEnabled;
31
32
  }
32
33
  get paymentMethodDetails() {
33
34
  return this._paymentMethodDetails;
@@ -52,15 +53,18 @@ class GeneralSettings {
52
53
  get fulfillmentModes() {
53
54
  return this._fulfillmentModes;
54
55
  }
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.
55
58
  get deliveryCharge() {
56
59
  return this._deliveryCharge;
57
60
  }
58
- // The taxes on the delivery charge, which are the business's own and not a product's - at most
59
- // Constants.MAX_TAXES of them. Empty means the charge is untaxed.
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.
60
63
  get deliveryChargeTaxes() {
61
64
  return this._deliveryChargeTaxes;
62
65
  }
63
- // Order total at or above which delivery is free. 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.
64
68
  get freeDeliveryTotal() {
65
69
  return this._freeDeliveryTotal;
66
70
  }
@@ -71,29 +75,37 @@ class GeneralSettings {
71
75
  get storeTagline() {
72
76
  return this._storeTagline;
73
77
  }
74
- // 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.
75
79
  get workingDays() {
76
80
  return this._workingDays;
77
81
  }
78
- // Where this business is: the coordinates of the first of businessAddresses. Null when the
79
- // business has not published them.
82
+ // Where this business is: the coordinates of the first of businessAddresses. Null when not
83
+ // published.
80
84
  get primaryStoreLocation() {
81
85
  return this._primaryStoreLocation;
82
86
  }
83
- // What kind of business this is, as its Google Business Profile category - "Bakery", say. The
84
- // 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.
85
89
  get googleBusinessCategoryId() {
86
90
  return this._googleBusinessCategoryId;
87
91
  }
88
92
  get googleBusinessCategoryName() {
89
93
  return this._googleBusinessCategoryName;
90
94
  }
91
- // Which of a storefront's own themes this business picked. The themes are the storefront's, so
92
- // 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.
93
97
  get onlineStoreThemeId() {
94
98
  return this._onlineStoreThemeId;
95
99
  }
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
+ get onlineShoppingEnabled() {
105
+ return this._onlineShoppingEnabled;
106
+ }
96
107
  }
97
- // A business with no published public settings, used instead of null so every field is already the protocol's empty value.
98
- 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);
108
+ // A business with no published public settings. Used instead of null so every field is already
109
+ // the protocol's empty value.
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);
99
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.d.ts CHANGED
@@ -7,11 +7,13 @@ export default class Ids {
7
7
  static isReservedPackageId(packageId: bigint): boolean;
8
8
  static toStorageHexString(id: bigint): string;
9
9
  static toOrderNumberString(orderId: bigint): string;
10
+ static getSyncCursorBlobId(key: number): bigint;
10
11
  static readonly ENTITY_TYPE_BIT_SHIFT: bigint;
11
12
  static readonly ID_MASK: bigint;
12
13
  static readonly GENERAL_SETTINGS_CONFIGURATION_ID: bigint;
13
14
  static readonly ATTACHMENT_BASE_URL_BLOB_ID: bigint;
14
15
  static readonly AUTH_TOKENS_BLOB_ID: bigint;
16
+ private static readonly SYNC_CURSOR_BLOB_ID_BASE;
15
17
  private static readonly UNSIGNED_64_BIT_MASK;
16
18
  private static readonly UNSIGNED_32_BIT_MASK;
17
19
  }
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,29 +46,37 @@ 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
  }
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.
63
+ static getSyncCursorBlobId(key) {
64
+ return Ids.SYNC_CURSOR_BLOB_ID_BASE - BigInt(key);
65
+ }
64
66
  }
65
67
  Ids.ENTITY_TYPE_BIT_SHIFT = BigInt(proto_Constants.ENTITY_TYPE_BIT_SHIFT);
66
68
  // Everything an id holds below its entity type; not in Constants.proto since 64 bits don't fit an enum value.
67
69
  Ids.ID_MASK = (1n << BigInt(proto_Constants.ENTITY_TYPE_BIT_SHIFT)) - 1n;
68
70
  // The configuration holding a business's public settings - the only one sent to a storefront (Java Configuration.MIN_PUBLIC_CONFIGURATION_ID).
69
71
  Ids.GENERAL_SETTINGS_CONFIGURATION_ID = 0x0d10000000000000n;
70
- // A price list id is built by Constants.buildPriceListId rather than named here: which built-in
71
- // 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.
72
74
  // Where a business container keeps that business's attachment base url; its eTag is that url's expiry.
73
75
  Ids.ATTACHMENT_BASE_URL_BLOB_ID = -1n;
74
- // 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.
75
77
  Ids.AUTH_TOKENS_BLOB_ID = -2n;
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.
79
+ Ids.SYNC_CURSOR_BLOB_ID_BASE = -0x100n;
76
80
  Ids.UNSIGNED_64_BIT_MASK = (1n << 64n) - 1n;
77
81
  Ids.UNSIGNED_32_BIT_MASK = (1n << 32n) - 1n;
78
82
  export default Ids;
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;