@peabody-soft/mbs-model-ts 1.5.0 → 1.7.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.
- package/dist/Address.js +8 -11
- package/dist/Attachment.js +7 -8
- package/dist/AttachmentBaseUrl.js +10 -10
- package/dist/AuthTokens.js +12 -7
- package/dist/Business.js +19 -19
- package/dist/BusinessIdentifier.js +2 -2
- package/dist/BusinessLink.js +16 -16
- package/dist/BusinessProfile.js +4 -4
- package/dist/CartEditor.js +137 -166
- package/dist/Catalog.js +19 -25
- package/dist/Constants.js +10 -10
- package/dist/ContainerNames.js +10 -11
- package/dist/Entity.js +11 -9
- package/dist/Enums.js +2 -2
- package/dist/Exceptions.js +2 -2
- package/dist/GeneralSettings.js +23 -21
- package/dist/HttpStatusCodes.js +10 -11
- package/dist/Ids.js +24 -30
- package/dist/Item.js +6 -7
- package/dist/LatLong.js +3 -3
- package/dist/LiveData.js +6 -6
- package/dist/Measures.js +2 -2
- package/dist/Message.js +6 -6
- package/dist/MockDatabase.js +2 -2
- package/dist/Model.js +294 -365
- package/dist/Order.js +20 -17
- package/dist/Package.js +7 -8
- package/dist/PackageIdentifier.js +2 -2
- package/dist/PaymentMethodDetail.js +4 -4
- package/dist/Photo.js +6 -7
- package/dist/Price.js +8 -9
- package/dist/PriceList.js +2 -2
- package/dist/Product.js +17 -22
- package/dist/ProductCategory.js +3 -4
- package/dist/Profile.js +26 -27
- package/dist/ProtoMap.d.ts +3 -0
- package/dist/ProtoMap.js +288 -294
- package/dist/Routes.js +3 -3
- package/dist/ServiceExceptions.js +11 -12
- package/dist/ServiceProxy.js +19 -23
- package/dist/ShoppingList.js +112 -130
- package/dist/SyncCache.d.ts +3 -0
- package/dist/SyncCache.js +92 -75
- package/dist/SyncKeys.js +8 -8
- package/dist/SyncState.js +5 -5
- package/dist/Tax.js +16 -21
- package/dist/TaxHelper.js +22 -30
- package/dist/TaxType.js +8 -9
- package/dist/UserProfile.js +4 -4
- package/dist/WorkingDays.js +15 -15
- package/dist/index.js +9 -9
- 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
|
|
5
|
-
//
|
|
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
|
|
13
|
-
//
|
|
14
|
-
//
|
|
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
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
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
|
|
39
|
-
//
|
|
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
|
|
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
|
|
64
|
-
//
|
|
65
|
-
//
|
|
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
|
-
//
|
|
83
|
-
//
|
|
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
|
|
89
|
-
// a storefront
|
|
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
|
|
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
|
|
10
|
-
//
|
|
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
|
|
15
|
-
//
|
|
16
|
-
//
|
|
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)
|
|
19
|
-
// a protobuf enum value. Unscaled it
|
|
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.
|
|
37
|
-
//
|
|
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
|
}
|
package/dist/ContainerNames.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
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
|
|
8
|
-
//
|
|
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
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
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
|
|
19
|
-
// id would
|
|
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
|
|
4
|
-
// the database it was last updated in. Rebuilt from the wire
|
|
5
|
-
//
|
|
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
|
-
//
|
|
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
|
|
25
|
-
// to avoid echoing a store's own changes back. Business entities only
|
|
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:
|
|
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
|
|
53
|
-
// bigint and
|
|
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
|
|
2
|
-
//
|
|
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";
|
package/dist/Exceptions.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// The two failures this library raises
|
|
2
|
-
// from
|
|
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
|
}
|
package/dist/GeneralSettings.js
CHANGED
|
@@ -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
|
|
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
|
-
//
|
|
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,
|
|
57
|
-
//
|
|
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
|
|
62
|
-
//
|
|
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
|
|
67
|
-
//
|
|
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,38 @@ 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
|
|
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
|
|
83
|
-
//
|
|
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
|
|
88
|
-
//
|
|
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
|
|
96
|
-
//
|
|
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
|
|
101
|
-
// with a NotFoundException,
|
|
102
|
-
// say so
|
|
103
|
-
// is what
|
|
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. It is the only thing about what a business sells that closes it: one with
|
|
104
|
+
// nothing a customer can buy is open, with an empty catalog.
|
|
104
105
|
get onlineShoppingEnabled() {
|
|
105
106
|
return this._onlineShoppingEnabled;
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
|
-
// A business with no published public settings
|
|
109
|
+
// A business with no published public settings. Used instead of null so every field is already
|
|
110
|
+
// the protocol's empty value.
|
|
109
111
|
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
112
|
export default GeneralSettings;
|
package/dist/HttpStatusCodes.js
CHANGED
|
@@ -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
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// Everything from 300 up
|
|
6
|
-
//
|
|
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
|
|
12
|
-
// A code with no class of its own
|
|
13
|
-
//
|
|
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
|
|
24
|
-
//
|
|
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
|
-
//
|
|
4
|
-
// Some ids below are
|
|
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
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
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
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
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
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
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
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
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
|
|
79
|
-
//
|
|
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
|
|
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
|
|
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
|
|
3
|
-
// parentItemId is the wire parent (
|
|
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
|
|
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,
|
|
24
|
-
// change after an item exists, so it is sent rather than filtered out
|
|
25
|
-
//
|
|
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
|
|
2
|
-
// location.
|
|
3
|
-
//
|
|
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
|
|
2
|
-
//
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2
|
-
// Units are the generated proto enums
|
|
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)
|
|
3
|
-
// A business entity despite being customer-authored, since Java authorizes the message table
|
|
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),
|
|
14
|
-
// sends a customer only
|
|
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
|
|
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
|
}
|
package/dist/MockDatabase.js
CHANGED
|
@@ -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
|
|
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
|
-
//
|
|
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;
|