feeef 0.8.10 → 0.8.13

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/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  `feeefjs` is a TypeScript library for managing feeef e-commerce platforms for self-hosted stores. It provides a wrapper for feeef rest api such like send order..., also have frontend srvices like the `CartService` class for managing cart items, shipping methods, and calculating totals. The library also includes a `NotifiableService` base class for handling listeners that react to changes in the service state.
4
4
 
5
+ ## Delivery parcel types
6
+
7
+ Canonical shipment shapes mirror the API domain in `feeefapps/backend/services/delivery/domain/parcel.ts`. In this package they live under `src/delivery/parcel.ts` and are exported from the main entry (`ParcelCreate`, `ParcelUpdate`, `DeliveryCarrierClient`, …). Keep them in sync when the backend parcel model changes; published npm `feeef` should re-export the same definitions after each release.
8
+
5
9
  ## Developer OAuth (third-party apps)
6
10
 
7
11
  Use `OAuthRepository.buildAuthorizeUrl` (point `baseUrl` at the **accounts** host, e.g. `https://accounts.feeef.org`) and `FeeeF` client `oauth.exchangeAuthorizationCode` / `revokeToken` / `introspectToken` against the **API** base (`.../v1`). Full flow, scopes, and troubleshooting: Feeef backend **`docs/OAUTH2_DEVELOPER.md`** (in the Adonis API repo).
package/build/index.js CHANGED
@@ -167,6 +167,7 @@ var OrderRepository = class extends ModelRepository {
167
167
  if (options.shippingState) params.shippingState = options.shippingState;
168
168
  if (options.shippingCity) params.shippingCity = options.shippingCity;
169
169
  if (options.deliveryService) params.deliveryService = options.deliveryService;
170
+ if (options.references !== void 0) params.references = options.references;
170
171
  }
171
172
  return super.list({ params });
172
173
  }
@@ -321,6 +322,15 @@ var ProductRepository = class extends ModelRepository {
321
322
  const res = await this.client.get(`/stores/${storeId}/${this.resource}/${productId}/report`);
322
323
  return res.data;
323
324
  }
325
+ /**
326
+ * Lite orders report for a product in a store.
327
+ */
328
+ async liteOrdersReport(storeId, productId) {
329
+ const res = await this.client.get(
330
+ `/stores/${storeId}/${this.resource}/${productId}/analytics/lor`
331
+ );
332
+ return res.data;
333
+ }
324
334
  };
325
335
 
326
336
  // src/feeef/repositories/store_invites_repository.ts
@@ -434,6 +444,13 @@ var StoreRepository = class extends ModelRepository {
434
444
  }
435
445
  return chartData;
436
446
  }
447
+ /**
448
+ * Lite orders report for the store (8 UTC days + total).
449
+ */
450
+ async liteOrdersReport(storeId) {
451
+ const res = await this.client.get(`/${this.resource}/${storeId}/analytics/lor`);
452
+ return res.data;
453
+ }
437
454
  /**
438
455
  * Adds a member to the store.
439
456
  * @param storeId - The store ID.
@@ -1815,6 +1832,15 @@ var ProductLandingPagesRepository = class extends ModelRepository {
1815
1832
  const params = { ...options?.params };
1816
1833
  return super.list({ params });
1817
1834
  }
1835
+ /**
1836
+ * Lite orders report for a product landing page in a store.
1837
+ */
1838
+ async liteOrdersReport(storeId, landingPageId) {
1839
+ const res = await this.client.get(
1840
+ `/stores/${storeId}/product_landing_pages/${landingPageId}/analytics/lor`
1841
+ );
1842
+ return res.data;
1843
+ }
1818
1844
  };
1819
1845
 
1820
1846
  // src/core/entities/attachment.ts
@@ -4085,6 +4111,14 @@ var ProductType = /* @__PURE__ */ ((ProductType2) => {
4085
4111
  ProductType2["service"] = "service";
4086
4112
  return ProductType2;
4087
4113
  })(ProductType || {});
4114
+ function formatProductOrderReference(productId) {
4115
+ return `product:${productId}`;
4116
+ }
4117
+
4118
+ // src/core/entities/product_landing_page.ts
4119
+ function formatProductLandingPageOrderReference(landingPageId) {
4120
+ return `product_landing_page:${landingPageId}`;
4121
+ }
4088
4122
 
4089
4123
  // src/core/entities/feedback.ts
4090
4124
  var FeedbackStatus = /* @__PURE__ */ ((FeedbackStatus2) => {
@@ -4192,6 +4226,414 @@ function createFeeefTransmitFromAxios(client, options) {
4192
4226
  });
4193
4227
  }
4194
4228
 
4229
+ // src/ai/ai_calculator.ts
4230
+ var FALLBACK_AI_EXCHANGE_RATE = 260;
4231
+ var DEFAULT_TTS = {
4232
+ whenScriptEmptyTokens: 200,
4233
+ whenAttachmentsOnlyTokens: 400,
4234
+ promptBaseTokens: 400,
4235
+ promptPerAttachmentTokens: 300,
4236
+ outputMinimumTokens: 300,
4237
+ outputToTextTokenRatio: 2.5,
4238
+ maxTotalTokens: 32e3
4239
+ };
4240
+ var DEFAULT_RESOLVED = {
4241
+ retailMarkup: { multiplier: 2.5 },
4242
+ referenceAttachmentSurcharge: {
4243
+ perFileUsd: 0.1,
4244
+ highResolutionExtraPerFileUsd: 0.05,
4245
+ lowResolutionDiscountPerFileUsd: 0.05
4246
+ },
4247
+ imageGeneration: { fallbackProviderCostPerImageUsd: 0.131 },
4248
+ textGeneration: {
4249
+ freeTierMaxPromptTokens: 1e3,
4250
+ estimatedPromptTokensDefault: 2e3,
4251
+ estimatedOutputTokensDefault: 1e3
4252
+ },
4253
+ voiceGeneration: {
4254
+ minimumChargeUsd: 50 / FALLBACK_AI_EXCHANGE_RATE,
4255
+ scriptEnhancementAddonUsd: 25 / FALLBACK_AI_EXCHANGE_RATE,
4256
+ ttsTokenEstimate: { ...DEFAULT_TTS }
4257
+ },
4258
+ landingPageImage: {
4259
+ fixedChargeUsd: 100 / FALLBACK_AI_EXCHANGE_RATE
4260
+ }
4261
+ };
4262
+ function mergeTts(base, partial) {
4263
+ if (!partial) return { ...base };
4264
+ return {
4265
+ whenScriptEmptyTokens: partial.whenScriptEmptyTokens ?? base.whenScriptEmptyTokens,
4266
+ whenAttachmentsOnlyTokens: partial.whenAttachmentsOnlyTokens ?? base.whenAttachmentsOnlyTokens,
4267
+ promptBaseTokens: partial.promptBaseTokens ?? base.promptBaseTokens,
4268
+ promptPerAttachmentTokens: partial.promptPerAttachmentTokens ?? base.promptPerAttachmentTokens,
4269
+ outputMinimumTokens: partial.outputMinimumTokens ?? base.outputMinimumTokens,
4270
+ outputToTextTokenRatio: partial.outputToTextTokenRatio ?? base.outputToTextTokenRatio,
4271
+ maxTotalTokens: partial.maxTotalTokens ?? base.maxTotalTokens
4272
+ };
4273
+ }
4274
+ function mergeAiModelsBilling(partial) {
4275
+ const d = DEFAULT_RESOLVED;
4276
+ if (!partial) return structuredClone(d);
4277
+ const v = partial.voiceGeneration;
4278
+ const tts = mergeTts(d.voiceGeneration.ttsTokenEstimate, v?.ttsTokenEstimate);
4279
+ return {
4280
+ retailMarkup: {
4281
+ multiplier: partial.retailMarkup?.multiplier ?? d.retailMarkup.multiplier
4282
+ },
4283
+ referenceAttachmentSurcharge: {
4284
+ perFileUsd: partial.referenceAttachmentSurcharge?.perFileUsd ?? d.referenceAttachmentSurcharge.perFileUsd,
4285
+ highResolutionExtraPerFileUsd: partial.referenceAttachmentSurcharge?.highResolutionExtraPerFileUsd ?? d.referenceAttachmentSurcharge.highResolutionExtraPerFileUsd,
4286
+ lowResolutionDiscountPerFileUsd: partial.referenceAttachmentSurcharge?.lowResolutionDiscountPerFileUsd ?? d.referenceAttachmentSurcharge.lowResolutionDiscountPerFileUsd
4287
+ },
4288
+ imageGeneration: {
4289
+ fallbackProviderCostPerImageUsd: partial.imageGeneration?.fallbackProviderCostPerImageUsd ?? d.imageGeneration.fallbackProviderCostPerImageUsd
4290
+ },
4291
+ textGeneration: {
4292
+ freeTierMaxPromptTokens: partial.textGeneration?.freeTierMaxPromptTokens ?? d.textGeneration.freeTierMaxPromptTokens,
4293
+ estimatedPromptTokensDefault: partial.textGeneration?.estimatedPromptTokensDefault ?? d.textGeneration.estimatedPromptTokensDefault,
4294
+ estimatedOutputTokensDefault: partial.textGeneration?.estimatedOutputTokensDefault ?? d.textGeneration.estimatedOutputTokensDefault
4295
+ },
4296
+ voiceGeneration: {
4297
+ minimumChargeUsd: v?.minimumChargeUsd ?? d.voiceGeneration.minimumChargeUsd,
4298
+ scriptEnhancementAddonUsd: v?.scriptEnhancementAddonUsd ?? d.voiceGeneration.scriptEnhancementAddonUsd,
4299
+ ttsTokenEstimate: tts
4300
+ },
4301
+ landingPageImage: {
4302
+ fixedChargeUsd: partial.landingPageImage?.fixedChargeUsd ?? d.landingPageImage.fixedChargeUsd
4303
+ }
4304
+ };
4305
+ }
4306
+ function roundMoney(amount, precision = 3) {
4307
+ const factor = 10 ** precision;
4308
+ return Math.round((amount + Number.EPSILON) * factor) / factor;
4309
+ }
4310
+ function getLegacyAiBillingFlat(exchangeRate, resolved = mergeAiModelsBilling(null)) {
4311
+ const t = resolved.voiceGeneration.ttsTokenEstimate;
4312
+ return {
4313
+ MULTIPLIER: resolved.retailMarkup.multiplier,
4314
+ FREE_TEXT_TOKENS_THRESHOLD: resolved.textGeneration.freeTierMaxPromptTokens,
4315
+ DEFAULT_EXCHANGE_RATE: FALLBACK_AI_EXCHANGE_RATE,
4316
+ DEFAULT_GOOGLE_IMAGE_COST_USD: resolved.imageGeneration.fallbackProviderCostPerImageUsd,
4317
+ DEFAULT_ATTACHMENT_COST_USD: resolved.referenceAttachmentSurcharge.perFileUsd,
4318
+ ATTACHMENT_HIGH_RES_EXTRA_USD: resolved.referenceAttachmentSurcharge.highResolutionExtraPerFileUsd,
4319
+ ATTACHMENT_LOW_RES_DISCOUNT_USD: resolved.referenceAttachmentSurcharge.lowResolutionDiscountPerFileUsd,
4320
+ VOICEOVER_FIXED_COST_DZD: roundMoney(
4321
+ resolved.voiceGeneration.minimumChargeUsd * exchangeRate,
4322
+ 3
4323
+ ),
4324
+ VOICEOVER_ENHANCE_ADDON_DZD: roundMoney(
4325
+ resolved.voiceGeneration.scriptEnhancementAddonUsd * exchangeRate,
4326
+ 3
4327
+ ),
4328
+ IMAGE_LANDING_PAGE_FIXED_COST_DZD: roundMoney(
4329
+ resolved.landingPageImage.fixedChargeUsd * exchangeRate,
4330
+ 3
4331
+ ),
4332
+ DEFAULT_TEXT_PROMPT_TOKENS: resolved.textGeneration.estimatedPromptTokensDefault,
4333
+ DEFAULT_TEXT_OUTPUT_TOKENS: resolved.textGeneration.estimatedOutputTokensDefault,
4334
+ VOICE_TTS_EMPTY_SCRIPT_TEXT_TOKENS: t.whenScriptEmptyTokens,
4335
+ VOICE_TTS_ATTACHMENT_ONLY_TEXT_TOKENS: t.whenAttachmentsOnlyTokens,
4336
+ VOICE_TTS_PROMPT_BASE: t.promptBaseTokens,
4337
+ VOICE_TTS_PROMPT_PER_ATTACHMENT: t.promptPerAttachmentTokens,
4338
+ VOICE_TTS_OUTPUT_MIN: t.outputMinimumTokens,
4339
+ VOICE_TTS_OUTPUT_TEXT_FACTOR: t.outputToTextTokenRatio,
4340
+ VOICE_TTS_TOKEN_CAP: t.maxTotalTokens
4341
+ };
4342
+ }
4343
+ var AI_BILLING = getLegacyAiBillingFlat(FALLBACK_AI_EXCHANGE_RATE);
4344
+ function findModel(models, modelId, fallbackId) {
4345
+ return models.find((m) => m.id === modelId) || models.find((m) => m.id === fallbackId) || models[0];
4346
+ }
4347
+ function modelHasVoiceCapability(model) {
4348
+ const caps = model?.capabilities;
4349
+ if (!Array.isArray(caps)) return false;
4350
+ return caps.some((c) => c === "voice" || c === "audio");
4351
+ }
4352
+ function findVoiceModel(models, modelId, fallbackId) {
4353
+ return models.find((m) => m.id === modelId) || models.find((m) => m.id === fallbackId) || models.find((m) => modelHasVoiceCapability(m));
4354
+ }
4355
+ function pickTtsProviderOutputUsd(model) {
4356
+ if (!model?.pricing?.length) return null;
4357
+ const voiceLike = modelHasVoiceCapability(model);
4358
+ for (const unit of ["audio", "voice"]) {
4359
+ const row = model.pricing.find((p) => p.unit === unit);
4360
+ if (row?.output == null) continue;
4361
+ const usd = row.output;
4362
+ if (usd > 0) return usd;
4363
+ }
4364
+ if (voiceLike) {
4365
+ const row = model.pricing.find((p) => p.unit === "image");
4366
+ if (row?.output == null) return null;
4367
+ const usd = row.output;
4368
+ if (usd > 0) return usd;
4369
+ }
4370
+ return null;
4371
+ }
4372
+ function ttsTokenEstimatesFromResolved(b, scriptCharLength, attachmentCount) {
4373
+ const t = b.voiceGeneration.ttsTokenEstimate;
4374
+ let textTok = Math.round(scriptCharLength / 4);
4375
+ if (textTok <= 0) {
4376
+ textTok = attachmentCount > 0 ? t.whenAttachmentsOnlyTokens : t.whenScriptEmptyTokens;
4377
+ }
4378
+ const rawPrompt = t.promptBaseTokens + textTok + attachmentCount * t.promptPerAttachmentTokens;
4379
+ const promptTokens = Math.min(t.maxTotalTokens, Math.max(0, rawPrompt));
4380
+ const rawOutput = Math.max(t.outputMinimumTokens, Math.round(textTok * t.outputToTextTokenRatio));
4381
+ const outputTokens = Math.min(t.maxTotalTokens, rawOutput);
4382
+ return { promptTokens, outputTokens };
4383
+ }
4384
+ function defaultVoiceTtsTokenEstimates(scriptCharLength, attachmentCount) {
4385
+ return ttsTokenEstimatesFromResolved(
4386
+ mergeAiModelsBilling(null),
4387
+ scriptCharLength,
4388
+ attachmentCount
4389
+ );
4390
+ }
4391
+ function pickVoiceTokenPricing(model, totalTokens) {
4392
+ const tokenPricings = (model?.pricing ?? []).filter((p) => p.unit === "tokens");
4393
+ if (!tokenPricings.length) return null;
4394
+ const isLargeContext = totalTokens > 2e5;
4395
+ const preferred = tokenPricings.find(
4396
+ (p) => isLargeContext ? String(p.contextThreshold ?? "").includes(">") : String(p.contextThreshold ?? "").includes("<=")
4397
+ ) || tokenPricings[0];
4398
+ const input = preferred.input ?? 0;
4399
+ const output = preferred.output ?? 0;
4400
+ if (input <= 0 && output <= 0) return null;
4401
+ return { input, output };
4402
+ }
4403
+ var AiCalculator = class {
4404
+ config;
4405
+ constructor(config = {}) {
4406
+ const exchangeRate = config.exchangeRate ?? FALLBACK_AI_EXCHANGE_RATE;
4407
+ const billing = mergeAiModelsBilling(config.billing ?? null);
4408
+ const fallbackDzd = billing.imageGeneration.fallbackProviderCostPerImageUsd * exchangeRate;
4409
+ this.config = {
4410
+ exchangeRate,
4411
+ defaultImageCost: config.defaultImageCost ?? fallbackDzd,
4412
+ referenceImageCost: config.referenceImageCost ?? 5,
4413
+ resolutionCosts: config.resolutionCosts ?? {
4414
+ MEDIA_RESOLUTION_LOW: 0,
4415
+ MEDIA_RESOLUTION_MEDIUM: 5,
4416
+ MEDIA_RESOLUTION_HIGH: 10
4417
+ },
4418
+ models: config.models ?? [],
4419
+ billing
4420
+ };
4421
+ }
4422
+ /** TTS base DZD: localCost → flat USD row → tokens (per 1M) × estimates × rate × multiplier → floor. */
4423
+ _voiceoverBaseUserCostDzd(modelId, promptTokens, outputTokens) {
4424
+ const { exchangeRate, billing, models } = this.config;
4425
+ const b = billing;
4426
+ const floorDzd = roundMoney(b.voiceGeneration.minimumChargeUsd * exchangeRate);
4427
+ const model = findVoiceModel(models, modelId, "gemini-2.5-pro-preview-tts");
4428
+ if (model?.localCost !== void 0 && model.localCost !== null) {
4429
+ return { baseDzd: roundMoney(model.localCost), usedLocalCost: true };
4430
+ }
4431
+ const providerUsd = pickTtsProviderOutputUsd(model);
4432
+ if (providerUsd != null) {
4433
+ const providerDzd = providerUsd * exchangeRate;
4434
+ return {
4435
+ baseDzd: roundMoney(providerDzd * b.retailMarkup.multiplier),
4436
+ usedLocalCost: false
4437
+ };
4438
+ }
4439
+ const tokenPricing = pickVoiceTokenPricing(model, promptTokens + outputTokens);
4440
+ if (tokenPricing) {
4441
+ const providerCostUsd = promptTokens / 1e6 * tokenPricing.input + outputTokens / 1e6 * tokenPricing.output;
4442
+ const providerDzd = providerCostUsd * exchangeRate;
4443
+ return {
4444
+ baseDzd: roundMoney(providerDzd * b.retailMarkup.multiplier),
4445
+ usedLocalCost: false
4446
+ };
4447
+ }
4448
+ return { baseDzd: floorDzd, usedLocalCost: false };
4449
+ }
4450
+ _attachmentExtraUserDzd(attachmentCount, attachmentResolution) {
4451
+ if (attachmentCount <= 0) return 0;
4452
+ const { exchangeRate, billing } = this.config;
4453
+ const ref = billing.referenceAttachmentSurcharge;
4454
+ const m = billing.retailMarkup.multiplier;
4455
+ let attachCostUsd = attachmentCount * ref.perFileUsd;
4456
+ if (attachmentResolution === "high") {
4457
+ attachCostUsd += attachmentCount * ref.highResolutionExtraPerFileUsd;
4458
+ } else if (attachmentResolution === "low") {
4459
+ attachCostUsd -= attachmentCount * ref.lowResolutionDiscountPerFileUsd;
4460
+ }
4461
+ return roundMoney(attachCostUsd * exchangeRate * m);
4462
+ }
4463
+ /**
4464
+ * Estimate the cost of an image generation action.
4465
+ * Covers: image gen, logo gen, editOrGenerateSimpleImage.
4466
+ */
4467
+ estimateImageGeneration(options = {}) {
4468
+ const {
4469
+ modelId = "gemini-3.1-flash-image-preview",
4470
+ attachmentCount = 0,
4471
+ attachmentResolution = "medium",
4472
+ resolution,
4473
+ imageSize,
4474
+ iterations = 1,
4475
+ referenceImageCount = 0
4476
+ } = options;
4477
+ const model = findModel(this.config.models, modelId, "gemini-3.1-flash-image-preview");
4478
+ const { exchangeRate, defaultImageCost, billing } = this.config;
4479
+ const mult = billing.retailMarkup.multiplier;
4480
+ const localCost = model?.localCost;
4481
+ const imagePricing = model?.pricing?.find((p) => p.unit === "image");
4482
+ const providerCostUsd = imagePricing?.output ? imagePricing.output : defaultImageCost / exchangeRate;
4483
+ const providerCostDzd = imagePricing?.output ? providerCostUsd * exchangeRate : defaultImageCost;
4484
+ const computedUserCostDzd = providerCostDzd * mult;
4485
+ const usedLocalCost = localCost !== void 0 && localCost !== null;
4486
+ const basePerIteration = usedLocalCost ? localCost : roundMoney(computedUserCostDzd);
4487
+ const baseCostDzd = roundMoney(basePerIteration * iterations);
4488
+ const refExtraDzd = roundMoney(referenceImageCount * this.config.referenceImageCost);
4489
+ const attachExtraDzd = this._attachmentExtraUserDzd(attachmentCount, attachmentResolution);
4490
+ const supportsImageSize = modelId === "gemini-3.1-flash-image-preview";
4491
+ let outputResKey = "MEDIA_RESOLUTION_HIGH";
4492
+ if (supportsImageSize && imageSize) {
4493
+ outputResKey = imageSize === "4K" ? "MEDIA_RESOLUTION_HIGH" : imageSize === "2K" ? "MEDIA_RESOLUTION_MEDIUM" : "MEDIA_RESOLUTION_LOW";
4494
+ } else if (resolution) {
4495
+ outputResKey = resolution;
4496
+ }
4497
+ const outputResExtraDzd = supportsImageSize ? roundMoney(this.config.resolutionCosts[outputResKey] ?? 0) : 0;
4498
+ let referenceResolutionExtraDzd = 0;
4499
+ if (referenceImageCount > 0 && resolution) {
4500
+ const low = this.config.resolutionCosts.MEDIA_RESOLUTION_LOW ?? 0;
4501
+ const tier = this.config.resolutionCosts[resolution] ?? 0;
4502
+ referenceResolutionExtraDzd = roundMoney(Math.max(0, tier - low));
4503
+ }
4504
+ const resExtraDzd = roundMoney(outputResExtraDzd + referenceResolutionExtraDzd);
4505
+ const userCostDzd = roundMoney(baseCostDzd + refExtraDzd + attachExtraDzd + resExtraDzd);
4506
+ return {
4507
+ providerCostUsd: providerCostUsd * iterations,
4508
+ providerCostDzd: providerCostDzd * iterations,
4509
+ userCostDzd,
4510
+ exchangeRate,
4511
+ multiplier: mult,
4512
+ usedLocalCost,
4513
+ breakdown: {
4514
+ baseCostDzd,
4515
+ referenceImageExtraDzd: refExtraDzd,
4516
+ attachmentExtraDzd: attachExtraDzd,
4517
+ outputResolutionExtraDzd: outputResExtraDzd,
4518
+ referenceResolutionExtraDzd,
4519
+ resolutionExtraDzd: resExtraDzd,
4520
+ iterations,
4521
+ referenceImageCount,
4522
+ attachmentCount
4523
+ }
4524
+ };
4525
+ }
4526
+ /**
4527
+ * Estimate the cost of a text generation action.
4528
+ * Covers: updateProductUsingAi, generateSimpleCode, generateCustomComponentCode.
4529
+ * Uses estimated tokens (exact cost billed post-generation).
4530
+ */
4531
+ estimateTextGeneration(options = {}) {
4532
+ const tg = this.config.billing.textGeneration;
4533
+ const mult = this.config.billing.retailMarkup.multiplier;
4534
+ const {
4535
+ modelId = "gemini-3-flash-preview",
4536
+ estimatedPromptTokens = tg.estimatedPromptTokensDefault,
4537
+ estimatedOutputTokens = tg.estimatedOutputTokensDefault
4538
+ } = options;
4539
+ const totalTokens = estimatedPromptTokens + estimatedOutputTokens;
4540
+ const { exchangeRate } = this.config;
4541
+ const model = findModel(this.config.models, modelId, "gemini-3-flash-preview");
4542
+ const tokenPricing = model?.pricing?.find((p) => p.unit === "tokens");
4543
+ if (!tokenPricing || totalTokens < tg.freeTierMaxPromptTokens) {
4544
+ return {
4545
+ providerCostUsd: 0,
4546
+ providerCostDzd: 0,
4547
+ userCostDzd: 0,
4548
+ exchangeRate,
4549
+ multiplier: mult,
4550
+ usedLocalCost: false,
4551
+ breakdown: {
4552
+ estimatedPromptTokens,
4553
+ estimatedOutputTokens,
4554
+ isFree: true
4555
+ }
4556
+ };
4557
+ }
4558
+ const inputPrice = tokenPricing.input ?? 0;
4559
+ const outputPrice = tokenPricing.output ?? 0;
4560
+ const providerCostUsd = estimatedPromptTokens / 1e6 * inputPrice + estimatedOutputTokens / 1e6 * outputPrice;
4561
+ const providerCostDzd = providerCostUsd * exchangeRate;
4562
+ const userCostDzd = roundMoney(providerCostDzd * mult);
4563
+ return {
4564
+ providerCostUsd,
4565
+ providerCostDzd,
4566
+ userCostDzd,
4567
+ exchangeRate,
4568
+ multiplier: mult,
4569
+ usedLocalCost: false,
4570
+ breakdown: {
4571
+ estimatedPromptTokens,
4572
+ estimatedOutputTokens,
4573
+ isFree: false
4574
+ }
4575
+ };
4576
+ }
4577
+ /**
4578
+ * Voiceover: base + attachment surcharge + optional script-enhancement add-on.
4579
+ */
4580
+ estimateVoiceover(options = {}) {
4581
+ const modelId = options.modelId ?? "gemini-2.5-pro-preview-tts";
4582
+ const attachmentCount = options.attachmentCount ?? 0;
4583
+ const attachmentResolution = options.attachmentResolution ?? "medium";
4584
+ const enhanceScript = options.enhanceScript !== false;
4585
+ const charLen = options.scriptCharLength ?? 0;
4586
+ const b = this.config.billing;
4587
+ const cap = b.voiceGeneration.ttsTokenEstimate.maxTotalTokens;
4588
+ const tokenEst = (
4589
+ // eslint-disable-next-line eqeqeq
4590
+ options.estimatedPromptTokens != null && options.estimatedOutputTokens != null ? {
4591
+ promptTokens: Math.max(0, Math.min(cap, options.estimatedPromptTokens)),
4592
+ outputTokens: Math.max(0, Math.min(cap, options.estimatedOutputTokens))
4593
+ } : ttsTokenEstimatesFromResolved(b, charLen, attachmentCount)
4594
+ );
4595
+ const { exchangeRate } = this.config;
4596
+ const { baseDzd, usedLocalCost } = this._voiceoverBaseUserCostDzd(
4597
+ modelId,
4598
+ tokenEst.promptTokens,
4599
+ tokenEst.outputTokens
4600
+ );
4601
+ const attachExtra = this._attachmentExtraUserDzd(attachmentCount, attachmentResolution);
4602
+ const enhanceExtra = enhanceScript ? roundMoney(b.voiceGeneration.scriptEnhancementAddonUsd * exchangeRate) : 0;
4603
+ const userCostDzd = roundMoney(baseDzd + attachExtra + enhanceExtra);
4604
+ return {
4605
+ providerCostUsd: userCostDzd / exchangeRate,
4606
+ providerCostDzd: userCostDzd,
4607
+ userCostDzd,
4608
+ exchangeRate,
4609
+ multiplier: 1,
4610
+ usedLocalCost,
4611
+ breakdown: {
4612
+ fixedCostDzd: baseDzd,
4613
+ attachmentExtraDzd: attachExtra,
4614
+ enhanceAddonDzd: enhanceExtra,
4615
+ attachmentCount,
4616
+ estimatedPromptTokens: tokenEst.promptTokens,
4617
+ estimatedOutputTokens: tokenEst.outputTokens
4618
+ }
4619
+ };
4620
+ }
4621
+ /** Get the fixed cost for image landing page generation. */
4622
+ estimateImageLandingPage() {
4623
+ const { exchangeRate, billing } = this.config;
4624
+ const costDzd = roundMoney(billing.landingPageImage.fixedChargeUsd * exchangeRate);
4625
+ return {
4626
+ providerCostUsd: costDzd / exchangeRate,
4627
+ providerCostDzd: costDzd,
4628
+ userCostDzd: costDzd,
4629
+ exchangeRate,
4630
+ multiplier: 1,
4631
+ usedLocalCost: false,
4632
+ breakdown: { fixedCostDzd: costDzd }
4633
+ };
4634
+ }
4635
+ };
4636
+
4195
4637
  // src/utils.ts
4196
4638
  var convertDartColorToCssNumber = (dartColor) => {
4197
4639
  const alpha = dartColor >> 24 & 255;
@@ -4276,8 +4718,10 @@ function validatePhoneNumber(phone) {
4276
4718
  return null;
4277
4719
  }
4278
4720
  export {
4721
+ AI_BILLING,
4279
4722
  ATTACHMENT_TYPES,
4280
4723
  ActionsService,
4724
+ AiCalculator,
4281
4725
  AppRepository,
4282
4726
  CartService,
4283
4727
  CategoryRepository,
@@ -4290,6 +4734,7 @@ export {
4290
4734
  EcomanagerDeliveryIntegrationApi,
4291
4735
  EcotrackDeliveryIntegrationApi,
4292
4736
  EmbaddedContactType,
4737
+ FALLBACK_AI_EXCHANGE_RATE,
4293
4738
  FeedbackPriority,
4294
4739
  FeedbackRepository,
4295
4740
  FeedbackStatus,
@@ -4346,6 +4791,9 @@ export {
4346
4791
  createImageGenerationFormData,
4347
4792
  cssColorToHslString,
4348
4793
  dartColorToCssColor,
4794
+ defaultVoiceTtsTokenEstimates,
4795
+ formatProductLandingPageOrderReference,
4796
+ formatProductOrderReference,
4349
4797
  generatePublicIntegrationsData,
4350
4798
  generatePublicIntegrationsDataGoogleAnalytics,
4351
4799
  generatePublicIntegrationsDataGoogleSheets,
@@ -4366,9 +4814,11 @@ export {
4366
4814
  generatePublicStoreIntegrationWebhooks,
4367
4815
  generatePublicStoreIntegrations,
4368
4816
  getAvailableShippingTypes,
4817
+ getLegacyAiBillingFlat,
4369
4818
  getShippingPrice,
4370
4819
  isAttachmentType,
4371
4820
  isShippingAvailable,
4821
+ mergeAiModelsBilling,
4372
4822
  normalizeAttachmentPayload,
4373
4823
  normalizeAttachmentPayloads,
4374
4824
  normalizeImageGeneration,