@liquidcommerce/elements-sdk 2.6.0-beta.135 → 2.6.0-beta.137

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.
@@ -1,4 +1,4 @@
1
- import type { ICartEvent, ICartFulfillment, ICartItemAttributes, ICartPromoCode, ICartRetailer } from '@/interfaces/api/cart.interface';
1
+ import type { IAutomaticDiscount, ICartEvent, ICartFulfillment, ICartItemAttributes, ICartPromoCode, ICartRetailer } from '@/interfaces/api/cart.interface';
2
2
  export interface ICartItemStore {
3
3
  id: string;
4
4
  variantId: string;
@@ -47,6 +47,7 @@ export interface ICartStore {
47
47
  fulfillments: Record<string, ICartFulfillmentStore>;
48
48
  retailers: Record<string, ICartRetailerStore>;
49
49
  promoCode: ICartPromoCode | null;
50
+ automaticDiscounts: IAutomaticDiscount[];
50
51
  events: ICartEvent[];
51
52
  loading: boolean;
52
53
  error: string | null;
@@ -1,3 +1,4 @@
1
+ import type { IAutomaticDiscount } from '@/interfaces/api/cart.interface';
1
2
  import type { ICheckoutAmounts, ICheckoutBilling, ICheckoutCustomer, ICheckoutEvent, ICheckoutFulfillment, ICheckoutFulfillmentMethod, ICheckoutGiftCard, ICheckoutGiftRecipient, ICheckoutItem, ICheckoutMarketingPreferences, ICheckoutPaymentConfirm, ICheckoutPaymentSession, ICheckoutPresale, ICheckoutPromoCode, ICheckoutRetailer, ICheckoutShippingAddress } from '@/interfaces/api/checkout.interface';
2
3
  export type DeliveryWindowLapse = 'expired' | 'unavailable' | 'required';
3
4
  export interface ICheckoutItemStore extends ICheckoutItem {
@@ -61,6 +62,7 @@ export interface ICheckoutStore {
61
62
  express: ICheckoutExpressStore;
62
63
  shippingAddress: ICheckoutShippingAddress;
63
64
  promoCode: ICheckoutPromoCode | null;
65
+ automaticDiscounts: IAutomaticDiscount[];
64
66
  giftCards: ICheckoutGiftCard[];
65
67
  itemsQuantity: number;
66
68
  items: Record<string, ICheckoutItemStore>;
@@ -77,6 +77,12 @@ export interface ICartEvent {
77
77
  message: string;
78
78
  level: CartEventLevel;
79
79
  }
80
+ export interface IAutomaticDiscount {
81
+ code: string;
82
+ title: string;
83
+ target: 'products' | 'fulfillment';
84
+ discount: number;
85
+ }
80
86
  export interface ICart {
81
87
  id: string;
82
88
  subtotal: number;
@@ -84,6 +90,7 @@ export interface ICart {
84
90
  items: Record<string, ICartItem>;
85
91
  retailers: Record<string, ICartRetailer>;
86
92
  promoCode: ICartPromoCode | null;
93
+ automaticDiscounts?: IAutomaticDiscount[];
87
94
  events: ICartEvent[];
88
95
  }
89
96
  export interface ICartItemEngraving {
@@ -1,6 +1,6 @@
1
1
  import type { CheckoutEventLevel, CheckoutEventType, FulfillmentType } from '@/enums';
2
2
  import type { IAddressAddress, IAddressCoordinates, ILocation } from '@/interfaces/api/address.interface';
3
- import type { ICart, ICartUpdateItem } from '@/interfaces/api/cart.interface';
3
+ import type { IAutomaticDiscount, ICart, ICartUpdateItem } from '@/interfaces/api/cart.interface';
4
4
  import type { CheckoutDeliveryScheduling, CheckoutDeliverySelection, DeliveryAvailability } from '@/interfaces/api/delivery.interface';
5
5
  import type { IProductPresale } from '@/interfaces/api/product.interface';
6
6
  export interface ICheckoutComplete {
@@ -267,6 +267,7 @@ export interface ICheckoutPrepare {
267
267
  shippingAddress: ICheckoutShippingAddress;
268
268
  billingAddress: ICheckoutBilling;
269
269
  promoCode: ICheckoutPromoCode | null;
270
+ automaticDiscounts?: IAutomaticDiscount[];
270
271
  amounts: ICheckoutAmounts;
271
272
  giftCards: ICheckoutGiftCard[];
272
273
  presale: ICheckoutPresale | null;
@@ -7,6 +7,8 @@ export declare class CartFooterComponent extends BaseComponent<ICartFooterCompon
7
7
  get hostClasses(): string[];
8
8
  private subtotalText;
9
9
  private subtotalPrice;
10
+ private itemsPrice;
11
+ private automaticDiscountRows;
10
12
  private checkoutButton;
11
13
  constructor();
12
14
  onStoreWatch(changes: IOnStoreChanged[]): void;
@@ -15,6 +17,7 @@ export declare class CartFooterComponent extends BaseComponent<ICartFooterCompon
15
17
  private shouldDisableCheckout;
16
18
  private updateCheckoutButtonState;
17
19
  private updateSubtotalItemsCount;
20
+ private renderAutomaticDiscounts;
18
21
  private handleCheckout;
19
22
  protected template(): HTMLElement[];
20
23
  }
@@ -152,6 +152,7 @@ export declare class CheckoutCommands extends BaseCommand {
152
152
  checkoutComplete(options?: {
153
153
  expressPaymentIntentId?: string;
154
154
  }): Promise<void>;
155
+ private discountUpdates;
155
156
  private updateStoreFromCheckoutResponse;
156
157
  private getDeliveryMethodLocation;
157
158
  loadDeliveryMethods(): Promise<void>;
@@ -5,9 +5,12 @@ export declare class CheckoutDeliverToComponent extends BaseComponent<null, IChe
5
5
  private giftData;
6
6
  private saveButtonText;
7
7
  private saveButton;
8
+ private deliveryTimeFocusFrame;
8
9
  get hostClasses(): string[];
9
10
  constructor();
10
11
  onStoreWatch(changes: IOnStoreChanged[]): void;
12
+ private moveToDeliveryTime;
13
+ protected disconnected(): void;
11
14
  private get requiresBirthDate();
12
15
  private updateSaveButtonState;
13
16
  protected template(): HTMLElement[];
@@ -43,6 +43,8 @@ export declare class CheckoutDeliverySchedulingComponent extends BaseComponent<n
43
43
  private radio;
44
44
  private createDates;
45
45
  private createTimes;
46
+ private shownZone;
47
+ private createZoneNote;
46
48
  private createNotice;
47
49
  private createLoading;
48
50
  private createDialogShell;
@@ -39,14 +39,18 @@ export declare function selectedDeliverySlot(scheduling: CheckoutDeliverySchedul
39
39
  export declare function isLapsedSelectedDeliverySlot(scheduling: CheckoutDeliveryScheduling | null | undefined, now?: number): boolean;
40
40
  export declare function deliverySchedulesReady(fulfillments: Record<string, ICheckoutFulfillment>, now?: number, expired?: string[]): boolean;
41
41
  export declare function deliveryChoicesMade(fulfillments: Record<string, ICheckoutFulfillment>, expired?: string[]): boolean;
42
+ export declare function deliveryWindowNeededIds(fulfillments: Record<string, ICheckoutFulfillment> | null | undefined, expired?: string[], now?: number): string[];
42
43
  export declare function hasDeliveryScheduling(fulfillments: Record<string, ICheckoutFulfillment> | null | undefined): boolean;
43
44
  export declare function retailerOfFulfillment(retailers: Record<string, ICheckoutRetailer> | null | undefined, fulfillmentId: string): ICheckoutRetailer | undefined;
44
45
  export declare function deliveryWindowNeededMessage(store: Pick<StoreService, 'get'>, fulfillmentIds: string[]): string;
45
46
  export declare function hasSeveralDeliveryGroups(fulfillments: Record<string, ICheckoutFulfillment> | null | undefined): boolean;
46
- export declare function deliveryTimeZone(): string;
47
+ export declare function deliveryTimeZone(storeTimeZone?: string | null): string;
48
+ export declare function schedulingTimeZone(scheduling: CheckoutDeliveryScheduling | null | undefined): string;
47
49
  export declare function deliveryDateTimeFormat(timezone: string, options: Omit<Intl.DateTimeFormatOptions, 'timeZone'>): Intl.DateTimeFormat;
48
50
  export declare function deliveryTimeZoneName(timezone: string, style?: 'short' | 'long'): string;
51
+ export declare function deliveryTimeZoneAbbreviation(instant: string, timezone: string): string;
49
52
  export declare function deliveryTimeLabel(slot: DeliverySlot, timezone: string): string;
53
+ export declare function deliveryRangeLabel(slot: DeliverySlot, timezone: string): string;
50
54
  export declare function deliveryWindowLabel(slot: DeliverySlot, timezone: string): string;
51
55
  export declare function deliveryScheduledLabel(slot: DeliverySlot, timezone: string): string;
52
56
  export declare function deliveryCutoffLabel(slot: DeliverySlot, timezone: string): string;
@@ -0,0 +1,2 @@
1
+ import type { IAutomaticDiscount } from '@/interfaces/api/cart.interface';
2
+ export declare function getPromoCodeSavings(discounts: number, promoCodeDiscount: number | undefined, automaticDiscounts: IAutomaticDiscount[]): number;
@@ -156,6 +156,15 @@ interface ICheckoutPrepare {
156
156
  items: Record<string /* cart item ID */, ICheckoutItem>;
157
157
  retailers: Record<string /* retailer ID */, ICheckoutRetailer>;
158
158
  events: ICheckoutEvent[];
159
+ automaticDiscounts?: IAutomaticDiscount[];
160
+ }
161
+
162
+ // A discount the store applied without a code being typed. Its savings are already in `amounts`.
163
+ interface IAutomaticDiscount {
164
+ code: string;
165
+ title: string;
166
+ target: 'products' | 'fulfillment';
167
+ discount: number; // cents
159
168
  }
160
169
  ```
161
170
 
@@ -589,7 +589,7 @@ export interface DeliveryAvailability {
589
589
  status: 'available' | 'unavailable'; // available when the store can deliver now or offers windows
590
590
  canDeliverNow: boolean;
591
591
  canSchedule: boolean;
592
- timezone: string | null; // store IANA time zone; null when the server has none. The SDK shows times in the viewer's browser time zone, not this one
592
+ timezone: string | null; // store IANA time zone; null when the server has none. The SDK shows every delivery time in this zone, named ("EDT"); the viewer's zone only when it is null
593
593
  nextSlot: DeliverySlot | null; // null on a product fulfillment of a closed store: checkout gives the windows
594
594
  checkedAt: string;
595
595
  }
@@ -154,7 +154,7 @@ on render, and a live region recreated with its text already in place is not ann
154
154
  option names the retailer, the price, the timing, the delivery fee and — for same-day — the
155
155
  store address and whether it is currently closed. A closed store that schedules delivery leads
156
156
  with that instead ("Columbus Avenue Wines, closed now, schedule delivery"), followed by
157
- "next arrival Thu, 1 PM – 2 PM" only when the server sends a next window; its card
157
+ "next arrival Thu 10/8, 1–2 PM EDT" only when the server sends a next window; its card
158
158
  stays enabled.
159
159
  - The California Proposition 65 warning on the product is a native `<details>` / `<summary>`
160
160
  disclosure: the summary is a button that exposes its expanded state, and `Enter` / `Space` toggle
@@ -295,4 +295,4 @@ keeps that channel visible when account data arrives later.
295
295
 
296
296
  ### Delivery date and arrival windows
297
297
 
298
- The delivery picker is a named native modal dialog opened from each delivery's Schedule delivery or Edit control. Dates and arrival windows are radio groups with one tab stop each and arrow-key selection; the date strip scrolls inside the dialog and unavailable dates are disabled. While windows load, the dialog is marked busy and shows a loading line; Cancel and Close stay available. Changes are drafts until Save; Cancel, Close and Escape discard them, and closing returns focus to the opening control. The dialog stays the same modal while it is open: a date or window change updates its contents in place, so it is not announced again. Save errors keep the dialog open and move focus to the error. Success is announced politely; errors and an expired or unavailable window are announced assertively, each with its own message. No time zone is shown above the windows; the ASAP window's accessible name is "As soon as possible", and the first window's accessible name also carries its "Order by" time. The window list scrolls inside the dialog, and the chosen or focused window is kept in view as the picker opens, the date changes and arrow keys move.
298
+ The delivery picker is a named native modal dialog opened from each delivery's Schedule delivery or Edit control. Dates and arrival windows are radio groups with one tab stop each and arrow-key selection; the date strip scrolls inside the dialog and unavailable dates are disabled. While windows load, the dialog is marked busy and shows a loading line; Cancel and Close stay available. Changes are drafts until Save; Cancel, Close and Escape discard them, and closing returns focus to the opening control. The dialog stays the same modal while it is open: a date or window change updates its contents in place, so it is not announced again. Save errors keep the dialog open and move focus to the error. Success is announced politely; errors and an expired or unavailable window are announced assertively, each with its own message. A line above the windows names their time zone, the store's ("Times in EDT"), and the window list's accessible name carries it ("Arrival window, times in EDT"); the ASAP window's accessible name is "As soon as possible", and the first window's accessible name also carries its "Order by" time. The window list scrolls inside the dialog, and the chosen or focused window is kept in view as the picker opens, the date changes and arrow keys move. When the shopper saves the delivery details with the Save button and a delivery still needs a window, focus moves to that delivery's Schedule delivery control and its row scrolls into view, because the collapsing form would otherwise leave the shopper at payment, past the step that blocks it. Focus moves only from the Save button: a prefill through the actions API, or a shopper who has already moved focus elsewhere, is left alone.
@@ -907,13 +907,15 @@ fails, the shopper can retry or explicitly choose the visible channel preference
907
907
 
908
908
  ## Scheduled delivery
909
909
 
910
- When an AccelPay retailer has scheduled delivery turned on, checkout shows a **Delivery time** row for that delivery, between the delivery details and payment. It reads "ASAP" while the store can deliver now, or asks for a window when it cannot. The row's button reads **Schedule** while the row is on as soon as possible, **Edit** after a window is saved, and **Schedule delivery** when the shopper must choose a window. It opens a dialog that loads the windows for that delivery: date cards for today and the next 7 days, then arrival windows. Days and times are in the shopper's own time zone (the browser's), so "Today" is the shopper's today; no time zone is shown. The window list scrolls inside the dialog and keeps the chosen window in view. The first window shows "Order by" with its cutoff, and **ASAP** sits next to it when the store can deliver now (screen readers hear "As soon as possible"). Choices are drafts until **Save**; Cancel, Close and Escape discard them.
910
+ When an AccelPay retailer has scheduled delivery turned on, checkout shows a **Delivery time** row for that delivery, between the delivery details and payment. It reads "ASAP" while the store can deliver now, or asks for a window when it cannot. The row's button reads **Schedule** while the row is on as soon as possible, **Edit** after a window is saved, and **Schedule delivery** when the shopper must choose a window. It opens a dialog that loads the windows for that delivery: date cards for today and the next 7 days, then arrival windows. Days and times are in the store's time zone (`availability.timezone`), which for a local delivery is also the recipient's, so a shopper in another zone (a gift, say) reads the hour the delivery really comes. "Today" is the store's today, and a line above the windows names the zone ("Times in EDT"). Only when the server sends no zone are the browser's zone and its abbreviation used. The window list scrolls inside the dialog and keeps the chosen window in view. The first window shows "Order by" with its cutoff, and **ASAP** sits next to it when the store can deliver now (screen readers hear "As soon as possible"). Choices are drafts until **Save**; Cancel, Close and Escape discard them.
911
911
 
912
- The accepted window shows in the row and on the items in the order summary ("Scheduled for Thu, 1 PM – 2 PM"), and on the thank-you page as "Delivery time" with "Thu, 1 PM – 2 PM". An ASAP order adds no line there. If the window's cutoff passes before the order is placed, the SDK clears it, shows "Your delivery window has passed. Choose a new delivery time.", reopens the picker and keeps Place Order disabled until a new choice is saved. If the store no longer offers the window, the same happens with "Your delivery window is no longer available. Choose a new delivery time." Everything else the shopper entered stays.
912
+ When the shopper saves the delivery details (and, in a gift order, the buyer's details are saved too) and a store still needs a window, focus moves to that row's **Schedule delivery** button and the row scrolls into view, so the shopper does not land on payment below it. A checkout where no store needs a window keeps the shopper where they are.
913
+
914
+ The accepted window shows in the row and on the items in the order summary ("Scheduled for Thu 10/8, 1–2 PM EDT"), and on the thank-you page as "Delivery time" with "Thu 10/8, 1–2 PM EDT": the weekday, the month and day, the window and the zone abbreviation, which follows daylight saving time ("EST" in winter). An ASAP order adds no line there. If the window's cutoff passes before the order is placed, the SDK clears it, shows "Your delivery window has passed. Choose a new delivery time.", reopens the picker and keeps Place Order disabled until a new choice is saved. If the store no longer offers the window, the same happens with "Your delivery window is no longer available. Choose a new delivery time." Everything else the shopper entered stays.
913
915
 
914
916
  A store that was added to the cart while closed cannot deliver now, so checkout asks for a window and offers no ASAP. If a store closes after the shopper chose ASAP, Place Order answers `delivery_window_required`: the SDK shows "The store cannot deliver now. Choose a delivery time.", opens the picker and keeps Place Order disabled until a window is saved.
915
917
 
916
- In a checkout with more than one fulfillment group (another store, or shipping), each same-day group with scheduled delivery gets its own **Delivery time** row and its own picker, which loads that group's windows. Its button reads **Schedule this store's delivery** (**Edit** after a window is saved), and the picker is named after the store ("Schedule delivery from Columbus Avenue Wines"). Every prepare sends each group's current choice, so saving one group keeps the other's window. A group whose store can deliver now stays on as soon as possible; a group whose store cannot needs its own window before payment and Place Order. When a window lapses, only that group's window is cleared and only its picker reopens: the SDK clears the window whose cutoff passed on the page, and for `delivery_window_required` marks the groups that have no window. When an error names its group (`fulfillmentId` on the error entry), only that group is cleared. A name that is not a scheduling group of this checkout counts as no name; when the error names no group and the SDK cannot place it, every saved window is cleared (a refused window save clears nothing and asks for another time). When the save of one store's window is refused because the window of another store lapsed, the SDK clears that other window and names its store: "Choose a delivery window for Columbus Avenue Wines before continuing." Until each store that cannot deliver now has a window, payment says which: "Choose a delivery window for Amsterdam Liquors before continuing." In an order with more than one fulfillment group, the thank-you page has one line per group, named after its store: the accepted window for a scheduled group ("Columbus Avenue Wines" with "Thu, 1 PM – 2 PM"), and the group's own expectation for the others ("Harbor Spirits" with "Ships in 3-5 days"). A scheduled group never shows the as-soon-as-possible expectation. While a store that cannot deliver now has no accepted window, the express wallet buttons stay off, like the card form. When checkout refuses a store that has scheduled delivery and also another delivery method (`multiple_fulfillment_options_per_store`, code 5516), the SDK shows the refusal as the checkout error and keeps the saved windows: "Scheduled delivery cannot be combined with another delivery method from the same store. Choose ASAP delivery, or use one delivery method for all items from this store." When that store is closed now, the text leaves out ASAP: "This store is closed now. Use one delivery method for all items from this store." When the refusal says whether the store is closed now (`closedNow` on the error entry), the SDK uses that. Otherwise, when Place Order is refused, the SDK reads the cart again and decides "closed now" from its `deliveryAvailability`, because a store can close after the last prepare. Place Order and the express wallet stay off until a prepare succeeds after a change to the cart or to a delivery choice.
918
+ In a checkout with more than one fulfillment group (another store, or shipping), each same-day group with scheduled delivery gets its own **Delivery time** row and its own picker, which loads that group's windows. Its button reads **Schedule this store's delivery** (**Edit** after a window is saved), and the picker is named after the store ("Schedule delivery from Columbus Avenue Wines"). Every prepare sends each group's current choice, so saving one group keeps the other's window. A group whose store can deliver now stays on as soon as possible; a group whose store cannot needs its own window before payment and Place Order. When a window lapses, only that group's window is cleared and only its picker reopens: the SDK clears the window whose cutoff passed on the page, and for `delivery_window_required` marks the groups that have no window. When an error names its group (`fulfillmentId` on the error entry), only that group is cleared. A name that is not a scheduling group of this checkout counts as no name; when the error names no group and the SDK cannot place it, every saved window is cleared (a refused window save clears nothing and asks for another time). When the save of one store's window is refused because the window of another store lapsed, the SDK clears that other window and names its store: "Choose a delivery window for Columbus Avenue Wines before continuing." Until each store that cannot deliver now has a window, payment says which: "Choose a delivery window for Amsterdam Liquors before continuing." In an order with more than one fulfillment group, the thank-you page has one line per group, named after its store: the accepted window for a scheduled group ("Columbus Avenue Wines" with "Thu 10/8, 1–2 PM EDT"), and the group's own expectation for the others ("Harbor Spirits" with "Ships in 3-5 days"). A scheduled group never shows the as-soon-as-possible expectation. While a store that cannot deliver now has no accepted window, the express wallet buttons stay off, like the card form. When checkout refuses a store that has scheduled delivery and also another delivery method (`multiple_fulfillment_options_per_store`, code 5516), the SDK shows the refusal as the checkout error and keeps the saved windows: "Scheduled delivery cannot be combined with another delivery method from the same store. Choose ASAP delivery, or use one delivery method for all items from this store." When that store is closed now, the text leaves out ASAP: "This store is closed now. Use one delivery method for all items from this store." When the refusal says whether the store is closed now (`closedNow` on the error entry), the SDK uses that. Otherwise, when Place Order is refused, the SDK reads the cart again and decides "closed now" from its `deliveryAvailability`, because a store can close after the last prepare. Place Order and the express wallet stay off until a prepare succeeds after a change to the cart or to a delivery choice.
917
919
 
918
920
  In the delivery-method switch, a same-day method whose `deliveryAvailability` says the store cannot deliver now but can schedule shows "Schedule delivery" in place of its delivery estimate. Without `deliveryAvailability` the card shows the estimate as before.
919
921
 
@@ -482,7 +482,7 @@ await window.LiquidCommerce.elements.actions.address.setAddressManually(
482
482
 
483
483
  ## Buying when a retailer is closed
484
484
 
485
- A same-day retailer whose store schedules delivery can take orders while it is closed. Its fulfillment then carries `deliveryAvailability`. When that says the store cannot deliver now but can schedule (`status: 'available'`, `canSchedule: true`), the retailer card stays on and shows "Schedule delivery". The product page shows no time: the server sends `nextSlot: null` for a closed store, and checkout asks the shopper to choose a delivery window. Add to Cart stays on. Screen readers hear "Columbus Avenue Wines, closed now, schedule delivery" in place of "Currently closed". When the server does send a `nextSlot`, the card adds it on a second line, in the shopper's own time zone ("Schedule delivery", then "Thu, 1 PM – 2 PM") and screen readers hear "…, next arrival Thu, 1 PM – 2 PM". The SDK selects a retailer that can deliver now first, then one that schedules delivery, and goes to shipping only when neither is available.
485
+ A same-day retailer whose store schedules delivery can take orders while it is closed. Its fulfillment then carries `deliveryAvailability`. When that says the store cannot deliver now but can schedule (`status: 'available'`, `canSchedule: true`), the retailer card stays on and shows "Schedule delivery". The product page shows no time: the server sends `nextSlot: null` for a closed store, and checkout asks the shopper to choose a delivery window. Add to Cart stays on. Screen readers hear "Columbus Avenue Wines, closed now, schedule delivery" in place of "Currently closed". When the server does send a `nextSlot`, the card adds it on a second line, in the store's time zone with its abbreviation ("Schedule delivery", then "Thu 10/8, 1–2 PM EDT") and screen readers hear "…, next arrival Thu 10/8, 1–2 PM EDT". The SDK selects a retailer that can deliver now first, then one that schedules delivery, and goes to shipping only when neither is available.
486
486
 
487
487
  A closed store without scheduled delivery is blocked as before. An unknown or unavailable `deliveryAvailability`, or a `nextSlot` whose order cutoff has passed, never turns purchase on.
488
488
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "LiquidCommerce Elements SDK",
4
4
  "license": "UNLICENSED",
5
5
  "author": "LiquidCommerce Team",
6
- "version": "2.6.0-beta.135",
6
+ "version": "2.6.0-beta.137",
7
7
  "homepage": "https://docs.liquidcommerce.co/elements-sdk",
8
8
  "repository": {
9
9
  "type": "git",