feeef 0.12.10 → 0.12.12

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/build/index.js CHANGED
@@ -3074,6 +3074,7 @@ var OrderStatus = /* @__PURE__ */ ((OrderStatus2) => {
3074
3074
  OrderStatus2["pending"] = "pending";
3075
3075
  OrderStatus2["review"] = "review";
3076
3076
  OrderStatus2["accepted"] = "accepted";
3077
+ OrderStatus2["followup"] = "followup";
3077
3078
  OrderStatus2["processing"] = "processing";
3078
3079
  OrderStatus2["completed"] = "completed";
3079
3080
  OrderStatus2["cancelled"] = "cancelled";
@@ -4822,8 +4823,7 @@ var FeeeF = class {
4822
4823
  * @param {AxiosInstance} config.client - The Axios instance used for making HTTP requests.
4823
4824
  * @param {boolean | number} config.cache - The caching configuration. Set to `false` to disable caching, or provide a number to set the cache TTL in milliseconds.
4824
4825
  */
4825
- constructor({ apiKey, client, cache, baseURL = "http://localhost:3333/api/v1" }) {
4826
- console.log("feeef super cache", cache);
4826
+ constructor({ apiKey, client, baseURL = "http://localhost:3333/api/v1" }) {
4827
4827
  this.apiKey = apiKey;
4828
4828
  this.client = client || axios;
4829
4829
  this.client.defaults.headers.common["Authorization"] = `Bearer ${this.apiKey}`;
@@ -4878,10 +4878,43 @@ var FeeeF = class {
4878
4878
  }
4879
4879
  };
4880
4880
 
4881
+ // src/core/oauth_scopes.ts
4882
+ var MEMBER_SCOPES = [
4883
+ "store",
4884
+ "store.read",
4885
+ "store.settings",
4886
+ "store.integrations",
4887
+ "store.members",
4888
+ "orders",
4889
+ "orders.read",
4890
+ "products",
4891
+ "products.read",
4892
+ "categories",
4893
+ "categories.read",
4894
+ "pages",
4895
+ "pages.read",
4896
+ "product_landing_pages",
4897
+ "product_landing_pages.read",
4898
+ "shipping_prices",
4899
+ "shipping_prices.read",
4900
+ "template_components",
4901
+ "template_components.read",
4902
+ "store_templates",
4903
+ "store_templates.read",
4904
+ "finance",
4905
+ "finance.read",
4906
+ "inventory",
4907
+ "inventory.read"
4908
+ ];
4909
+ var OAUTH_PLATFORM_SCOPES = ["auth", "apps"];
4910
+ var DEFAULT_OAUTH_SCOPES = ["auth"];
4911
+ var OAUTH_SCOPES = [...MEMBER_SCOPES, ...OAUTH_PLATFORM_SCOPES];
4912
+
4881
4913
  // src/core/entities/store.ts
4882
- var generatePublicStoreIntegrations = (integrations) => {
4914
+ var generatePublicStoreIntegrations = (integrations, configs) => {
4883
4915
  if (!integrations) return null;
4884
4916
  const {
4917
+ meta,
4885
4918
  metaPixel,
4886
4919
  tiktokPixel,
4887
4920
  googleAnalytics,
@@ -4893,9 +4926,11 @@ var generatePublicStoreIntegrations = (integrations) => {
4893
4926
  security,
4894
4927
  customFields,
4895
4928
  payment,
4896
- connectors
4929
+ connectors,
4930
+ inventory
4897
4931
  } = integrations;
4898
4932
  return {
4933
+ meta: generatePublicStoreIntegrationMeta(meta),
4899
4934
  metaPixel: generatePublicStoreIntegrationMetaPixel(metaPixel) || null,
4900
4935
  tiktokPixel: generatePublicStoreIntegrationTiktokPixel(tiktokPixel) || null,
4901
4936
  googleAnalytics: generatePublicStoreIntegrationGoogleAnalytics(googleAnalytics) || null,
@@ -4908,7 +4943,17 @@ var generatePublicStoreIntegrations = (integrations) => {
4908
4943
  security: generatePublicStoreIntegrationSecurity(security) || null,
4909
4944
  customFields: generatePublicStoreIntegrationCustomFields(customFields) || null,
4910
4945
  payment: generatePublicStoreIntegrationPayment(payment) || null,
4911
- connectors: generatePublicStoreIntegrationConnectors(connectors) || null
4946
+ connectors: generatePublicStoreIntegrationConnectors(connectors) || null,
4947
+ inventory: generatePublicStoreIntegrationInventory(inventory, {
4948
+ showUnavailableOnFrontend: configs?.inventory_integration?.show_unavailable_on_frontend === true
4949
+ }) || null
4950
+ };
4951
+ };
4952
+ var generatePublicStoreIntegrationInventory = (inventory, options) => {
4953
+ if (!inventory) return null;
4954
+ return {
4955
+ active: inventory.active === true,
4956
+ showUnavailableOnFrontend: options?.showUnavailableOnFrontend === true
4912
4957
  };
4913
4958
  };
4914
4959
  var generatePublicStoreIntegrationConnectors = (connectors) => {
@@ -5138,6 +5183,13 @@ var PixelStatusDimension = /* @__PURE__ */ ((PixelStatusDimension2) => {
5138
5183
  PixelStatusDimension2["customStatus"] = "customStatus";
5139
5184
  return PixelStatusDimension2;
5140
5185
  })(PixelStatusDimension || {});
5186
+ var generatePublicStoreIntegrationMeta = (meta) => {
5187
+ if (!meta) return null;
5188
+ return {
5189
+ active: meta.active === true,
5190
+ ads: meta.ads ? { active: meta.ads.active === true } : null
5191
+ };
5192
+ };
5141
5193
  var SecurityTreatment = /* @__PURE__ */ ((SecurityTreatment2) => {
5142
5194
  SecurityTreatment2["block"] = "block";
5143
5195
  SecurityTreatment2["warning"] = "warning";
@@ -5148,6 +5200,9 @@ var WebhookEvent = /* @__PURE__ */ ((WebhookEvent2) => {
5148
5200
  WebhookEvent2["ORDER_CREATED"] = "orderCreated";
5149
5201
  WebhookEvent2["ORDER_UPDATED"] = "orderUpdated";
5150
5202
  WebhookEvent2["ORDER_DELETED"] = "orderDeleted";
5203
+ WebhookEvent2["PRODUCT_CREATED"] = "productCreated";
5204
+ WebhookEvent2["PRODUCT_UPDATED"] = "productUpdated";
5205
+ WebhookEvent2["PRODUCT_DELETED"] = "productDeleted";
5151
5206
  return WebhookEvent2;
5152
5207
  })(WebhookEvent || {});
5153
5208
  var StoreSubscriptionStatus = /* @__PURE__ */ ((StoreSubscriptionStatus2) => {
@@ -5474,6 +5529,10 @@ function roundMoney(amount, precision = 3) {
5474
5529
  const factor = 10 ** precision;
5475
5530
  return Math.round((amount + Number.EPSILON) * factor) / factor;
5476
5531
  }
5532
+ function safeNumber(value, fallback = 0) {
5533
+ const n = Number(value);
5534
+ return Number.isFinite(n) ? n : fallback;
5535
+ }
5477
5536
  function getLegacyAiBillingFlat(exchangeRate, resolved = mergeAiModelsBilling(null)) {
5478
5537
  const t = resolved.voiceGeneration.ttsTokenEstimate;
5479
5538
  return {
@@ -5508,76 +5567,22 @@ function getLegacyAiBillingFlat(exchangeRate, resolved = mergeAiModelsBilling(nu
5508
5567
  };
5509
5568
  }
5510
5569
  var AI_BILLING = getLegacyAiBillingFlat(FALLBACK_AI_EXCHANGE_RATE);
5511
- function findModel(models, modelId, fallbackId) {
5512
- return models.find((m) => m.id === modelId) || models.find((m) => m.id === fallbackId) || models[0];
5513
- }
5514
- function pickCatalogTokenPricingPerM(catalog, modelId) {
5515
- if (!catalog?.data?.length) return null;
5516
- const row = catalog.data.find((r) => r?.id === modelId);
5517
- const p = row?.pricing;
5518
- const promptPerToken = Number(p?.prompt);
5519
- const completionPerToken = Number(p?.completion);
5520
- if (!Number.isFinite(promptPerToken) && !Number.isFinite(completionPerToken)) return null;
5521
- const inTok = Number.isFinite(promptPerToken) ? promptPerToken : 0;
5522
- const outTok = Number.isFinite(completionPerToken) ? completionPerToken : 0;
5523
- if (inTok <= 0 && outTok <= 0) return null;
5524
- return { input: inTok * 1e6, output: outTok * 1e6 };
5525
- }
5526
- function modelHasVoiceCapability(model) {
5527
- const caps = model?.capabilities;
5528
- if (!Array.isArray(caps)) return false;
5529
- return caps.some((c) => c === "voice" || c === "audio");
5530
- }
5531
- function findVoiceModel(models, modelId, fallbackId) {
5532
- return models.find((m) => m.id === modelId) || models.find((m) => m.id === fallbackId) || models.find((m) => modelHasVoiceCapability(m));
5570
+ var DEFAULT_TEXT_PRICING_MODEL_ID = "gemini-flash-lite-latest";
5571
+ var DEFAULT_IMAGE_MODEL_ID = "gemini-3.1-flash-image-preview";
5572
+ var DEFAULT_TTS_MODEL_ID = "gemini-2.5-pro-preview-tts";
5573
+ function canonicalModelId(id) {
5574
+ return id.trim().toLowerCase().replace(/^models\//, "");
5533
5575
  }
5534
- function pickTtsProviderOutputUsd(model) {
5535
- if (!model?.pricing?.length) return null;
5536
- const voiceLike = modelHasVoiceCapability(model);
5537
- for (const unit of ["audio", "voice"]) {
5538
- const row = model.pricing.find((p) => p.unit === unit);
5539
- if (row?.output == null) continue;
5540
- const usd = row.output;
5541
- if (usd > 0) return usd;
5542
- }
5543
- if (voiceLike) {
5544
- const row = model.pricing.find((p) => p.unit === "image");
5545
- if (row?.output == null) return null;
5546
- const usd = row.output;
5547
- if (usd > 0) return usd;
5548
- }
5549
- return null;
5550
- }
5551
- function ttsTokenEstimatesFromResolved(b, scriptCharLength, attachmentCount) {
5552
- const t = b.voiceGeneration.ttsTokenEstimate;
5553
- let textTok = Math.round(scriptCharLength / 4);
5554
- if (textTok <= 0) {
5555
- textTok = attachmentCount > 0 ? t.whenAttachmentsOnlyTokens : t.whenScriptEmptyTokens;
5556
- }
5557
- const rawPrompt = t.promptBaseTokens + textTok + attachmentCount * t.promptPerAttachmentTokens;
5558
- const promptTokens = Math.min(t.maxTotalTokens, Math.max(0, rawPrompt));
5559
- const rawOutput = Math.max(t.outputMinimumTokens, Math.round(textTok * t.outputToTextTokenRatio));
5560
- const outputTokens = Math.min(t.maxTotalTokens, rawOutput);
5561
- return { promptTokens, outputTokens };
5562
- }
5563
- function defaultVoiceTtsTokenEstimates(scriptCharLength, attachmentCount) {
5564
- return ttsTokenEstimatesFromResolved(
5565
- mergeAiModelsBilling(null),
5566
- scriptCharLength,
5567
- attachmentCount
5568
- );
5576
+ function stripNamespace(id) {
5577
+ const trimmed = id.trim();
5578
+ const i = trimmed.indexOf("/");
5579
+ return i >= 0 ? trimmed.slice(i + 1) : trimmed;
5569
5580
  }
5570
- function pickVoiceTokenPricing(model, totalTokens) {
5571
- const tokenPricings = (model?.pricing ?? []).filter((p) => p.unit === "tokens");
5572
- if (!tokenPricings.length) return null;
5573
- const isLargeContext = totalTokens > 2e5;
5574
- const preferred = tokenPricings.find(
5575
- (p) => isLargeContext ? String(p.contextThreshold ?? "").includes(">") : String(p.contextThreshold ?? "").includes("<=")
5576
- ) || tokenPricings[0];
5577
- const input = preferred.input ?? 0;
5578
- const output = preferred.output ?? 0;
5579
- if (input <= 0 && output <= 0) return null;
5580
- return { input, output };
5581
+ function catalogRowId(row) {
5582
+ const r = row;
5583
+ if (typeof r.id === "string" && r.id.trim()) return r.id.trim();
5584
+ if (typeof r.slug === "string" && r.slug.trim()) return r.slug.trim();
5585
+ return "";
5581
5586
  }
5582
5587
  var AiCalculator = class {
5583
5588
  config;
@@ -5599,93 +5604,266 @@ var AiCalculator = class {
5599
5604
  billing
5600
5605
  };
5601
5606
  }
5602
- /** TTS base DZD: localCost → flat USD row → tokens (per 1M) × estimates × rate × multiplier → floor. */
5603
- _voiceoverBaseUserCostDzd(modelId, promptTokens, outputTokens) {
5604
- const { exchangeRate, billing, models } = this.config;
5605
- const b = billing;
5606
- const floorDzd = roundMoney(b.voiceGeneration.minimumChargeUsd * exchangeRate);
5607
- const model = findVoiceModel(models, modelId, "gemini-2.5-pro-preview-tts");
5608
- if (model?.localCost !== void 0 && model.localCost !== null) {
5609
- return { baseDzd: roundMoney(model.localCost), usedLocalCost: true };
5607
+ // -- lookup ---------------------------------------------------------------
5608
+ /** Exact-id legacy row (namespace tolerant). NO `models[0]` fallback. */
5609
+ findLegacyModel(modelId) {
5610
+ const id = modelId.trim();
5611
+ const bare = stripNamespace(id);
5612
+ return this.config.models.find((m) => m.id === id || m.id === bare);
5613
+ }
5614
+ findCatalogRow(modelId) {
5615
+ const rows = this.config.modelsCatalog?.data;
5616
+ const id = modelId.trim();
5617
+ if (!id || !rows?.length) return void 0;
5618
+ const canonical = canonicalModelId(id);
5619
+ return rows.find((r) => canonicalModelId(catalogRowId(r)) === canonical);
5620
+ }
5621
+ modelHasVoiceCapability(model) {
5622
+ const caps = model?.capabilities;
5623
+ if (!Array.isArray(caps)) return false;
5624
+ return caps.some((c) => c === "voice" || c === "audio");
5625
+ }
5626
+ // -- text pricing ----------------------------------------------------------
5627
+ /** Context-tier row from a legacy `tokens` pricing list (mirror backend). */
5628
+ pickLegacyTokenRow(model, totalTokens) {
5629
+ const rows = (model.pricing ?? []).filter((p) => p.unit === "tokens");
5630
+ if (!rows.length) return null;
5631
+ const isLargeContext = totalTokens > 2e5;
5632
+ const preferred = rows.find(
5633
+ (p) => isLargeContext ? String(p.contextThreshold ?? "").includes(">") : String(p.contextThreshold ?? "").includes("<=")
5634
+ ) ?? rows[0];
5635
+ const input = safeNumber(preferred.input);
5636
+ const output = safeNumber(preferred.output);
5637
+ if (input <= 0 && output <= 0) return null;
5638
+ return { input, output };
5639
+ }
5640
+ /** Catalog `pricing.prompt`/`completion` (USD per token) → USD per 1M. */
5641
+ pickCatalogTokenPricing(modelId) {
5642
+ const row = this.findCatalogRow(modelId);
5643
+ const pricing = row?.pricing;
5644
+ if (!pricing || typeof pricing !== "object") return null;
5645
+ const promptPerToken = safeNumber(pricing.prompt);
5646
+ const completionPerToken = safeNumber(pricing.completion);
5647
+ if (promptPerToken <= 0 && completionPerToken <= 0) return null;
5648
+ return { input: promptPerToken * 1e6, output: completionPerToken * 1e6 };
5649
+ }
5650
+ /**
5651
+ * USD-per-1M pricing: legacy exact-id → catalog → named default legacy row →
5652
+ * `null` (free). Mirrors backend `resolveTextTokenPricing`.
5653
+ */
5654
+ resolveTextTokenPricing(modelId, totalTokens) {
5655
+ const legacy = this.findLegacyModel(modelId);
5656
+ if (legacy) {
5657
+ const row = this.pickLegacyTokenRow(legacy, totalTokens);
5658
+ if (row) return row;
5610
5659
  }
5611
- const providerUsd = pickTtsProviderOutputUsd(model);
5612
- if (providerUsd != null) {
5613
- const providerDzd = providerUsd * exchangeRate;
5614
- return {
5615
- baseDzd: roundMoney(providerDzd * b.retailMarkup.multiplier),
5616
- usedLocalCost: false
5617
- };
5618
- }
5619
- const tokenPricing = pickVoiceTokenPricing(model, promptTokens + outputTokens);
5620
- if (tokenPricing) {
5621
- const providerCostUsd = promptTokens / 1e6 * tokenPricing.input + outputTokens / 1e6 * tokenPricing.output;
5622
- const providerDzd = providerCostUsd * exchangeRate;
5623
- return {
5624
- baseDzd: roundMoney(providerDzd * b.retailMarkup.multiplier),
5625
- usedLocalCost: false
5626
- };
5660
+ const catalog = this.pickCatalogTokenPricing(modelId);
5661
+ if (catalog) return catalog;
5662
+ const fallback = this.findLegacyModel(DEFAULT_TEXT_PRICING_MODEL_ID);
5663
+ if (fallback) {
5664
+ const row = this.pickLegacyTokenRow(fallback, totalTokens);
5665
+ if (row) return row;
5627
5666
  }
5628
- return { baseDzd: floorDzd, usedLocalCost: false };
5667
+ return null;
5629
5668
  }
5630
- _attachmentExtraUserDzd(attachmentCount, attachmentResolution) {
5669
+ // -- image pricing ----------------------------------------------------------
5670
+ /**
5671
+ * Catalog per-image USD, preferring the per-tier map
5672
+ * (`image_output_per_size_usd`: requested tier → 1K → 2K → 4K → first
5673
+ * positive), then flat `image_output`.
5674
+ */
5675
+ pickCatalogImageUsd(modelId, imageSize) {
5676
+ const row = this.findCatalogRow(modelId);
5677
+ const pricing = row?.pricing;
5678
+ if (!pricing || typeof pricing !== "object") return null;
5679
+ const perTier = pricing.image_output_per_size_usd ?? pricing.imageOutputPerSizeUsd;
5680
+ if (perTier && typeof perTier === "object") {
5681
+ const ordered = imageSize ? [imageSize, "1K", "2K", "4K"] : ["1K", "2K", "4K"];
5682
+ for (const key of ordered) {
5683
+ const v = safeNumber(perTier[key]);
5684
+ if (v > 0) return v;
5685
+ }
5686
+ for (const v of Object.values(perTier)) {
5687
+ const n = safeNumber(v);
5688
+ if (n > 0) return n;
5689
+ }
5690
+ }
5691
+ const flat = safeNumber(pricing.image_output ?? pricing.imageOutput);
5692
+ return flat > 0 ? flat : null;
5693
+ }
5694
+ /** Legacy exact-id `unit:'image'` row output (USD per image). */
5695
+ pickLegacyImageUsd(modelId) {
5696
+ const model = this.findLegacyModel(modelId);
5697
+ const row = (model?.pricing ?? []).find((p) => p.unit === "image");
5698
+ const usd = safeNumber(row?.output);
5699
+ return usd > 0 ? usd : null;
5700
+ }
5701
+ // -- shared retail add-ons ----------------------------------------------------
5702
+ /** Attachment surcharge (stores/products/audio context files) in retail DZD. */
5703
+ attachmentExtraUserDzd(attachmentCount, attachmentResolution) {
5631
5704
  if (attachmentCount <= 0) return 0;
5632
5705
  const { exchangeRate, billing } = this.config;
5633
5706
  const ref = billing.referenceAttachmentSurcharge;
5634
- const m = billing.retailMarkup.multiplier;
5635
- let attachCostUsd = attachmentCount * ref.perFileUsd;
5707
+ let usd = attachmentCount * ref.perFileUsd;
5636
5708
  if (attachmentResolution === "high") {
5637
- attachCostUsd += attachmentCount * ref.highResolutionExtraPerFileUsd;
5709
+ usd += attachmentCount * ref.highResolutionExtraPerFileUsd;
5638
5710
  } else if (attachmentResolution === "low") {
5639
- attachCostUsd -= attachmentCount * ref.lowResolutionDiscountPerFileUsd;
5711
+ usd -= attachmentCount * ref.lowResolutionDiscountPerFileUsd;
5712
+ }
5713
+ return roundMoney(usd * exchangeRate * billing.retailMarkup.multiplier);
5714
+ }
5715
+ /**
5716
+ * Flat DZD resolution extras — ONE rule, mirror of backend
5717
+ * `computeResolutionExtrasDzd`:
5718
+ * - output extra only when an output size tier is requested (1K→LOW,
5719
+ * 2K→MEDIUM, 4K→HIGH),
5720
+ * - input extra only when an explicit input resolution is requested
5721
+ * (`max(0, cost[resolution] − cost[LOW])`).
5722
+ */
5723
+ resolutionExtrasDzd(options) {
5724
+ const costs = this.config.resolutionCosts;
5725
+ let outputExtraDzd = 0;
5726
+ if (options.imageSize) {
5727
+ const key = options.imageSize === "4K" ? "MEDIA_RESOLUTION_HIGH" : options.imageSize === "2K" ? "MEDIA_RESOLUTION_MEDIUM" : "MEDIA_RESOLUTION_LOW";
5728
+ outputExtraDzd = roundMoney(safeNumber(costs[key]));
5729
+ }
5730
+ let inputExtraDzd = 0;
5731
+ if (options.resolution) {
5732
+ const low = safeNumber(costs.MEDIA_RESOLUTION_LOW);
5733
+ const tier = safeNumber(costs[options.resolution]);
5734
+ inputExtraDzd = roundMoney(Math.max(0, tier - low));
5735
+ }
5736
+ return { outputExtraDzd, inputExtraDzd, totalDzd: roundMoney(outputExtraDzd + inputExtraDzd) };
5737
+ }
5738
+ // -- voice pricing ------------------------------------------------------------
5739
+ /** Voice row: exact id → named TTS default → any voice-capable row. */
5740
+ findVoiceModel(modelId) {
5741
+ return this.findLegacyModel(modelId) ?? this.findLegacyModel(DEFAULT_TTS_MODEL_ID) ?? this.config.models.find((m) => this.modelHasVoiceCapability(m));
5742
+ }
5743
+ /**
5744
+ * Flat provider USD per TTS generation: `audio`/`voice` units; for
5745
+ * voice-capable rows also the `image` unit (legacy admin editor artifact).
5746
+ */
5747
+ pickTtsFlatUsd(model) {
5748
+ const rows = model.pricing ?? [];
5749
+ for (const unit of ["audio", "voice"]) {
5750
+ const row = rows.find((p) => p.unit === unit);
5751
+ const usd = safeNumber(row?.output);
5752
+ if (usd > 0) return usd;
5753
+ }
5754
+ if (this.modelHasVoiceCapability(model)) {
5755
+ const row = rows.find((p) => p.unit === "image");
5756
+ const usd = safeNumber(row?.output);
5757
+ if (usd > 0) return usd;
5758
+ }
5759
+ return null;
5760
+ }
5761
+ /**
5762
+ * Base TTS pricing (mirror backend `computeBaseVoiceoverBilling`):
5763
+ * `localCost` → flat USD → tokens per-1M → floor; non-localCost paths are
5764
+ * floored by `voiceGeneration.minimumChargeUsd`. Provider cost is honest
5765
+ * (0 when only a retail figure is known).
5766
+ */
5767
+ voiceoverBase(modelId, promptTokens, outputTokens) {
5768
+ const { exchangeRate, billing } = this.config;
5769
+ const floorDzd = roundMoney(billing.voiceGeneration.minimumChargeUsd * exchangeRate);
5770
+ const model = this.findVoiceModel(modelId);
5771
+ if (model?.localCost !== void 0 && model?.localCost !== null) {
5772
+ return {
5773
+ baseDzd: roundMoney(safeNumber(model.localCost)),
5774
+ providerCostUsd: 0,
5775
+ usedLocalCost: true
5776
+ };
5777
+ }
5778
+ if (model) {
5779
+ const flatUsd = this.pickTtsFlatUsd(model);
5780
+ if (flatUsd !== null) {
5781
+ return {
5782
+ baseDzd: Math.max(
5783
+ floorDzd,
5784
+ roundMoney(flatUsd * exchangeRate * billing.retailMarkup.multiplier)
5785
+ ),
5786
+ providerCostUsd: flatUsd,
5787
+ usedLocalCost: false
5788
+ };
5789
+ }
5790
+ const tokenRow = this.pickLegacyTokenRow(model, promptTokens + outputTokens);
5791
+ if (tokenRow) {
5792
+ const providerCostUsd = promptTokens / 1e6 * tokenRow.input + outputTokens / 1e6 * tokenRow.output;
5793
+ return {
5794
+ baseDzd: Math.max(
5795
+ floorDzd,
5796
+ roundMoney(providerCostUsd * exchangeRate * billing.retailMarkup.multiplier)
5797
+ ),
5798
+ providerCostUsd,
5799
+ usedLocalCost: false
5800
+ };
5801
+ }
5802
+ }
5803
+ return { baseDzd: floorDzd, providerCostUsd: 0, usedLocalCost: false };
5804
+ }
5805
+ ttsTokenEstimates(scriptCharLength, attachmentCount) {
5806
+ const t = this.config.billing.voiceGeneration.ttsTokenEstimate;
5807
+ let textTok = Math.round(scriptCharLength / 4);
5808
+ if (textTok <= 0) {
5809
+ textTok = attachmentCount > 0 ? t.whenAttachmentsOnlyTokens : t.whenScriptEmptyTokens;
5640
5810
  }
5641
- return roundMoney(attachCostUsd * exchangeRate * m);
5811
+ const promptTokens = Math.min(
5812
+ t.maxTotalTokens,
5813
+ Math.max(0, t.promptBaseTokens + textTok + attachmentCount * t.promptPerAttachmentTokens)
5814
+ );
5815
+ const outputTokens = Math.min(
5816
+ t.maxTotalTokens,
5817
+ Math.max(t.outputMinimumTokens, Math.round(textTok * t.outputToTextTokenRatio))
5818
+ );
5819
+ return { promptTokens, outputTokens };
5642
5820
  }
5821
+ // -- estimators -----------------------------------------------------------
5643
5822
  /**
5644
- * Estimate the cost of an image generation action.
5645
- * Covers: image gen, logo gen, editOrGenerateSimpleImage.
5823
+ * Image generation cost (image studio, logo studio, landing-page image).
5824
+ *
5825
+ * `userCostDzd = base(model, tier) × iterations + referenceImages ×
5826
+ * referenceImageCost + attachment surcharge + resolution extras + feature
5827
+ * add-ons`. Base precedence: catalog → legacy `unit:'image'` row →
5828
+ * `defaultImageCost` floor; legacy `localCost` overrides retail.
5646
5829
  */
5647
5830
  estimateImageGeneration(options = {}) {
5648
5831
  const {
5649
- modelId = "gemini-3.1-flash-image-preview",
5832
+ modelId = DEFAULT_IMAGE_MODEL_ID,
5650
5833
  attachmentCount = 0,
5651
5834
  attachmentResolution = "medium",
5652
5835
  resolution,
5653
5836
  imageSize,
5654
5837
  iterations = 1,
5655
- referenceImageCount = 0
5838
+ referenceImageCount = 0,
5839
+ featureAddonsDzd = 0
5656
5840
  } = options;
5657
- const model = findModel(this.config.models, modelId, "gemini-3.1-flash-image-preview");
5658
- const { exchangeRate, defaultImageCost, billing } = this.config;
5841
+ const { exchangeRate, billing } = this.config;
5659
5842
  const mult = billing.retailMarkup.multiplier;
5660
- const localCost = model?.localCost;
5661
- const imagePricing = model?.pricing?.find((p) => p.unit === "image");
5662
- const providerCostUsd = imagePricing?.output ? imagePricing.output : defaultImageCost / exchangeRate;
5663
- const providerCostDzd = imagePricing?.output ? providerCostUsd * exchangeRate : defaultImageCost;
5664
- const computedUserCostDzd = providerCostDzd * mult;
5843
+ const catalogUsd = this.pickCatalogImageUsd(modelId, imageSize);
5844
+ const legacyUsd = catalogUsd === null ? this.pickLegacyImageUsd(modelId) : null;
5845
+ const providerCostUsdPerImage = catalogUsd ?? legacyUsd ?? this.config.defaultImageCost / exchangeRate;
5846
+ const providerCostDzdPerImage = providerCostUsdPerImage * exchangeRate;
5847
+ const localCost = this.findLegacyModel(modelId)?.localCost;
5665
5848
  const usedLocalCost = localCost !== void 0 && localCost !== null;
5666
- const basePerIteration = usedLocalCost ? localCost : roundMoney(computedUserCostDzd);
5667
- const baseCostDzd = roundMoney(basePerIteration * iterations);
5668
- const refExtraDzd = roundMoney(referenceImageCount * this.config.referenceImageCost);
5669
- const attachExtraDzd = this._attachmentExtraUserDzd(attachmentCount, attachmentResolution);
5670
- const supportsImageSize = modelId === "gemini-3.1-flash-image-preview";
5671
- let outputResKey = "MEDIA_RESOLUTION_HIGH";
5672
- if (supportsImageSize && imageSize) {
5673
- outputResKey = imageSize === "4K" ? "MEDIA_RESOLUTION_HIGH" : imageSize === "2K" ? "MEDIA_RESOLUTION_MEDIUM" : "MEDIA_RESOLUTION_LOW";
5674
- } else if (resolution) {
5675
- outputResKey = resolution;
5676
- }
5677
- const outputResExtraDzd = supportsImageSize ? roundMoney(this.config.resolutionCosts[outputResKey] ?? 0) : 0;
5678
- let referenceResolutionExtraDzd = 0;
5679
- if (resolution) {
5680
- const low = this.config.resolutionCosts.MEDIA_RESOLUTION_LOW ?? 0;
5681
- const tier = this.config.resolutionCosts[resolution] ?? 0;
5682
- referenceResolutionExtraDzd = roundMoney(Math.max(0, tier - low));
5683
- }
5684
- const resExtraDzd = roundMoney(outputResExtraDzd + referenceResolutionExtraDzd);
5685
- const userCostDzd = roundMoney(baseCostDzd + refExtraDzd + attachExtraDzd + resExtraDzd);
5849
+ const basePerIteration = usedLocalCost ? roundMoney(safeNumber(localCost)) : roundMoney(providerCostDzdPerImage * mult);
5850
+ const iter = Math.max(1, Math.floor(iterations));
5851
+ const baseCostDzd = roundMoney(basePerIteration * iter);
5852
+ const refExtraDzd = roundMoney(
5853
+ Math.max(0, referenceImageCount) * this.config.referenceImageCost
5854
+ );
5855
+ const attachExtraDzd = this.attachmentExtraUserDzd(
5856
+ Math.max(0, attachmentCount),
5857
+ attachmentResolution
5858
+ );
5859
+ const resExtras = this.resolutionExtrasDzd({ imageSize, resolution });
5860
+ const addonsDzd = roundMoney(Math.max(0, safeNumber(featureAddonsDzd)));
5861
+ const userCostDzd = roundMoney(
5862
+ baseCostDzd + refExtraDzd + attachExtraDzd + resExtras.totalDzd + addonsDzd
5863
+ );
5686
5864
  return {
5687
- providerCostUsd: providerCostUsd * iterations,
5688
- providerCostDzd: providerCostDzd * iterations,
5865
+ providerCostUsd: providerCostUsdPerImage * iter,
5866
+ providerCostDzd: providerCostDzdPerImage * iter,
5689
5867
  userCostDzd,
5690
5868
  exchangeRate,
5691
5869
  multiplier: mult,
@@ -5694,34 +5872,34 @@ var AiCalculator = class {
5694
5872
  baseCostDzd,
5695
5873
  referenceImageExtraDzd: refExtraDzd,
5696
5874
  attachmentExtraDzd: attachExtraDzd,
5697
- outputResolutionExtraDzd: outputResExtraDzd,
5698
- referenceResolutionExtraDzd,
5699
- resolutionExtraDzd: resExtraDzd,
5700
- iterations,
5875
+ outputResolutionExtraDzd: resExtras.outputExtraDzd,
5876
+ inputResolutionExtraDzd: resExtras.inputExtraDzd,
5877
+ resolutionExtraDzd: resExtras.totalDzd,
5878
+ featureAddonsDzd: addonsDzd,
5879
+ iterations: iter,
5701
5880
  referenceImageCount,
5702
5881
  attachmentCount
5703
5882
  }
5704
5883
  };
5705
5884
  }
5706
5885
  /**
5707
- * Estimate the cost of a text generation action.
5708
- * Covers: updateProductUsingAi, generateSimpleCode, generateCustomComponentCode.
5709
- * Uses estimated tokens (exact cost billed post-generation).
5886
+ * Text generation estimate (actual billing happens post-success from real
5887
+ * token usage on the backend). Free when the model is unpriced everywhere
5888
+ * or `promptTokens < freeTierMaxPromptTokens` (prompt tokens not total).
5710
5889
  */
5711
5890
  estimateTextGeneration(options = {}) {
5712
5891
  const tg = this.config.billing.textGeneration;
5713
5892
  const mult = this.config.billing.retailMarkup.multiplier;
5714
5893
  const {
5715
- modelId = "gemini-3-flash-preview",
5894
+ modelId = DEFAULT_TEXT_PRICING_MODEL_ID,
5716
5895
  estimatedPromptTokens = tg.estimatedPromptTokensDefault,
5717
5896
  estimatedOutputTokens = tg.estimatedOutputTokensDefault
5718
5897
  } = options;
5719
- const totalTokens = estimatedPromptTokens + estimatedOutputTokens;
5898
+ const promptTokens = Math.max(0, estimatedPromptTokens);
5899
+ const outputTokens = Math.max(0, estimatedOutputTokens);
5720
5900
  const { exchangeRate } = this.config;
5721
- const model = findModel(this.config.models, modelId, "gemini-3-flash-preview");
5722
- const tokenPricing = model?.pricing?.find((p) => p.unit === "tokens");
5723
- const catalogPricing = tokenPricing ? null : pickCatalogTokenPricingPerM(this.config.modelsCatalog, modelId);
5724
- if (!tokenPricing && !catalogPricing || totalTokens < tg.freeTierMaxPromptTokens) {
5901
+ const pricing = this.resolveTextTokenPricing(modelId, promptTokens + outputTokens);
5902
+ if (!pricing || promptTokens < tg.freeTierMaxPromptTokens) {
5725
5903
  return {
5726
5904
  providerCostUsd: 0,
5727
5905
  providerCostDzd: 0,
@@ -5730,15 +5908,13 @@ var AiCalculator = class {
5730
5908
  multiplier: mult,
5731
5909
  usedLocalCost: false,
5732
5910
  breakdown: {
5733
- estimatedPromptTokens,
5734
- estimatedOutputTokens,
5735
- isFree: true
5911
+ estimatedPromptTokens: promptTokens,
5912
+ estimatedOutputTokens: outputTokens,
5913
+ isFree: 1
5736
5914
  }
5737
5915
  };
5738
5916
  }
5739
- const inputPrice = tokenPricing?.input ?? catalogPricing?.input ?? 0;
5740
- const outputPrice = tokenPricing?.output ?? catalogPricing?.output ?? 0;
5741
- const providerCostUsd = estimatedPromptTokens / 1e6 * inputPrice + estimatedOutputTokens / 1e6 * outputPrice;
5917
+ const providerCostUsd = promptTokens / 1e6 * pricing.input + outputTokens / 1e6 * pricing.output;
5742
5918
  const providerCostDzd = providerCostUsd * exchangeRate;
5743
5919
  const userCostDzd = roundMoney(providerCostDzd * mult);
5744
5920
  return {
@@ -5749,48 +5925,42 @@ var AiCalculator = class {
5749
5925
  multiplier: mult,
5750
5926
  usedLocalCost: false,
5751
5927
  breakdown: {
5752
- estimatedPromptTokens,
5753
- estimatedOutputTokens,
5754
- isFree: false
5928
+ estimatedPromptTokens: promptTokens,
5929
+ estimatedOutputTokens: outputTokens,
5930
+ isFree: 0
5755
5931
  }
5756
5932
  };
5757
5933
  }
5758
5934
  /**
5759
- * Voiceover: base + attachment surcharge + optional script-enhancement add-on.
5935
+ * Voiceover estimate: TTS base (heuristic tokens unless explicitly given) +
5936
+ * attachment surcharge + optional script-enhancement add-on. The backend
5937
+ * settles from ACTUAL usage (`computeVoiceoverSettlement`); this preview
5938
+ * uses the same retail policy.
5760
5939
  */
5761
5940
  estimateVoiceover(options = {}) {
5762
- const modelId = options.modelId ?? "gemini-2.5-pro-preview-tts";
5763
- const attachmentCount = options.attachmentCount ?? 0;
5941
+ const modelId = options.modelId ?? DEFAULT_TTS_MODEL_ID;
5942
+ const attachmentCount = Math.max(0, options.attachmentCount ?? 0);
5764
5943
  const attachmentResolution = options.attachmentResolution ?? "medium";
5765
5944
  const enhanceScript = options.enhanceScript !== false;
5766
- const charLen = options.scriptCharLength ?? 0;
5767
- const b = this.config.billing;
5768
- const cap = b.voiceGeneration.ttsTokenEstimate.maxTotalTokens;
5769
- const tokenEst = (
5770
- // eslint-disable-next-line eqeqeq
5771
- options.estimatedPromptTokens != null && options.estimatedOutputTokens != null ? {
5772
- promptTokens: Math.max(0, Math.min(cap, options.estimatedPromptTokens)),
5773
- outputTokens: Math.max(0, Math.min(cap, options.estimatedOutputTokens))
5774
- } : ttsTokenEstimatesFromResolved(b, charLen, attachmentCount)
5775
- );
5776
- const { exchangeRate } = this.config;
5777
- const { baseDzd, usedLocalCost } = this._voiceoverBaseUserCostDzd(
5778
- modelId,
5779
- tokenEst.promptTokens,
5780
- tokenEst.outputTokens
5781
- );
5782
- const attachExtra = this._attachmentExtraUserDzd(attachmentCount, attachmentResolution);
5783
- const enhanceExtra = enhanceScript ? roundMoney(b.voiceGeneration.scriptEnhancementAddonUsd * exchangeRate) : 0;
5784
- const userCostDzd = roundMoney(baseDzd + attachExtra + enhanceExtra);
5945
+ const hasExplicitTokens = options.estimatedPromptTokens !== void 0 && options.estimatedPromptTokens !== null && options.estimatedOutputTokens !== void 0 && options.estimatedOutputTokens !== null;
5946
+ const tokenEst = hasExplicitTokens ? {
5947
+ promptTokens: Math.max(0, options.estimatedPromptTokens),
5948
+ outputTokens: Math.max(0, options.estimatedOutputTokens)
5949
+ } : this.ttsTokenEstimates(Math.max(0, options.scriptCharLength ?? 0), attachmentCount);
5950
+ const { exchangeRate, billing } = this.config;
5951
+ const base = this.voiceoverBase(modelId, tokenEst.promptTokens, tokenEst.outputTokens);
5952
+ const attachExtra = this.attachmentExtraUserDzd(attachmentCount, attachmentResolution);
5953
+ const enhanceExtra = enhanceScript ? roundMoney(billing.voiceGeneration.scriptEnhancementAddonUsd * exchangeRate) : 0;
5954
+ const userCostDzd = roundMoney(base.baseDzd + attachExtra + enhanceExtra);
5785
5955
  return {
5786
- providerCostUsd: userCostDzd / exchangeRate,
5787
- providerCostDzd: userCostDzd,
5956
+ providerCostUsd: base.providerCostUsd,
5957
+ providerCostDzd: base.providerCostUsd * exchangeRate,
5788
5958
  userCostDzd,
5789
5959
  exchangeRate,
5790
- multiplier: 1,
5791
- usedLocalCost,
5960
+ multiplier: billing.retailMarkup.multiplier,
5961
+ usedLocalCost: base.usedLocalCost,
5792
5962
  breakdown: {
5793
- fixedCostDzd: baseDzd,
5963
+ ttsBaseDzd: base.baseDzd,
5794
5964
  attachmentExtraDzd: attachExtra,
5795
5965
  enhanceAddonDzd: enhanceExtra,
5796
5966
  attachmentCount,
@@ -5799,21 +5969,61 @@ var AiCalculator = class {
5799
5969
  }
5800
5970
  };
5801
5971
  }
5802
- /** Get the fixed cost for image landing page generation. */
5803
- estimateImageLandingPage() {
5972
+ /**
5973
+ * Landing-page image cost — identical formula to [estimateImageGeneration]
5974
+ * (image-studio parity). Without an image model id, returns the fixed
5975
+ * `billing.landingPageImage` charge (provider cost unknown → 0). A ≤ 0
5976
+ * image quote also falls back to the fixed charge (zero-guard).
5977
+ */
5978
+ estimateImageLandingPage(options = {}) {
5804
5979
  const { exchangeRate, billing } = this.config;
5805
- const costDzd = roundMoney(billing.landingPageImage.fixedChargeUsd * exchangeRate);
5980
+ const imageModelId = options.imageModelId?.trim();
5981
+ if (!imageModelId) {
5982
+ const fixedDzd2 = roundMoney(billing.landingPageImage.fixedChargeUsd * exchangeRate);
5983
+ return {
5984
+ providerCostUsd: 0,
5985
+ providerCostDzd: 0,
5986
+ userCostDzd: fixedDzd2,
5987
+ exchangeRate,
5988
+ multiplier: billing.retailMarkup.multiplier,
5989
+ usedLocalCost: false,
5990
+ breakdown: { fixedCostDzd: fixedDzd2 }
5991
+ };
5992
+ }
5993
+ const img = this.estimateImageGeneration({
5994
+ modelId: imageModelId,
5995
+ attachmentCount: options.attachmentCount,
5996
+ attachmentResolution: options.attachmentResolution,
5997
+ resolution: options.resolution,
5998
+ imageSize: options.imageSize,
5999
+ referenceImageCount: options.referenceImageCount,
6000
+ featureAddonsDzd: options.featureAddonsDzd
6001
+ });
6002
+ if (img.userCostDzd > 0) return img;
6003
+ const fixedDzd = roundMoney(billing.landingPageImage.fixedChargeUsd * exchangeRate);
5806
6004
  return {
5807
- providerCostUsd: costDzd / exchangeRate,
5808
- providerCostDzd: costDzd,
5809
- userCostDzd: costDzd,
5810
- exchangeRate,
5811
- multiplier: 1,
5812
- usedLocalCost: false,
5813
- breakdown: { fixedCostDzd: costDzd }
6005
+ ...img,
6006
+ userCostDzd: fixedDzd,
6007
+ breakdown: { ...img.breakdown, fixedCostDzd: fixedDzd }
5814
6008
  };
5815
6009
  }
5816
6010
  };
6011
+ function defaultVoiceTtsTokenEstimates(scriptCharLength, attachmentCount) {
6012
+ const t = mergeAiModelsBilling(null).voiceGeneration.ttsTokenEstimate;
6013
+ let textTok = Math.round(scriptCharLength / 4);
6014
+ if (textTok <= 0) {
6015
+ textTok = attachmentCount > 0 ? t.whenAttachmentsOnlyTokens : t.whenScriptEmptyTokens;
6016
+ }
6017
+ const promptTokens = Math.min(
6018
+ t.maxTotalTokens,
6019
+ Math.max(0, t.promptBaseTokens + textTok + attachmentCount * t.promptPerAttachmentTokens)
6020
+ );
6021
+ const outputTokens = Math.min(
6022
+ t.maxTotalTokens,
6023
+ Math.max(t.outputMinimumTokens, Math.round(textTok * t.outputToTextTokenRatio))
6024
+ );
6025
+ return { promptTokens, outputTokens };
6026
+ }
5817
6027
 
5818
6028
  // src/utils.ts
5819
6029
  var convertDartColorToCssNumber = (dartColor) => {
@@ -5910,6 +6120,7 @@ export {
5910
6120
  CountryRepository,
5911
6121
  CurrencyRepository,
5912
6122
  CustomerPaymentRepository,
6123
+ DEFAULT_OAUTH_SCOPES,
5913
6124
  DeliveryServiceFilter,
5914
6125
  DeliveryStatus,
5915
6126
  DepositRepository,
@@ -5938,10 +6149,13 @@ export {
5938
6149
  InventoryRepository,
5939
6150
  InventoryReservationRepository,
5940
6151
  InventoryWarehouseRepository,
6152
+ MEMBER_SCOPES,
5941
6153
  MetaPixelEvent,
5942
6154
  ModelRepository,
5943
6155
  NoestDeliveryIntegrationApi,
5944
6156
  NotificationsService,
6157
+ OAUTH_PLATFORM_SCOPES,
6158
+ OAUTH_SCOPES,
5945
6159
  OAuthRepository,
5946
6160
  ORDER_FILTER_ANY,
5947
6161
  OrderRepository,
@@ -6021,6 +6235,8 @@ export {
6021
6235
  generatePublicStoreIntegrationGoogleAnalytics,
6022
6236
  generatePublicStoreIntegrationGoogleSheet,
6023
6237
  generatePublicStoreIntegrationGoogleTags,
6238
+ generatePublicStoreIntegrationInventory,
6239
+ generatePublicStoreIntegrationMeta,
6024
6240
  generatePublicStoreIntegrationMetaPixel,
6025
6241
  generatePublicStoreIntegrationOrderdz,
6026
6242
  generatePublicStoreIntegrationPayment,