feeef 0.12.11 → 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
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,
@@ -4897,6 +4930,7 @@ var generatePublicStoreIntegrations = (integrations, configs) => {
4897
4930
  inventory
4898
4931
  } = integrations;
4899
4932
  return {
4933
+ meta: generatePublicStoreIntegrationMeta(meta),
4900
4934
  metaPixel: generatePublicStoreIntegrationMetaPixel(metaPixel) || null,
4901
4935
  tiktokPixel: generatePublicStoreIntegrationTiktokPixel(tiktokPixel) || null,
4902
4936
  googleAnalytics: generatePublicStoreIntegrationGoogleAnalytics(googleAnalytics) || null,
@@ -5149,6 +5183,13 @@ var PixelStatusDimension = /* @__PURE__ */ ((PixelStatusDimension2) => {
5149
5183
  PixelStatusDimension2["customStatus"] = "customStatus";
5150
5184
  return PixelStatusDimension2;
5151
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
+ };
5152
5193
  var SecurityTreatment = /* @__PURE__ */ ((SecurityTreatment2) => {
5153
5194
  SecurityTreatment2["block"] = "block";
5154
5195
  SecurityTreatment2["warning"] = "warning";
@@ -5159,6 +5200,9 @@ var WebhookEvent = /* @__PURE__ */ ((WebhookEvent2) => {
5159
5200
  WebhookEvent2["ORDER_CREATED"] = "orderCreated";
5160
5201
  WebhookEvent2["ORDER_UPDATED"] = "orderUpdated";
5161
5202
  WebhookEvent2["ORDER_DELETED"] = "orderDeleted";
5203
+ WebhookEvent2["PRODUCT_CREATED"] = "productCreated";
5204
+ WebhookEvent2["PRODUCT_UPDATED"] = "productUpdated";
5205
+ WebhookEvent2["PRODUCT_DELETED"] = "productDeleted";
5162
5206
  return WebhookEvent2;
5163
5207
  })(WebhookEvent || {});
5164
5208
  var StoreSubscriptionStatus = /* @__PURE__ */ ((StoreSubscriptionStatus2) => {
@@ -5485,6 +5529,10 @@ function roundMoney(amount, precision = 3) {
5485
5529
  const factor = 10 ** precision;
5486
5530
  return Math.round((amount + Number.EPSILON) * factor) / factor;
5487
5531
  }
5532
+ function safeNumber(value, fallback = 0) {
5533
+ const n = Number(value);
5534
+ return Number.isFinite(n) ? n : fallback;
5535
+ }
5488
5536
  function getLegacyAiBillingFlat(exchangeRate, resolved = mergeAiModelsBilling(null)) {
5489
5537
  const t = resolved.voiceGeneration.ttsTokenEstimate;
5490
5538
  return {
@@ -5519,76 +5567,22 @@ function getLegacyAiBillingFlat(exchangeRate, resolved = mergeAiModelsBilling(nu
5519
5567
  };
5520
5568
  }
5521
5569
  var AI_BILLING = getLegacyAiBillingFlat(FALLBACK_AI_EXCHANGE_RATE);
5522
- function findModel(models, modelId, fallbackId) {
5523
- return models.find((m) => m.id === modelId) || models.find((m) => m.id === fallbackId) || models[0];
5524
- }
5525
- function pickCatalogTokenPricingPerM(catalog, modelId) {
5526
- if (!catalog?.data?.length) return null;
5527
- const row = catalog.data.find((r) => r?.id === modelId);
5528
- const p = row?.pricing;
5529
- const promptPerToken = Number(p?.prompt);
5530
- const completionPerToken = Number(p?.completion);
5531
- if (!Number.isFinite(promptPerToken) && !Number.isFinite(completionPerToken)) return null;
5532
- const inTok = Number.isFinite(promptPerToken) ? promptPerToken : 0;
5533
- const outTok = Number.isFinite(completionPerToken) ? completionPerToken : 0;
5534
- if (inTok <= 0 && outTok <= 0) return null;
5535
- return { input: inTok * 1e6, output: outTok * 1e6 };
5536
- }
5537
- function modelHasVoiceCapability(model) {
5538
- const caps = model?.capabilities;
5539
- if (!Array.isArray(caps)) return false;
5540
- return caps.some((c) => c === "voice" || c === "audio");
5541
- }
5542
- function findVoiceModel(models, modelId, fallbackId) {
5543
- return models.find((m) => m.id === modelId) || models.find((m) => m.id === fallbackId) || models.find((m) => modelHasVoiceCapability(m));
5544
- }
5545
- function pickTtsProviderOutputUsd(model) {
5546
- if (!model?.pricing?.length) return null;
5547
- const voiceLike = modelHasVoiceCapability(model);
5548
- for (const unit of ["audio", "voice"]) {
5549
- const row = model.pricing.find((p) => p.unit === unit);
5550
- if (row?.output == null) continue;
5551
- const usd = row.output;
5552
- if (usd > 0) return usd;
5553
- }
5554
- if (voiceLike) {
5555
- const row = model.pricing.find((p) => p.unit === "image");
5556
- if (row?.output == null) return null;
5557
- const usd = row.output;
5558
- if (usd > 0) return usd;
5559
- }
5560
- return null;
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\//, "");
5561
5575
  }
5562
- function ttsTokenEstimatesFromResolved(b, scriptCharLength, attachmentCount) {
5563
- const t = b.voiceGeneration.ttsTokenEstimate;
5564
- let textTok = Math.round(scriptCharLength / 4);
5565
- if (textTok <= 0) {
5566
- textTok = attachmentCount > 0 ? t.whenAttachmentsOnlyTokens : t.whenScriptEmptyTokens;
5567
- }
5568
- const rawPrompt = t.promptBaseTokens + textTok + attachmentCount * t.promptPerAttachmentTokens;
5569
- const promptTokens = Math.min(t.maxTotalTokens, Math.max(0, rawPrompt));
5570
- const rawOutput = Math.max(t.outputMinimumTokens, Math.round(textTok * t.outputToTextTokenRatio));
5571
- const outputTokens = Math.min(t.maxTotalTokens, rawOutput);
5572
- return { promptTokens, outputTokens };
5576
+ function stripNamespace(id) {
5577
+ const trimmed = id.trim();
5578
+ const i = trimmed.indexOf("/");
5579
+ return i >= 0 ? trimmed.slice(i + 1) : trimmed;
5573
5580
  }
5574
- function defaultVoiceTtsTokenEstimates(scriptCharLength, attachmentCount) {
5575
- return ttsTokenEstimatesFromResolved(
5576
- mergeAiModelsBilling(null),
5577
- scriptCharLength,
5578
- attachmentCount
5579
- );
5580
- }
5581
- function pickVoiceTokenPricing(model, totalTokens) {
5582
- const tokenPricings = (model?.pricing ?? []).filter((p) => p.unit === "tokens");
5583
- if (!tokenPricings.length) return null;
5584
- const isLargeContext = totalTokens > 2e5;
5585
- const preferred = tokenPricings.find(
5586
- (p) => isLargeContext ? String(p.contextThreshold ?? "").includes(">") : String(p.contextThreshold ?? "").includes("<=")
5587
- ) || tokenPricings[0];
5588
- const input = preferred.input ?? 0;
5589
- const output = preferred.output ?? 0;
5590
- if (input <= 0 && output <= 0) return null;
5591
- 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 "";
5592
5586
  }
5593
5587
  var AiCalculator = class {
5594
5588
  config;
@@ -5610,93 +5604,266 @@ var AiCalculator = class {
5610
5604
  billing
5611
5605
  };
5612
5606
  }
5613
- /** TTS base DZD: localCost → flat USD row → tokens (per 1M) × estimates × rate × multiplier → floor. */
5614
- _voiceoverBaseUserCostDzd(modelId, promptTokens, outputTokens) {
5615
- const { exchangeRate, billing, models } = this.config;
5616
- const b = billing;
5617
- const floorDzd = roundMoney(b.voiceGeneration.minimumChargeUsd * exchangeRate);
5618
- const model = findVoiceModel(models, modelId, "gemini-2.5-pro-preview-tts");
5619
- if (model?.localCost !== void 0 && model.localCost !== null) {
5620
- return { baseDzd: roundMoney(model.localCost), usedLocalCost: true };
5621
- }
5622
- const providerUsd = pickTtsProviderOutputUsd(model);
5623
- if (providerUsd != null) {
5624
- const providerDzd = providerUsd * exchangeRate;
5625
- return {
5626
- baseDzd: roundMoney(providerDzd * b.retailMarkup.multiplier),
5627
- usedLocalCost: false
5628
- };
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;
5629
5659
  }
5630
- const tokenPricing = pickVoiceTokenPricing(model, promptTokens + outputTokens);
5631
- if (tokenPricing) {
5632
- const providerCostUsd = promptTokens / 1e6 * tokenPricing.input + outputTokens / 1e6 * tokenPricing.output;
5633
- const providerDzd = providerCostUsd * exchangeRate;
5634
- return {
5635
- baseDzd: roundMoney(providerDzd * b.retailMarkup.multiplier),
5636
- usedLocalCost: false
5637
- };
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;
5638
5666
  }
5639
- return { baseDzd: floorDzd, usedLocalCost: false };
5667
+ return null;
5640
5668
  }
5641
- _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) {
5642
5704
  if (attachmentCount <= 0) return 0;
5643
5705
  const { exchangeRate, billing } = this.config;
5644
5706
  const ref = billing.referenceAttachmentSurcharge;
5645
- const m = billing.retailMarkup.multiplier;
5646
- let attachCostUsd = attachmentCount * ref.perFileUsd;
5707
+ let usd = attachmentCount * ref.perFileUsd;
5647
5708
  if (attachmentResolution === "high") {
5648
- attachCostUsd += attachmentCount * ref.highResolutionExtraPerFileUsd;
5709
+ usd += attachmentCount * ref.highResolutionExtraPerFileUsd;
5649
5710
  } else if (attachmentResolution === "low") {
5650
- 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;
5651
5753
  }
5652
- return roundMoney(attachCostUsd * exchangeRate * m);
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;
5653
5760
  }
5654
5761
  /**
5655
- * Estimate the cost of an image generation action.
5656
- * Covers: image gen, logo gen, editOrGenerateSimpleImage.
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;
5810
+ }
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 };
5820
+ }
5821
+ // -- estimators -----------------------------------------------------------
5822
+ /**
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.
5657
5829
  */
5658
5830
  estimateImageGeneration(options = {}) {
5659
5831
  const {
5660
- modelId = "gemini-3.1-flash-image-preview",
5832
+ modelId = DEFAULT_IMAGE_MODEL_ID,
5661
5833
  attachmentCount = 0,
5662
5834
  attachmentResolution = "medium",
5663
5835
  resolution,
5664
5836
  imageSize,
5665
5837
  iterations = 1,
5666
- referenceImageCount = 0
5838
+ referenceImageCount = 0,
5839
+ featureAddonsDzd = 0
5667
5840
  } = options;
5668
- const model = findModel(this.config.models, modelId, "gemini-3.1-flash-image-preview");
5669
- const { exchangeRate, defaultImageCost, billing } = this.config;
5841
+ const { exchangeRate, billing } = this.config;
5670
5842
  const mult = billing.retailMarkup.multiplier;
5671
- const localCost = model?.localCost;
5672
- const imagePricing = model?.pricing?.find((p) => p.unit === "image");
5673
- const providerCostUsd = imagePricing?.output ? imagePricing.output : defaultImageCost / exchangeRate;
5674
- const providerCostDzd = imagePricing?.output ? providerCostUsd * exchangeRate : defaultImageCost;
5675
- 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;
5676
5848
  const usedLocalCost = localCost !== void 0 && localCost !== null;
5677
- const basePerIteration = usedLocalCost ? localCost : roundMoney(computedUserCostDzd);
5678
- const baseCostDzd = roundMoney(basePerIteration * iterations);
5679
- const refExtraDzd = roundMoney(referenceImageCount * this.config.referenceImageCost);
5680
- const attachExtraDzd = this._attachmentExtraUserDzd(attachmentCount, attachmentResolution);
5681
- const supportsImageSize = modelId === "gemini-3.1-flash-image-preview";
5682
- let outputResKey = "MEDIA_RESOLUTION_HIGH";
5683
- if (supportsImageSize && imageSize) {
5684
- outputResKey = imageSize === "4K" ? "MEDIA_RESOLUTION_HIGH" : imageSize === "2K" ? "MEDIA_RESOLUTION_MEDIUM" : "MEDIA_RESOLUTION_LOW";
5685
- } else if (resolution) {
5686
- outputResKey = resolution;
5687
- }
5688
- const outputResExtraDzd = supportsImageSize ? roundMoney(this.config.resolutionCosts[outputResKey] ?? 0) : 0;
5689
- let referenceResolutionExtraDzd = 0;
5690
- if (resolution) {
5691
- const low = this.config.resolutionCosts.MEDIA_RESOLUTION_LOW ?? 0;
5692
- const tier = this.config.resolutionCosts[resolution] ?? 0;
5693
- referenceResolutionExtraDzd = roundMoney(Math.max(0, tier - low));
5694
- }
5695
- const resExtraDzd = roundMoney(outputResExtraDzd + referenceResolutionExtraDzd);
5696
- 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
+ );
5697
5864
  return {
5698
- providerCostUsd: providerCostUsd * iterations,
5699
- providerCostDzd: providerCostDzd * iterations,
5865
+ providerCostUsd: providerCostUsdPerImage * iter,
5866
+ providerCostDzd: providerCostDzdPerImage * iter,
5700
5867
  userCostDzd,
5701
5868
  exchangeRate,
5702
5869
  multiplier: mult,
@@ -5705,34 +5872,34 @@ var AiCalculator = class {
5705
5872
  baseCostDzd,
5706
5873
  referenceImageExtraDzd: refExtraDzd,
5707
5874
  attachmentExtraDzd: attachExtraDzd,
5708
- outputResolutionExtraDzd: outputResExtraDzd,
5709
- referenceResolutionExtraDzd,
5710
- resolutionExtraDzd: resExtraDzd,
5711
- iterations,
5875
+ outputResolutionExtraDzd: resExtras.outputExtraDzd,
5876
+ inputResolutionExtraDzd: resExtras.inputExtraDzd,
5877
+ resolutionExtraDzd: resExtras.totalDzd,
5878
+ featureAddonsDzd: addonsDzd,
5879
+ iterations: iter,
5712
5880
  referenceImageCount,
5713
5881
  attachmentCount
5714
5882
  }
5715
5883
  };
5716
5884
  }
5717
5885
  /**
5718
- * Estimate the cost of a text generation action.
5719
- * Covers: updateProductUsingAi, generateSimpleCode, generateCustomComponentCode.
5720
- * 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).
5721
5889
  */
5722
5890
  estimateTextGeneration(options = {}) {
5723
5891
  const tg = this.config.billing.textGeneration;
5724
5892
  const mult = this.config.billing.retailMarkup.multiplier;
5725
5893
  const {
5726
- modelId = "gemini-3-flash-preview",
5894
+ modelId = DEFAULT_TEXT_PRICING_MODEL_ID,
5727
5895
  estimatedPromptTokens = tg.estimatedPromptTokensDefault,
5728
5896
  estimatedOutputTokens = tg.estimatedOutputTokensDefault
5729
5897
  } = options;
5730
- const totalTokens = estimatedPromptTokens + estimatedOutputTokens;
5898
+ const promptTokens = Math.max(0, estimatedPromptTokens);
5899
+ const outputTokens = Math.max(0, estimatedOutputTokens);
5731
5900
  const { exchangeRate } = this.config;
5732
- const model = findModel(this.config.models, modelId, "gemini-3-flash-preview");
5733
- const tokenPricing = model?.pricing?.find((p) => p.unit === "tokens");
5734
- const catalogPricing = tokenPricing ? null : pickCatalogTokenPricingPerM(this.config.modelsCatalog, modelId);
5735
- if (!tokenPricing && !catalogPricing || totalTokens < tg.freeTierMaxPromptTokens) {
5901
+ const pricing = this.resolveTextTokenPricing(modelId, promptTokens + outputTokens);
5902
+ if (!pricing || promptTokens < tg.freeTierMaxPromptTokens) {
5736
5903
  return {
5737
5904
  providerCostUsd: 0,
5738
5905
  providerCostDzd: 0,
@@ -5741,15 +5908,13 @@ var AiCalculator = class {
5741
5908
  multiplier: mult,
5742
5909
  usedLocalCost: false,
5743
5910
  breakdown: {
5744
- estimatedPromptTokens,
5745
- estimatedOutputTokens,
5746
- isFree: true
5911
+ estimatedPromptTokens: promptTokens,
5912
+ estimatedOutputTokens: outputTokens,
5913
+ isFree: 1
5747
5914
  }
5748
5915
  };
5749
5916
  }
5750
- const inputPrice = tokenPricing?.input ?? catalogPricing?.input ?? 0;
5751
- const outputPrice = tokenPricing?.output ?? catalogPricing?.output ?? 0;
5752
- const providerCostUsd = estimatedPromptTokens / 1e6 * inputPrice + estimatedOutputTokens / 1e6 * outputPrice;
5917
+ const providerCostUsd = promptTokens / 1e6 * pricing.input + outputTokens / 1e6 * pricing.output;
5753
5918
  const providerCostDzd = providerCostUsd * exchangeRate;
5754
5919
  const userCostDzd = roundMoney(providerCostDzd * mult);
5755
5920
  return {
@@ -5760,48 +5925,42 @@ var AiCalculator = class {
5760
5925
  multiplier: mult,
5761
5926
  usedLocalCost: false,
5762
5927
  breakdown: {
5763
- estimatedPromptTokens,
5764
- estimatedOutputTokens,
5765
- isFree: false
5928
+ estimatedPromptTokens: promptTokens,
5929
+ estimatedOutputTokens: outputTokens,
5930
+ isFree: 0
5766
5931
  }
5767
5932
  };
5768
5933
  }
5769
5934
  /**
5770
- * 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.
5771
5939
  */
5772
5940
  estimateVoiceover(options = {}) {
5773
- const modelId = options.modelId ?? "gemini-2.5-pro-preview-tts";
5774
- const attachmentCount = options.attachmentCount ?? 0;
5941
+ const modelId = options.modelId ?? DEFAULT_TTS_MODEL_ID;
5942
+ const attachmentCount = Math.max(0, options.attachmentCount ?? 0);
5775
5943
  const attachmentResolution = options.attachmentResolution ?? "medium";
5776
5944
  const enhanceScript = options.enhanceScript !== false;
5777
- const charLen = options.scriptCharLength ?? 0;
5778
- const b = this.config.billing;
5779
- const cap = b.voiceGeneration.ttsTokenEstimate.maxTotalTokens;
5780
- const tokenEst = (
5781
- // eslint-disable-next-line eqeqeq
5782
- options.estimatedPromptTokens != null && options.estimatedOutputTokens != null ? {
5783
- promptTokens: Math.max(0, Math.min(cap, options.estimatedPromptTokens)),
5784
- outputTokens: Math.max(0, Math.min(cap, options.estimatedOutputTokens))
5785
- } : ttsTokenEstimatesFromResolved(b, charLen, attachmentCount)
5786
- );
5787
- const { exchangeRate } = this.config;
5788
- const { baseDzd, usedLocalCost } = this._voiceoverBaseUserCostDzd(
5789
- modelId,
5790
- tokenEst.promptTokens,
5791
- tokenEst.outputTokens
5792
- );
5793
- const attachExtra = this._attachmentExtraUserDzd(attachmentCount, attachmentResolution);
5794
- const enhanceExtra = enhanceScript ? roundMoney(b.voiceGeneration.scriptEnhancementAddonUsd * exchangeRate) : 0;
5795
- 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);
5796
5955
  return {
5797
- providerCostUsd: userCostDzd / exchangeRate,
5798
- providerCostDzd: userCostDzd,
5956
+ providerCostUsd: base.providerCostUsd,
5957
+ providerCostDzd: base.providerCostUsd * exchangeRate,
5799
5958
  userCostDzd,
5800
5959
  exchangeRate,
5801
- multiplier: 1,
5802
- usedLocalCost,
5960
+ multiplier: billing.retailMarkup.multiplier,
5961
+ usedLocalCost: base.usedLocalCost,
5803
5962
  breakdown: {
5804
- fixedCostDzd: baseDzd,
5963
+ ttsBaseDzd: base.baseDzd,
5805
5964
  attachmentExtraDzd: attachExtra,
5806
5965
  enhanceAddonDzd: enhanceExtra,
5807
5966
  attachmentCount,
@@ -5810,21 +5969,61 @@ var AiCalculator = class {
5810
5969
  }
5811
5970
  };
5812
5971
  }
5813
- /** Get the fixed cost for image landing page generation. */
5814
- 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 = {}) {
5815
5979
  const { exchangeRate, billing } = this.config;
5816
- 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);
5817
6004
  return {
5818
- providerCostUsd: costDzd / exchangeRate,
5819
- providerCostDzd: costDzd,
5820
- userCostDzd: costDzd,
5821
- exchangeRate,
5822
- multiplier: 1,
5823
- usedLocalCost: false,
5824
- breakdown: { fixedCostDzd: costDzd }
6005
+ ...img,
6006
+ userCostDzd: fixedDzd,
6007
+ breakdown: { ...img.breakdown, fixedCostDzd: fixedDzd }
5825
6008
  };
5826
6009
  }
5827
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
+ }
5828
6027
 
5829
6028
  // src/utils.ts
5830
6029
  var convertDartColorToCssNumber = (dartColor) => {
@@ -5921,6 +6120,7 @@ export {
5921
6120
  CountryRepository,
5922
6121
  CurrencyRepository,
5923
6122
  CustomerPaymentRepository,
6123
+ DEFAULT_OAUTH_SCOPES,
5924
6124
  DeliveryServiceFilter,
5925
6125
  DeliveryStatus,
5926
6126
  DepositRepository,
@@ -5949,10 +6149,13 @@ export {
5949
6149
  InventoryRepository,
5950
6150
  InventoryReservationRepository,
5951
6151
  InventoryWarehouseRepository,
6152
+ MEMBER_SCOPES,
5952
6153
  MetaPixelEvent,
5953
6154
  ModelRepository,
5954
6155
  NoestDeliveryIntegrationApi,
5955
6156
  NotificationsService,
6157
+ OAUTH_PLATFORM_SCOPES,
6158
+ OAUTH_SCOPES,
5956
6159
  OAuthRepository,
5957
6160
  ORDER_FILTER_ANY,
5958
6161
  OrderRepository,
@@ -6033,6 +6236,7 @@ export {
6033
6236
  generatePublicStoreIntegrationGoogleSheet,
6034
6237
  generatePublicStoreIntegrationGoogleTags,
6035
6238
  generatePublicStoreIntegrationInventory,
6239
+ generatePublicStoreIntegrationMeta,
6036
6240
  generatePublicStoreIntegrationMetaPixel,
6037
6241
  generatePublicStoreIntegrationOrderdz,
6038
6242
  generatePublicStoreIntegrationPayment,