@aglyn/aglyn 1.0.0-beta.230 → 1.0.0-beta.232

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 (150) hide show
  1. package/package.json +11 -11
  2. package/src/lib/app-utils/account-acquisition.d.ts +15 -2
  3. package/src/lib/app-utils/account-acquisition.js +20 -1
  4. package/src/lib/app-utils/account-acquisition.js.map +1 -1
  5. package/src/lib/app-utils/artifact-list-queries.d.ts +35 -0
  6. package/src/lib/app-utils/artifact-list-queries.js +203 -0
  7. package/src/lib/app-utils/artifact-list-queries.js.map +1 -0
  8. package/src/lib/app-utils/business-profile.d.ts +171 -0
  9. package/src/lib/app-utils/business-profile.js +277 -0
  10. package/src/lib/app-utils/business-profile.js.map +1 -0
  11. package/src/lib/app-utils/child-contract.d.ts +4 -1
  12. package/src/lib/app-utils/child-contract.js +5 -2
  13. package/src/lib/app-utils/child-contract.js.map +1 -1
  14. package/src/lib/app-utils/console-routes.d.ts +5 -0
  15. package/src/lib/app-utils/console-routes.js +1 -0
  16. package/src/lib/app-utils/console-routes.js.map +1 -1
  17. package/src/lib/app-utils/content-schema-type.d.ts +9 -0
  18. package/src/lib/app-utils/content-schema-type.js +4 -0
  19. package/src/lib/app-utils/content-schema-type.js.map +1 -1
  20. package/src/lib/app-utils/crm.d.ts +14 -1
  21. package/src/lib/app-utils/crm.js +19 -2
  22. package/src/lib/app-utils/crm.js.map +1 -1
  23. package/src/lib/app-utils/docs-help.generated.d.ts +119 -5
  24. package/src/lib/app-utils/docs-help.generated.js +290 -2
  25. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  26. package/src/lib/app-utils/docs-index.generated.js +697 -32
  27. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  28. package/src/lib/app-utils/entry-list-declaration.d.ts +33 -0
  29. package/src/lib/app-utils/entry-list-declaration.js +172 -0
  30. package/src/lib/app-utils/entry-list-declaration.js.map +1 -0
  31. package/src/lib/app-utils/first-version-seed.d.ts +19 -0
  32. package/src/lib/app-utils/first-version-seed.js +53 -0
  33. package/src/lib/app-utils/first-version-seed.js.map +1 -0
  34. package/src/lib/app-utils/host-status.d.ts +85 -0
  35. package/src/lib/app-utils/host-status.js +115 -0
  36. package/src/lib/app-utils/host-status.js.map +1 -0
  37. package/src/lib/app-utils/local-business.d.ts +8 -0
  38. package/src/lib/app-utils/local-business.js +4 -0
  39. package/src/lib/app-utils/local-business.js.map +1 -1
  40. package/src/lib/app-utils/lockdown.js +1 -1
  41. package/src/lib/app-utils/lockdown.js.map +1 -1
  42. package/src/lib/app-utils/media-filter.d.ts +136 -0
  43. package/src/lib/app-utils/media-filter.js +400 -0
  44. package/src/lib/app-utils/media-filter.js.map +1 -0
  45. package/src/lib/app-utils/mobile-push.d.ts +98 -0
  46. package/src/lib/app-utils/mobile-push.js +97 -0
  47. package/src/lib/app-utils/mobile-push.js.map +1 -0
  48. package/src/lib/app-utils/notification-push.d.ts +38 -0
  49. package/src/lib/app-utils/notification-push.js +54 -0
  50. package/src/lib/app-utils/notification-push.js.map +1 -0
  51. package/src/lib/app-utils/notifications.d.ts +7 -0
  52. package/src/lib/app-utils/notifications.js.map +1 -1
  53. package/src/lib/app-utils/organizations.js +5 -2
  54. package/src/lib/app-utils/organizations.js.map +1 -1
  55. package/src/lib/app-utils/page-markdown.js +3 -0
  56. package/src/lib/app-utils/page-markdown.js.map +1 -1
  57. package/src/lib/app-utils/plugin-host-events.generated.d.ts +1 -1
  58. package/src/lib/app-utils/plugin-host-events.generated.js +149 -0
  59. package/src/lib/app-utils/plugin-host-events.generated.js.map +1 -1
  60. package/src/lib/app-utils/plugin-release-flags.generated.d.ts +1 -1
  61. package/src/lib/app-utils/plugin-release-flags.generated.js +7 -0
  62. package/src/lib/app-utils/plugin-release-flags.generated.js.map +1 -1
  63. package/src/lib/app-utils/realm-host-surface.generated.js +3 -0
  64. package/src/lib/app-utils/realm-host-surface.generated.js.map +1 -1
  65. package/src/lib/app-utils/release-flags.js +6 -2
  66. package/src/lib/app-utils/release-flags.js.map +1 -1
  67. package/src/lib/app-utils/scope-tokens.d.ts +16 -1
  68. package/src/lib/app-utils/scope-tokens.js +15 -1
  69. package/src/lib/app-utils/scope-tokens.js.map +1 -1
  70. package/src/lib/app-utils/site-list-query.d.ts +47 -0
  71. package/src/lib/app-utils/site-list-query.js +142 -0
  72. package/src/lib/app-utils/site-list-query.js.map +1 -0
  73. package/src/lib/app-utils/site-wide-outbox.d.ts +95 -0
  74. package/src/lib/app-utils/site-wide-outbox.js +117 -0
  75. package/src/lib/app-utils/site-wide-outbox.js.map +1 -0
  76. package/src/lib/app-utils/transfer-launcher-context.d.ts +6 -0
  77. package/src/lib/app-utils/transfer-launcher-context.js.map +1 -1
  78. package/src/lib/app-utils/upload-inspection.js +7 -0
  79. package/src/lib/app-utils/upload-inspection.js.map +1 -1
  80. package/src/lib/app-utils/webhook-delivery.js +4 -1
  81. package/src/lib/app-utils/webhook-delivery.js.map +1 -1
  82. package/src/lib/foundation/definitions/org-billing.types.d.ts +8 -0
  83. package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
  84. package/src/lib/foundation/definitions/organization.types.d.ts +18 -7
  85. package/src/lib/foundation/definitions/organization.types.js.map +1 -1
  86. package/src/lib/foundation/definitions/write-deny-coverage.util.d.ts +4 -1
  87. package/src/lib/foundation/definitions/write-deny-coverage.util.js +12 -2
  88. package/src/lib/foundation/definitions/write-deny-coverage.util.js.map +1 -1
  89. package/src/lib/plugin-manager/enabled-plugins.js +4 -2
  90. package/src/lib/plugin-manager/enabled-plugins.js.map +1 -1
  91. package/src/lib/plugin-manager/feature-plugins.d.ts +107 -0
  92. package/src/lib/plugin-manager/feature-plugins.js +52 -0
  93. package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
  94. package/src/lib/plugin-manager/first-party-plugins.generated.js +326 -2
  95. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  96. package/src/lib/plugin-manager/plugin-channel-orders.d.ts +262 -0
  97. package/src/lib/plugin-manager/plugin-channel-orders.js +32 -0
  98. package/src/lib/plugin-manager/plugin-channel-orders.js.map +1 -0
  99. package/src/lib/plugin-manager/plugin-checkout-extras.d.ts +168 -0
  100. package/src/lib/plugin-manager/plugin-checkout-extras.js +172 -0
  101. package/src/lib/plugin-manager/plugin-checkout-extras.js.map +1 -0
  102. package/src/lib/plugin-manager/plugin-contributions.d.ts +7 -0
  103. package/src/lib/plugin-manager/plugin-contributions.js +1 -1
  104. package/src/lib/plugin-manager/plugin-contributions.js.map +1 -1
  105. package/src/lib/plugin-manager/plugin-domain-events.d.ts +138 -0
  106. package/src/lib/plugin-manager/plugin-domain-events.js +148 -0
  107. package/src/lib/plugin-manager/plugin-domain-events.js.map +1 -0
  108. package/src/lib/plugin-manager/plugin-events.d.ts +30 -0
  109. package/src/lib/plugin-manager/plugin-events.js +4 -0
  110. package/src/lib/plugin-manager/plugin-events.js.map +1 -1
  111. package/src/lib/plugin-manager/plugin-fulfillment-providers.d.ts +101 -0
  112. package/src/lib/plugin-manager/plugin-fulfillment-providers.js +83 -0
  113. package/src/lib/plugin-manager/plugin-fulfillment-providers.js.map +1 -0
  114. package/src/lib/plugin-manager/plugin-permissions.js +21 -5
  115. package/src/lib/plugin-manager/plugin-permissions.js.map +1 -1
  116. package/src/lib/plugin-manager/plugin-person-records.d.ts +90 -0
  117. package/src/lib/plugin-manager/plugin-person-records.js +26 -0
  118. package/src/lib/plugin-manager/plugin-person-records.js.map +1 -1
  119. package/src/lib/plugin-manager/plugin-product-catalog.d.ts +209 -0
  120. package/src/lib/plugin-manager/plugin-product-catalog.js +43 -0
  121. package/src/lib/plugin-manager/plugin-product-catalog.js.map +1 -0
  122. package/src/lib/plugin-manager/plugin-product-writer.d.ts +166 -0
  123. package/src/lib/plugin-manager/plugin-product-writer.js +31 -0
  124. package/src/lib/plugin-manager/plugin-product-writer.js.map +1 -0
  125. package/src/lib/plugin-manager/plugin-shipment-records.d.ts +210 -0
  126. package/src/lib/plugin-manager/plugin-shipment-records.js +63 -0
  127. package/src/lib/plugin-manager/plugin-shipment-records.js.map +1 -0
  128. package/src/lib/plugin-manager/plugin-shipping-rates.d.ts +151 -0
  129. package/src/lib/plugin-manager/plugin-shipping-rates.js +62 -0
  130. package/src/lib/plugin-manager/plugin-shipping-rates.js.map +1 -0
  131. package/src/lib/plugin-manager/plugin-sms-messaging.d.ts +103 -0
  132. package/src/lib/plugin-manager/plugin-sms-messaging.js +39 -0
  133. package/src/lib/plugin-manager/plugin-sms-messaging.js.map +1 -0
  134. package/src/lib/plugin-manager/plugin-stock-levels.d.ts +81 -0
  135. package/src/lib/plugin-manager/plugin-stock-levels.js +32 -0
  136. package/src/lib/plugin-manager/plugin-stock-levels.js.map +1 -0
  137. package/src/lib/plugin-manager/plugin-tax-profile.d.ts +154 -0
  138. package/src/lib/plugin-manager/plugin-tax-profile.js +56 -0
  139. package/src/lib/plugin-manager/plugin-tax-profile.js.map +1 -1
  140. package/src/lib/plugin-manager/plugin-theme-font-catalog.d.ts +59 -0
  141. package/src/lib/plugin-manager/plugin-theme-font-catalog.js +40 -0
  142. package/src/lib/plugin-manager/plugin-theme-font-catalog.js.map +1 -0
  143. package/src/lib/plugin-manager/plugin-theme-presets.d.ts +40 -0
  144. package/src/lib/plugin-manager/plugin-theme-presets.js +44 -0
  145. package/src/lib/plugin-manager/plugin-theme-presets.js.map +1 -0
  146. package/src/lib/plugin-manager/plugin-tracking-pages.d.ts +55 -0
  147. package/src/lib/plugin-manager/plugin-tracking-pages.js +76 -0
  148. package/src/lib/plugin-manager/plugin-tracking-pages.js.map +1 -0
  149. package/src/lib/plugin-manager/realm-host-aglyn.generated.js +3 -0
  150. package/src/lib/plugin-manager/realm-host-aglyn.generated.js.map +1 -1
@@ -0,0 +1,209 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * A site's sellable catalog, read whole and in pages by a plugin that does
19
+ * not own it (AGL-3637).
20
+ *
21
+ * A product feed, a marketplace listing sync and an ad catalog all need the
22
+ * same thing: every offer a store sells, with the facts a shopping channel
23
+ * asks for — price in the store's currency, stock, photos, identifiers,
24
+ * shipping. The plugin that keeps the products answers it here, so a reader
25
+ * never learns where products are stored, how variants are modeled or what a
26
+ * deleted product looks like.
27
+ *
28
+ * ## One OFFER per sellable configuration
29
+ *
30
+ * A product with one configuration is one offer whose `id` is the product's
31
+ * own id. A product with several (sizes, colors) is one offer per
32
+ * configuration, each with its own `id` and the product's id as `groupId`,
33
+ * which is what every shopping channel calls the variant group.
34
+ *
35
+ * ## Money is integer minor units in the store's currency
36
+ *
37
+ * `priceMinor` and every other amount are integers (cents for USD) in
38
+ * {@link CatalogStore.currency}. A reader formats them; it never assumes a
39
+ * currency.
40
+ *
41
+ * ## Pages, not a list
42
+ *
43
+ * A store may hold far more products than one read should load, so the
44
+ * catalog is walked in pages: `page()` answers up to about `limit` offers and
45
+ * a `nextCursor` to continue from, `null` on the last page. A cursor is the
46
+ * owner's own opaque string; a reader passes it back untouched.
47
+ *
48
+ * ## One owner
49
+ *
50
+ * A slot, like the tax profile: a site has one catalog, so a second plugin's
51
+ * registration is refused and the incumbent keeps serving. No owner
52
+ * registered is an ordinary answer — {@link pluginProductCatalog} returns
53
+ * `undefined` and a reader serves nothing.
54
+ */
55
+ /** The store an offer belongs to, as a channel needs it. */
56
+ export interface CatalogStore {
57
+ hostId: string;
58
+ /** The store's name, as its site calls itself. */
59
+ name: string;
60
+ /**
61
+ * The origin the store's pages are served on (`https://shop.example.com`),
62
+ * or `null` when the site has no address yet.
63
+ */
64
+ origin: string | null;
65
+ /** ISO 4217, upper case. Every amount in the catalog is in it. */
66
+ currency: string;
67
+ /**
68
+ * Whether `/products/{slug}` pages are served. When `false`, every offer's
69
+ * `path` answers 404, and a channel will refuse the offers.
70
+ */
71
+ productPagesServed: boolean;
72
+ /**
73
+ * Countries the store's checkout prices shipping to by a live carrier
74
+ * quote rather than its own table (ISO 3166-1 alpha-2, upper case). An
75
+ * offer carries no {@link CatalogShippingPrice} for them, because the price
76
+ * depends on the address; a channel that calculates carrier rates itself
77
+ * prices them from the offer's weight and dimensions.
78
+ */
79
+ carrierPricedCountries: string[];
80
+ }
81
+ export type CatalogAvailability = 'in_stock' | 'out_of_stock' | 'backorder';
82
+ export type CatalogCondition = 'new' | 'refurbished' | 'used';
83
+ export type CatalogProductKind = 'physical' | 'digital' | 'service';
84
+ /** What one offer costs to ship to one country, cheapest rate first chosen. */
85
+ export interface CatalogShippingPrice {
86
+ /** ISO 3166-1 alpha-2, upper case. */
87
+ country: string;
88
+ /** The rate's name as the shopper sees it at checkout. */
89
+ service: string;
90
+ priceMinor: number;
91
+ }
92
+ /** One sellable configuration of a product. */
93
+ export interface CatalogOffer {
94
+ /** Stable, unique within the store: the product id, or product and variant. */
95
+ id: string;
96
+ /** The product's id: the variant group every configuration shares. */
97
+ groupId: string;
98
+ /** Whether the product has more than one configuration. */
99
+ hasVariants: boolean;
100
+ productId: string;
101
+ variantId: string;
102
+ /**
103
+ * The merchant's own SKU for this configuration, as entered; absent when
104
+ * they entered none. What a marketplace matches its listing by (AGL-3638).
105
+ */
106
+ sku?: string;
107
+ /** The product's name. */
108
+ productName: string;
109
+ /** The product's name with this configuration's choices, e.g. `Tee — Red / M`. */
110
+ title: string;
111
+ /** Plain text: no markup. May be empty. */
112
+ description: string;
113
+ /** Site-relative path of the product page, e.g. `/products/tee`. */
114
+ path: string;
115
+ /** Absolute URL of the photo shown for this configuration. */
116
+ imageUrl?: string;
117
+ /** Absolute URLs of the product's other photos, in order. */
118
+ additionalImageUrls: string[];
119
+ /** What the shopper pays, before any sale: the regular price. */
120
+ priceMinor: number;
121
+ /** A lower price the offer is on sale for now; absent when not on sale. */
122
+ salePriceMinor?: number;
123
+ availability: CatalogAvailability;
124
+ /** Units in stock, or `null` when stock is not tracked. */
125
+ quantity: number | null;
126
+ kind: CatalogProductKind;
127
+ /** Whether the product is sold only as a recurring subscription. */
128
+ subscriptionOnly: boolean;
129
+ /** What the merchant entered for shopping channels; absent when they did not. */
130
+ condition?: CatalogCondition;
131
+ brand?: string;
132
+ /** GTIN (UPC, EAN, ISBN, JAN, ITF-14) as entered, digits only. */
133
+ gtin?: string;
134
+ mpn?: string;
135
+ /** A Google product taxonomy id or full path, as entered. */
136
+ googleProductCategory?: string;
137
+ /** The store's own category path, e.g. `Apparel > Shirts`. */
138
+ productType?: string;
139
+ /** The configuration's choices, by option name. */
140
+ options: Readonly<Record<string, string>>;
141
+ /** The choice of an option named like a color, when there is one. */
142
+ color?: string;
143
+ /** The choice of an option named like a size, when there is one. */
144
+ size?: string;
145
+ weightGrams?: number;
146
+ /** One unit's packed size, in centimeters, when the merchant entered it. */
147
+ dimensionsCm?: {
148
+ length: number;
149
+ width: number;
150
+ height: number;
151
+ };
152
+ /** What it costs to ship one unit, per country the store ships to. */
153
+ shipping: CatalogShippingPrice[];
154
+ /** When the product last changed, epoch ms; absent when unknown. */
155
+ updatedAtMs?: number;
156
+ }
157
+ export interface CatalogPage {
158
+ offers: CatalogOffer[];
159
+ /** Where the next page starts, or `null` when this is the last one. */
160
+ nextCursor: string | null;
161
+ }
162
+ export interface PluginProductCatalog {
163
+ /** The store, or `null` for a site that does not sell. */
164
+ store(hostId: string): Promise<CatalogStore | null>;
165
+ /**
166
+ * One page of the store's live, listed offers: never a draft, archived or
167
+ * deleted product. About `limit` offers — a product's configurations are
168
+ * never split across pages, so a page may run over by one product's worth.
169
+ */
170
+ page(request: {
171
+ hostId: string;
172
+ cursor?: string | null;
173
+ limit: number;
174
+ }): Promise<CatalogPage>;
175
+ }
176
+ export declare const PLUGIN_PRODUCT_CATALOG: import("./plugin-services").PluginServiceContract<PluginProductCatalog>;
177
+ /** Registers the store catalog. A second plugin's is refused, naming both. */
178
+ export declare function registerPluginProductCatalog(catalog: PluginProductCatalog, options?: {
179
+ pluginId?: string;
180
+ }): void;
181
+ /** The registered catalog, or `undefined` when no plugin sells. */
182
+ export declare function pluginProductCatalog(): PluginProductCatalog | undefined;
183
+ /**
184
+ * The plugin that publishes the catalog to shopping channels, as the
185
+ * catalog's owner hands it an address of its own (AGL-3637).
186
+ *
187
+ * Before any plugin published feeds, the plugin that keeps the products
188
+ * served one feed itself, and merchants pasted that address into their
189
+ * channel. The address stays the owner's — a request for it reaches the
190
+ * owner's routes — but the feed is written by whoever publishes the catalog
191
+ * now, so the owner answers it from here. Nobody registered is an ordinary
192
+ * answer: {@link pluginCatalogFeed} returns `undefined` and the owner
193
+ * answers 404.
194
+ */
195
+ export interface PluginCatalogFeed {
196
+ /**
197
+ * Answers a request for the owner's pre-existing feed address for the site
198
+ * `hostId`: the feed, a 404 when the site's publisher has retired the
199
+ * address or does not publish, or a 503 to try later.
200
+ */
201
+ serveLegacyFeed(request: Request, hostId: string): Promise<Response>;
202
+ }
203
+ export declare const PLUGIN_CATALOG_FEED: import("./plugin-services").PluginServiceContract<PluginCatalogFeed>;
204
+ /** Registers the catalog's feed publisher. A second plugin's is refused, naming both. */
205
+ export declare function registerPluginCatalogFeed(feed: PluginCatalogFeed, options?: {
206
+ pluginId?: string;
207
+ }): void;
208
+ /** The registered feed publisher, or `undefined` when no plugin publishes feeds. */
209
+ export declare function pluginCatalogFeed(): PluginCatalogFeed | undefined;
@@ -0,0 +1,43 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { definePluginServiceContract, registerPluginService, resolvePluginServices } from "./plugin-services.js";
18
+ export const PLUGIN_PRODUCT_CATALOG = definePluginServiceContract('core.product-catalog', {
19
+ multiple: false
20
+ });
21
+ /** Registers the store catalog. A second plugin's is refused, naming both. */ export function registerPluginProductCatalog(catalog, options) {
22
+ registerPluginService(PLUGIN_PRODUCT_CATALOG, catalog, _extends({}, (options == null ? void 0 : options.pluginId) ? {
23
+ pluginId: options.pluginId
24
+ } : {}));
25
+ }
26
+ /** The registered catalog, or `undefined` when no plugin sells. */ export function pluginProductCatalog() {
27
+ var _resolvePluginServices_;
28
+ return (_resolvePluginServices_ = resolvePluginServices(PLUGIN_PRODUCT_CATALOG)[0]) == null ? void 0 : _resolvePluginServices_.impl;
29
+ }
30
+ export const PLUGIN_CATALOG_FEED = definePluginServiceContract('core.catalog-feed', {
31
+ multiple: false
32
+ });
33
+ /** Registers the catalog's feed publisher. A second plugin's is refused, naming both. */ export function registerPluginCatalogFeed(feed, options) {
34
+ registerPluginService(PLUGIN_CATALOG_FEED, feed, _extends({}, (options == null ? void 0 : options.pluginId) ? {
35
+ pluginId: options.pluginId
36
+ } : {}));
37
+ }
38
+ /** The registered feed publisher, or `undefined` when no plugin publishes feeds. */ export function pluginCatalogFeed() {
39
+ var _resolvePluginServices_;
40
+ return (_resolvePluginServices_ = resolvePluginServices(PLUGIN_CATALOG_FEED)[0]) == null ? void 0 : _resolvePluginServices_.impl;
41
+ }
42
+
43
+ //# sourceMappingURL=plugin-product-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-product-catalog.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * A site's sellable catalog, read whole and in pages by a plugin that does\n * not own it (AGL-3637).\n *\n * A product feed, a marketplace listing sync and an ad catalog all need the\n * same thing: every offer a store sells, with the facts a shopping channel\n * asks for — price in the store's currency, stock, photos, identifiers,\n * shipping. The plugin that keeps the products answers it here, so a reader\n * never learns where products are stored, how variants are modeled or what a\n * deleted product looks like.\n *\n * ## One OFFER per sellable configuration\n *\n * A product with one configuration is one offer whose `id` is the product's\n * own id. A product with several (sizes, colors) is one offer per\n * configuration, each with its own `id` and the product's id as `groupId`,\n * which is what every shopping channel calls the variant group.\n *\n * ## Money is integer minor units in the store's currency\n *\n * `priceMinor` and every other amount are integers (cents for USD) in\n * {@link CatalogStore.currency}. A reader formats them; it never assumes a\n * currency.\n *\n * ## Pages, not a list\n *\n * A store may hold far more products than one read should load, so the\n * catalog is walked in pages: `page()` answers up to about `limit` offers and\n * a `nextCursor` to continue from, `null` on the last page. A cursor is the\n * owner's own opaque string; a reader passes it back untouched.\n *\n * ## One owner\n *\n * A slot, like the tax profile: a site has one catalog, so a second plugin's\n * registration is refused and the incumbent keeps serving. No owner\n * registered is an ordinary answer — {@link pluginProductCatalog} returns\n * `undefined` and a reader serves nothing.\n */\n\n/** The store an offer belongs to, as a channel needs it. */\nexport interface CatalogStore {\n hostId: string\n /** The store's name, as its site calls itself. */\n name: string\n /**\n * The origin the store's pages are served on (`https://shop.example.com`),\n * or `null` when the site has no address yet.\n */\n origin: string | null\n /** ISO 4217, upper case. Every amount in the catalog is in it. */\n currency: string\n /**\n * Whether `/products/{slug}` pages are served. When `false`, every offer's\n * `path` answers 404, and a channel will refuse the offers.\n */\n productPagesServed: boolean\n /**\n * Countries the store's checkout prices shipping to by a live carrier\n * quote rather than its own table (ISO 3166-1 alpha-2, upper case). An\n * offer carries no {@link CatalogShippingPrice} for them, because the price\n * depends on the address; a channel that calculates carrier rates itself\n * prices them from the offer's weight and dimensions.\n */\n carrierPricedCountries: string[]\n}\n\nexport type CatalogAvailability = 'in_stock' | 'out_of_stock' | 'backorder'\nexport type CatalogCondition = 'new' | 'refurbished' | 'used'\nexport type CatalogProductKind = 'physical' | 'digital' | 'service'\n\n/** What one offer costs to ship to one country, cheapest rate first chosen. */\nexport interface CatalogShippingPrice {\n /** ISO 3166-1 alpha-2, upper case. */\n country: string\n /** The rate's name as the shopper sees it at checkout. */\n service: string\n priceMinor: number\n}\n\n/** One sellable configuration of a product. */\nexport interface CatalogOffer {\n /** Stable, unique within the store: the product id, or product and variant. */\n id: string\n /** The product's id: the variant group every configuration shares. */\n groupId: string\n /** Whether the product has more than one configuration. */\n hasVariants: boolean\n productId: string\n variantId: string\n /**\n * The merchant's own SKU for this configuration, as entered; absent when\n * they entered none. What a marketplace matches its listing by (AGL-3638).\n */\n sku?: string\n /** The product's name. */\n productName: string\n /** The product's name with this configuration's choices, e.g. `Tee — Red / M`. */\n title: string\n /** Plain text: no markup. May be empty. */\n description: string\n /** Site-relative path of the product page, e.g. `/products/tee`. */\n path: string\n /** Absolute URL of the photo shown for this configuration. */\n imageUrl?: string\n /** Absolute URLs of the product's other photos, in order. */\n additionalImageUrls: string[]\n /** What the shopper pays, before any sale: the regular price. */\n priceMinor: number\n /** A lower price the offer is on sale for now; absent when not on sale. */\n salePriceMinor?: number\n availability: CatalogAvailability\n /** Units in stock, or `null` when stock is not tracked. */\n quantity: number | null\n kind: CatalogProductKind\n /** Whether the product is sold only as a recurring subscription. */\n subscriptionOnly: boolean\n /** What the merchant entered for shopping channels; absent when they did not. */\n condition?: CatalogCondition\n brand?: string\n /** GTIN (UPC, EAN, ISBN, JAN, ITF-14) as entered, digits only. */\n gtin?: string\n mpn?: string\n /** A Google product taxonomy id or full path, as entered. */\n googleProductCategory?: string\n /** The store's own category path, e.g. `Apparel > Shirts`. */\n productType?: string\n /** The configuration's choices, by option name. */\n options: Readonly<Record<string, string>>\n /** The choice of an option named like a color, when there is one. */\n color?: string\n /** The choice of an option named like a size, when there is one. */\n size?: string\n weightGrams?: number\n /** One unit's packed size, in centimeters, when the merchant entered it. */\n dimensionsCm?: { length: number; width: number; height: number }\n /** What it costs to ship one unit, per country the store ships to. */\n shipping: CatalogShippingPrice[]\n /** When the product last changed, epoch ms; absent when unknown. */\n updatedAtMs?: number\n}\n\nexport interface CatalogPage {\n offers: CatalogOffer[]\n /** Where the next page starts, or `null` when this is the last one. */\n nextCursor: string | null\n}\n\nexport interface PluginProductCatalog {\n /** The store, or `null` for a site that does not sell. */\n store(hostId: string): Promise<CatalogStore | null>\n /**\n * One page of the store's live, listed offers: never a draft, archived or\n * deleted product. About `limit` offers — a product's configurations are\n * never split across pages, so a page may run over by one product's worth.\n */\n page(request: { hostId: string; cursor?: string | null; limit: number }): Promise<CatalogPage>\n}\n\nexport const PLUGIN_PRODUCT_CATALOG = definePluginServiceContract<PluginProductCatalog>(\n 'core.product-catalog',\n { multiple: false },\n)\n\n/** Registers the store catalog. A second plugin's is refused, naming both. */\nexport function registerPluginProductCatalog(\n catalog: PluginProductCatalog,\n options?: { pluginId?: string },\n): void {\n registerPluginService(PLUGIN_PRODUCT_CATALOG, catalog, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n })\n}\n\n/** The registered catalog, or `undefined` when no plugin sells. */\nexport function pluginProductCatalog(): PluginProductCatalog | undefined {\n return resolvePluginServices(PLUGIN_PRODUCT_CATALOG)[0]?.impl\n}\n\n/**\n * The plugin that publishes the catalog to shopping channels, as the\n * catalog's owner hands it an address of its own (AGL-3637).\n *\n * Before any plugin published feeds, the plugin that keeps the products\n * served one feed itself, and merchants pasted that address into their\n * channel. The address stays the owner's — a request for it reaches the\n * owner's routes — but the feed is written by whoever publishes the catalog\n * now, so the owner answers it from here. Nobody registered is an ordinary\n * answer: {@link pluginCatalogFeed} returns `undefined` and the owner\n * answers 404.\n */\nexport interface PluginCatalogFeed {\n /**\n * Answers a request for the owner's pre-existing feed address for the site\n * `hostId`: the feed, a 404 when the site's publisher has retired the\n * address or does not publish, or a 503 to try later.\n */\n serveLegacyFeed(request: Request, hostId: string): Promise<Response>\n}\n\nexport const PLUGIN_CATALOG_FEED = definePluginServiceContract<PluginCatalogFeed>(\n 'core.catalog-feed',\n { multiple: false },\n)\n\n/** Registers the catalog's feed publisher. A second plugin's is refused, naming both. */\nexport function registerPluginCatalogFeed(\n feed: PluginCatalogFeed,\n options?: { pluginId?: string },\n): void {\n registerPluginService(PLUGIN_CATALOG_FEED, feed, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n })\n}\n\n/** The registered feed publisher, or `undefined` when no plugin publishes feeds. */\nexport function pluginCatalogFeed(): PluginCatalogFeed | undefined {\n return resolvePluginServices(PLUGIN_CATALOG_FEED)[0]?.impl\n}\n"],"names":["definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_PRODUCT_CATALOG","multiple","registerPluginProductCatalog","catalog","options","pluginId","pluginProductCatalog","impl","PLUGIN_CATALOG_FEED","registerPluginCatalogFeed","feed","pluginCatalogFeed"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AAgK1B,OAAO,MAAMC,yBAAyBH,4BACpC,wBACA;IAAEI,UAAU;AAAM,GACnB;AAED,4EAA4E,GAC5E,OAAO,SAASC,6BACdC,OAA6B,EAC7BC,OAA+B;IAE/BN,sBAAsBE,wBAAwBG,SAAS,aACjDC,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;QAAEA,UAAUD,QAAQC,QAAQ;IAAC,IAAI,CAAC;AAE9D;AAEA,iEAAiE,GACjE,OAAO,SAASC;QACPP;IAAP,QAAOA,0BAAAA,sBAAsBC,uBAAuB,CAAC,EAAE,qBAAhDD,wBAAkDQ,IAAI;AAC/D;AAuBA,OAAO,MAAMC,sBAAsBX,4BACjC,qBACA;IAAEI,UAAU;AAAM,GACnB;AAED,uFAAuF,GACvF,OAAO,SAASQ,0BACdC,IAAuB,EACvBN,OAA+B;IAE/BN,sBAAsBU,qBAAqBE,MAAM,aAC3CN,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;QAAEA,UAAUD,QAAQC,QAAQ;IAAC,IAAI,CAAC;AAE9D;AAEA,kFAAkF,GAClF,OAAO,SAASM;QACPZ;IAAP,QAAOA,0BAAAA,sBAAsBS,oBAAoB,CAAC,EAAE,qBAA7CT,wBAA+CQ,IAAI;AAC5D"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * Products that come from somewhere else, written by a plugin that does not
19
+ * keep the store's products (AGL-3641).
20
+ *
21
+ * A print-on-demand service, a dropshipping supplier and a fulfillment
22
+ * network each hold a catalog of their own, and a merchant who sells it wants
23
+ * those products in their store without typing them in again — then kept in
24
+ * step as the source changes what it offers. The plugin that keeps the
25
+ * products answers here, so the plugin that reads the source never learns
26
+ * how products are stored, how their addresses are chosen, how the plan's
27
+ * product allowance is counted or what the store's search keys are. Every
28
+ * write keeps the owner's own rules: a create counts against the allowance in
29
+ * the transaction that makes it, and an edit leaves an entry in the site's
30
+ * activity like any other.
31
+ *
32
+ * ## Variants by the caller's key
33
+ *
34
+ * The caller names every configuration by its own stable key — the source's
35
+ * variant id — and the owner answers with its own variant id for each, so an
36
+ * order line can later be traced back to what the source sells. On an
37
+ * update the caller passes back the variant ids it was given; a key with no
38
+ * id is a new configuration, and an existing variant whose id is not passed
39
+ * is one the source no longer offers, removed.
40
+ *
41
+ * ## Money is integer minor units in the store's currency
42
+ *
43
+ * `priceMinor` is an integer (cents for USD) in the currency the store sells
44
+ * in, the `currency` the store's catalog (`core.product-catalog`) names. The
45
+ * caller converts nothing: a source priced in another currency is the
46
+ * caller's to refuse.
47
+ *
48
+ * ## What an update leaves alone
49
+ *
50
+ * The merchant edits an imported product like any other. An update rewrites
51
+ * only what the caller names: prices when `prices` is set, the description
52
+ * and photos when `content` is set, and always the configurations and their
53
+ * availability, because those are what an order can be filled from.
54
+ *
55
+ * ## One owner
56
+ *
57
+ * A slot, like the catalog: a site's products have one keeper, so a second
58
+ * plugin's registration is refused and the incumbent keeps serving. No owner
59
+ * registered is an ordinary answer — {@link pluginProductWriter} returns
60
+ * `undefined` and a caller imports nothing.
61
+ *
62
+ * Import this module by its own subpath
63
+ * (`@aglyn/aglyn/plugin-manager/plugin-product-writer`); it is not in the
64
+ * barrel.
65
+ */
66
+ /** One axis of variation, e.g. `{ name: 'Size', values: ['S', 'M', 'L'] }`. */
67
+ export interface SourcedProductOption {
68
+ name: string;
69
+ values: string[];
70
+ }
71
+ /** One sellable configuration, as the source offers it. */
72
+ export interface SourcedProductVariant {
73
+ /** The source's own stable id for it. */
74
+ key: string;
75
+ /** The owner's id, when an earlier write returned one. */
76
+ variantId?: string;
77
+ /** Choices by option name; `{}` for a product with one configuration. */
78
+ options: Record<string, string>;
79
+ sku?: string;
80
+ /** What the shopper pays, integer minor units in the store's currency. */
81
+ priceMinor: number;
82
+ weightGrams?: number;
83
+ /** A photo the owner already holds (a media-library URL) for this choice. */
84
+ imageUrl?: string;
85
+ /**
86
+ * Whether the source can make or ship it now. An unavailable variant is
87
+ * kept, sold out, so a product page does not lose a choice for a day's
88
+ * shortage; a variant the source dropped is not passed at all.
89
+ */
90
+ available: boolean;
91
+ }
92
+ /** A product as the source offers it. */
93
+ export interface SourcedProduct {
94
+ /** The source's stable key for the product, e.g. `printful:123`. */
95
+ sourceKey: string;
96
+ name: string;
97
+ /** Plain text or simple HTML, as the source wrote it. */
98
+ description?: string;
99
+ /** Photos the owner already holds (media-library URLs), first is primary. */
100
+ mediaUrls: string[];
101
+ tags?: string[];
102
+ options: SourcedProductOption[];
103
+ variants: SourcedProductVariant[];
104
+ }
105
+ export interface SourcedProductWrite {
106
+ hostId: string;
107
+ product: SourcedProduct;
108
+ /** The owner's product id, when an earlier write returned one. */
109
+ productId?: string;
110
+ /** A new product is born a draft unless the caller says to list it. */
111
+ status?: 'draft' | 'active';
112
+ /** On an update: rewrite prices from the source. */
113
+ prices?: boolean;
114
+ /** On an update: rewrite the name, description and photos from the source. */
115
+ content?: boolean;
116
+ /**
117
+ * Whether a `productId` the store no longer has (the merchant deleted it)
118
+ * is made again. Default true; a background re-sync passes false and is
119
+ * answered `missing`, so a product the merchant deleted stays deleted.
120
+ */
121
+ recreate?: boolean;
122
+ /** The member the write is made for, for the site's activity. */
123
+ actorUid?: string;
124
+ }
125
+ export type SourcedProductWriteOutcome = {
126
+ outcome: 'created' | 'updated' | 'unchanged';
127
+ productId: string;
128
+ /** Every variant passed, with the owner's id for it. */
129
+ variants: Array<{
130
+ key: string;
131
+ variantId: string;
132
+ }>;
133
+ }
134
+ /** The plan's product allowance is used up; nothing was written. */
135
+ | {
136
+ outcome: 'plan_limit';
137
+ limit: number;
138
+ }
139
+ /** The owner's rules refused the product; nothing was written. */
140
+ | {
141
+ outcome: 'invalid';
142
+ message: string;
143
+ }
144
+ /** The site does not sell. */
145
+ | {
146
+ outcome: 'no_store';
147
+ }
148
+ /** `productId` is gone and `recreate` was false; nothing was written. */
149
+ | {
150
+ outcome: 'missing';
151
+ };
152
+ export interface PluginProductWriter {
153
+ /**
154
+ * Creates the product, or updates the one `productId` names. A `productId`
155
+ * the store no longer has (the merchant deleted it) creates a new one,
156
+ * unless `recreate` is false.
157
+ */
158
+ upsertSourced(write: SourcedProductWrite): Promise<SourcedProductWriteOutcome>;
159
+ }
160
+ export declare const PLUGIN_PRODUCT_WRITER: import("./plugin-services").PluginServiceContract<PluginProductWriter>;
161
+ /** Registers the products' keeper. A second plugin's is refused, naming both. */
162
+ export declare function registerPluginProductWriter(writer: PluginProductWriter, options?: {
163
+ pluginId?: string;
164
+ }): void;
165
+ /** The registered keeper, or `undefined` when no plugin keeps products. */
166
+ export declare function pluginProductWriter(): PluginProductWriter | undefined;
@@ -0,0 +1,31 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { definePluginServiceContract, registerPluginService, resolvePluginServices } from "./plugin-services.js";
18
+ export const PLUGIN_PRODUCT_WRITER = definePluginServiceContract('core.product-writer', {
19
+ multiple: false
20
+ });
21
+ /** Registers the products' keeper. A second plugin's is refused, naming both. */ export function registerPluginProductWriter(writer, options) {
22
+ registerPluginService(PLUGIN_PRODUCT_WRITER, writer, _extends({}, (options == null ? void 0 : options.pluginId) ? {
23
+ pluginId: options.pluginId
24
+ } : {}));
25
+ }
26
+ /** The registered keeper, or `undefined` when no plugin keeps products. */ export function pluginProductWriter() {
27
+ var _resolvePluginServices_;
28
+ return (_resolvePluginServices_ = resolvePluginServices(PLUGIN_PRODUCT_WRITER)[0]) == null ? void 0 : _resolvePluginServices_.impl;
29
+ }
30
+
31
+ //# sourceMappingURL=plugin-product-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-product-writer.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * Products that come from somewhere else, written by a plugin that does not\n * keep the store's products (AGL-3641).\n *\n * A print-on-demand service, a dropshipping supplier and a fulfillment\n * network each hold a catalog of their own, and a merchant who sells it wants\n * those products in their store without typing them in again — then kept in\n * step as the source changes what it offers. The plugin that keeps the\n * products answers here, so the plugin that reads the source never learns\n * how products are stored, how their addresses are chosen, how the plan's\n * product allowance is counted or what the store's search keys are. Every\n * write keeps the owner's own rules: a create counts against the allowance in\n * the transaction that makes it, and an edit leaves an entry in the site's\n * activity like any other.\n *\n * ## Variants by the caller's key\n *\n * The caller names every configuration by its own stable key — the source's\n * variant id — and the owner answers with its own variant id for each, so an\n * order line can later be traced back to what the source sells. On an\n * update the caller passes back the variant ids it was given; a key with no\n * id is a new configuration, and an existing variant whose id is not passed\n * is one the source no longer offers, removed.\n *\n * ## Money is integer minor units in the store's currency\n *\n * `priceMinor` is an integer (cents for USD) in the currency the store sells\n * in, the `currency` the store's catalog (`core.product-catalog`) names. The\n * caller converts nothing: a source priced in another currency is the\n * caller's to refuse.\n *\n * ## What an update leaves alone\n *\n * The merchant edits an imported product like any other. An update rewrites\n * only what the caller names: prices when `prices` is set, the description\n * and photos when `content` is set, and always the configurations and their\n * availability, because those are what an order can be filled from.\n *\n * ## One owner\n *\n * A slot, like the catalog: a site's products have one keeper, so a second\n * plugin's registration is refused and the incumbent keeps serving. No owner\n * registered is an ordinary answer — {@link pluginProductWriter} returns\n * `undefined` and a caller imports nothing.\n *\n * Import this module by its own subpath\n * (`@aglyn/aglyn/plugin-manager/plugin-product-writer`); it is not in the\n * barrel.\n */\n\n/** One axis of variation, e.g. `{ name: 'Size', values: ['S', 'M', 'L'] }`. */\nexport interface SourcedProductOption {\n name: string\n values: string[]\n}\n\n/** One sellable configuration, as the source offers it. */\nexport interface SourcedProductVariant {\n /** The source's own stable id for it. */\n key: string\n /** The owner's id, when an earlier write returned one. */\n variantId?: string\n /** Choices by option name; `{}` for a product with one configuration. */\n options: Record<string, string>\n sku?: string\n /** What the shopper pays, integer minor units in the store's currency. */\n priceMinor: number\n weightGrams?: number\n /** A photo the owner already holds (a media-library URL) for this choice. */\n imageUrl?: string\n /**\n * Whether the source can make or ship it now. An unavailable variant is\n * kept, sold out, so a product page does not lose a choice for a day's\n * shortage; a variant the source dropped is not passed at all.\n */\n available: boolean\n}\n\n/** A product as the source offers it. */\nexport interface SourcedProduct {\n /** The source's stable key for the product, e.g. `printful:123`. */\n sourceKey: string\n name: string\n /** Plain text or simple HTML, as the source wrote it. */\n description?: string\n /** Photos the owner already holds (media-library URLs), first is primary. */\n mediaUrls: string[]\n tags?: string[]\n options: SourcedProductOption[]\n variants: SourcedProductVariant[]\n}\n\nexport interface SourcedProductWrite {\n hostId: string\n product: SourcedProduct\n /** The owner's product id, when an earlier write returned one. */\n productId?: string\n /** A new product is born a draft unless the caller says to list it. */\n status?: 'draft' | 'active'\n /** On an update: rewrite prices from the source. */\n prices?: boolean\n /** On an update: rewrite the name, description and photos from the source. */\n content?: boolean\n /**\n * Whether a `productId` the store no longer has (the merchant deleted it)\n * is made again. Default true; a background re-sync passes false and is\n * answered `missing`, so a product the merchant deleted stays deleted.\n */\n recreate?: boolean\n /** The member the write is made for, for the site's activity. */\n actorUid?: string\n}\n\nexport type SourcedProductWriteOutcome =\n | {\n outcome: 'created' | 'updated' | 'unchanged'\n productId: string\n /** Every variant passed, with the owner's id for it. */\n variants: Array<{ key: string; variantId: string }>\n }\n /** The plan's product allowance is used up; nothing was written. */\n | { outcome: 'plan_limit'; limit: number }\n /** The owner's rules refused the product; nothing was written. */\n | { outcome: 'invalid'; message: string }\n /** The site does not sell. */\n | { outcome: 'no_store' }\n /** `productId` is gone and `recreate` was false; nothing was written. */\n | { outcome: 'missing' }\n\nexport interface PluginProductWriter {\n /**\n * Creates the product, or updates the one `productId` names. A `productId`\n * the store no longer has (the merchant deleted it) creates a new one,\n * unless `recreate` is false.\n */\n upsertSourced(write: SourcedProductWrite): Promise<SourcedProductWriteOutcome>\n}\n\nexport const PLUGIN_PRODUCT_WRITER = definePluginServiceContract<PluginProductWriter>(\n 'core.product-writer',\n { multiple: false },\n)\n\n/** Registers the products' keeper. A second plugin's is refused, naming both. */\nexport function registerPluginProductWriter(\n writer: PluginProductWriter,\n options?: { pluginId?: string },\n): void {\n registerPluginService(PLUGIN_PRODUCT_WRITER, writer, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n })\n}\n\n/** The registered keeper, or `undefined` when no plugin keeps products. */\nexport function pluginProductWriter(): PluginProductWriter | undefined {\n return resolvePluginServices(PLUGIN_PRODUCT_WRITER)[0]?.impl\n}\n"],"names":["definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_PRODUCT_WRITER","multiple","registerPluginProductWriter","writer","options","pluginId","pluginProductWriter","impl"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AA4I1B,OAAO,MAAMC,wBAAwBH,4BACnC,uBACA;IAAEI,UAAU;AAAM,GACnB;AAED,+EAA+E,GAC/E,OAAO,SAASC,4BACdC,MAA2B,EAC3BC,OAA+B;IAE/BN,sBAAsBE,uBAAuBG,QAAQ,aAC/CC,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;QAAEA,UAAUD,QAAQC,QAAQ;IAAC,IAAI,CAAC;AAE9D;AAEA,yEAAyE,GACzE,OAAO,SAASC;QACPP;IAAP,QAAOA,0BAAAA,sBAAsBC,sBAAsB,CAAC,EAAE,qBAA/CD,wBAAiDQ,IAAI;AAC9D"}