@happyvertical/smrt-ads 0.34.0 → 0.34.1

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/dist/index.d.ts CHANGED
@@ -263,13 +263,20 @@ export declare class AdEventCollection extends SmrtCollection<AdEvent> {
263
263
  */
264
264
  findByTenant(tenantId: string): Promise<AdEvent[]>;
265
265
  /**
266
- * Find global ad events (no tenant association)
266
+ * Find global ad events (no tenant association).
267
+ *
268
+ * Routes through the shared tenant-global helper so it does not throw under
269
+ * an active tenant context (an explicit `tenant_id IS NULL` filter would be
270
+ * flagged as an isolation violation). (#1600)
267
271
  *
268
272
  * @returns Array of global ad events
269
273
  */
270
274
  findGlobal(): Promise<AdEvent[]>;
271
275
  /**
272
- * Find ad events for a tenant including global (shared) events
276
+ * Find ad events for a tenant including global (shared) events.
277
+ *
278
+ * Fails closed if an active tenant context requests a different tenant's
279
+ * rows; the admin/system path keeps the cross-tenant capability. (#1600)
273
280
  *
274
281
  * @param tenantId - Tenant ID to include
275
282
  * @returns Array of tenant-specific and global ad events
@@ -575,13 +582,20 @@ export declare class AdGroupCollection extends SmrtCollection<AdGroup> {
575
582
  */
576
583
  findByTenant(tenantId: string): Promise<AdGroup[]>;
577
584
  /**
578
- * Find global ad groups (no tenant association)
585
+ * Find global ad groups (no tenant association).
586
+ *
587
+ * Routes through the shared tenant-global helper so it does not throw under
588
+ * an active tenant context (an explicit `tenant_id IS NULL` filter would be
589
+ * flagged as an isolation violation). (#1600)
579
590
  *
580
591
  * @returns Array of global ad groups
581
592
  */
582
593
  findGlobal(): Promise<AdGroup[]>;
583
594
  /**
584
- * Find ad groups for a tenant including global (shared) ad groups
595
+ * Find ad groups for a tenant including global (shared) ad groups.
596
+ *
597
+ * Fails closed if an active tenant context requests a different tenant's
598
+ * rows; the admin/system path keeps the cross-tenant capability. (#1600)
585
599
  *
586
600
  * @param tenantId - Tenant ID to include
587
601
  * @returns Array of tenant-specific and global ad groups
@@ -758,13 +772,20 @@ export declare class AdVariationCollection extends SmrtCollection<AdVariation> {
758
772
  */
759
773
  findByTenant(tenantId: string): Promise<AdVariation[]>;
760
774
  /**
761
- * Find global ad variations (no tenant association)
775
+ * Find global ad variations (no tenant association).
776
+ *
777
+ * Routes through the shared tenant-global helper so it does not throw under
778
+ * an active tenant context (an explicit `tenant_id IS NULL` filter would be
779
+ * flagged as an isolation violation). (#1600)
762
780
  *
763
781
  * @returns Array of global ad variations
764
782
  */
765
783
  findGlobal(): Promise<AdVariation[]>;
766
784
  /**
767
- * Find ad variations for a tenant including global (shared) variations
785
+ * Find ad variations for a tenant including global (shared) variations.
786
+ *
787
+ * Fails closed if an active tenant context requests a different tenant's
788
+ * rows; the admin/system path keeps the cross-tenant capability. (#1600)
768
789
  *
769
790
  * @param tenantId - Tenant ID to include
770
791
  * @returns Array of tenant-specific and global ad variations
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ObjectRegistry, smrt, SmrtObject, SmrtCollection, foreignKey, crossPackageRef } from "@happyvertical/smrt-core";
2
- import { tenantId, TenantScoped } from "@happyvertical/smrt-tenancy";
2
+ import { tenantId, TenantScoped, queryGlobal, queryWithGlobals } from "@happyvertical/smrt-tenancy";
3
3
  ObjectRegistry.registerPackageManifest(
4
4
  new URL("./manifest.json", import.meta.url)
5
5
  );
@@ -396,24 +396,28 @@ class AdEventCollection extends SmrtCollection {
396
396
  return this.list({ where: { tenantId: tenantId2 } });
397
397
  }
398
398
  /**
399
- * Find global ad events (no tenant association)
399
+ * Find global ad events (no tenant association).
400
+ *
401
+ * Routes through the shared tenant-global helper so it does not throw under
402
+ * an active tenant context (an explicit `tenant_id IS NULL` filter would be
403
+ * flagged as an isolation violation). (#1600)
400
404
  *
401
405
  * @returns Array of global ad events
402
406
  */
403
407
  async findGlobal() {
404
- return this.list({ where: { tenantId: null } });
408
+ return queryGlobal(this);
405
409
  }
406
410
  /**
407
- * Find ad events for a tenant including global (shared) events
411
+ * Find ad events for a tenant including global (shared) events.
412
+ *
413
+ * Fails closed if an active tenant context requests a different tenant's
414
+ * rows; the admin/system path keeps the cross-tenant capability. (#1600)
408
415
  *
409
416
  * @param tenantId - Tenant ID to include
410
417
  * @returns Array of tenant-specific and global ad events
411
418
  */
412
419
  async findWithGlobals(tenantId2) {
413
- return this.query(
414
- `SELECT * FROM ad_events WHERE tenant_id = ? OR tenant_id IS NULL`,
415
- [tenantId2]
416
- );
420
+ return queryWithGlobals(this, tenantId2, "AdEvent.findWithGlobals");
417
421
  }
418
422
  }
419
423
  var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
@@ -826,24 +830,28 @@ class AdGroupCollection extends SmrtCollection {
826
830
  return this.list({ where: { tenantId: tenantId2 } });
827
831
  }
828
832
  /**
829
- * Find global ad groups (no tenant association)
833
+ * Find global ad groups (no tenant association).
834
+ *
835
+ * Routes through the shared tenant-global helper so it does not throw under
836
+ * an active tenant context (an explicit `tenant_id IS NULL` filter would be
837
+ * flagged as an isolation violation). (#1600)
830
838
  *
831
839
  * @returns Array of global ad groups
832
840
  */
833
841
  async findGlobal() {
834
- return this.list({ where: { tenantId: null } });
842
+ return queryGlobal(this);
835
843
  }
836
844
  /**
837
- * Find ad groups for a tenant including global (shared) ad groups
845
+ * Find ad groups for a tenant including global (shared) ad groups.
846
+ *
847
+ * Fails closed if an active tenant context requests a different tenant's
848
+ * rows; the admin/system path keeps the cross-tenant capability. (#1600)
838
849
  *
839
850
  * @param tenantId - Tenant ID to include
840
851
  * @returns Array of tenant-specific and global ad groups
841
852
  */
842
853
  async findWithGlobals(tenantId2) {
843
- return this.query(
844
- `SELECT * FROM ad_groups WHERE tenant_id = ? OR tenant_id IS NULL`,
845
- [tenantId2]
846
- );
854
+ return queryWithGlobals(this, tenantId2, "AdGroup.findWithGlobals");
847
855
  }
848
856
  }
849
857
  var __defProp = Object.defineProperty;
@@ -1081,23 +1089,31 @@ class AdVariationCollection extends SmrtCollection {
1081
1089
  return this.list({ where: { tenantId: tenantId2 } });
1082
1090
  }
1083
1091
  /**
1084
- * Find global ad variations (no tenant association)
1092
+ * Find global ad variations (no tenant association).
1093
+ *
1094
+ * Routes through the shared tenant-global helper so it does not throw under
1095
+ * an active tenant context (an explicit `tenant_id IS NULL` filter would be
1096
+ * flagged as an isolation violation). (#1600)
1085
1097
  *
1086
1098
  * @returns Array of global ad variations
1087
1099
  */
1088
1100
  async findGlobal() {
1089
- return this.list({ where: { tenantId: null } });
1101
+ return queryGlobal(this);
1090
1102
  }
1091
1103
  /**
1092
- * Find ad variations for a tenant including global (shared) variations
1104
+ * Find ad variations for a tenant including global (shared) variations.
1105
+ *
1106
+ * Fails closed if an active tenant context requests a different tenant's
1107
+ * rows; the admin/system path keeps the cross-tenant capability. (#1600)
1093
1108
  *
1094
1109
  * @param tenantId - Tenant ID to include
1095
1110
  * @returns Array of tenant-specific and global ad variations
1096
1111
  */
1097
1112
  async findWithGlobals(tenantId2) {
1098
- return this.query(
1099
- `SELECT * FROM ad_variations WHERE tenant_id = ? OR tenant_id IS NULL`,
1100
- [tenantId2]
1113
+ return queryWithGlobals(
1114
+ this,
1115
+ tenantId2,
1116
+ "AdVariation.findWithGlobals"
1101
1117
  );
1102
1118
  }
1103
1119
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/__smrt-register__.ts","../src/types/index.ts","../src/models/AdDeliveryTier.ts","../src/collections/AdDeliveryTierCollection.ts","../src/models/AdEvent.ts","../src/collections/AdEventCollection.ts","../src/models/AdFormat.ts","../src/collections/AdFormatCollection.ts","../src/models/AdGroup.ts","../src/collections/AdGroupCollection.ts","../src/models/AdVariation.ts","../src/collections/AdVariationCollection.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// `new URL('./manifest.json', import.meta.url)` resolves at runtime to the\n// manifest sitting next to this module's compiled output. Vite warns at build\n// time that it cannot pre-resolve the URL; that is the intended behavior —\n// the URL must resolve to dist/manifest.json at runtime, not be inlined.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","/**\n * Types and enums for smrt-ads package\n */\n\n/**\n * Ad format types for different creative types\n */\nexport enum AdFormatType {\n BANNER = 'banner',\n NATIVE = 'native',\n VIDEO = 'video',\n}\n\n/**\n * Pricing models for ad delivery tiers\n */\nexport enum PricingModel {\n FIXED = 'fixed',\n CPM = 'cpm',\n CPC = 'cpc',\n CPA = 'cpa',\n}\n\n/**\n * Status for ad groups\n */\nexport enum AdGroupStatus {\n DRAFT = 'draft',\n ACTIVE = 'active',\n PAUSED = 'paused',\n COMPLETED = 'completed',\n}\n\n/**\n * Status for ad variations\n */\nexport enum AdVariationStatus {\n DRAFT = 'draft',\n ACTIVE = 'active',\n PAUSED = 'paused',\n}\n\n/**\n * Event types for ad tracking\n */\nexport enum AdEventType {\n IMPRESSION = 'impression',\n CLICK = 'click',\n CONVERSION = 'conversion',\n}\n","/**\n * AdDeliveryTier model - Priority waterfall for ad selection\n * @packageDocumentation\n */\n\nimport { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { PricingModel } from '../types/index.js';\n\n/**\n * AdDeliveryTier defines priority levels for ad serving.\n *\n * Ads are selected by priority (lower = higher priority):\n * 1. Sponsorship - Guaranteed, premium placements\n * 2. Standard - Regular programmatic ads\n * 3. House - Self-promotional/fallback ads\n *\n * @example\n * ```typescript\n * const sponsorship = await tiers.create({\n * name: 'Sponsorship',\n * priority: 1,\n * pricingModel: PricingModel.FIXED,\n * description: 'Premium guaranteed placements'\n * });\n * ```\n */\n// Intentional exception to standards.md §7 (`@TenantScoped`):\n// `AdDeliveryTier` is a shared catalog model describing the priority waterfall\n// for ad selection (Sponsorship → Standard → House). Tier definitions are part\n// of the package's ad-serving contract, not per-tenant configuration. Adding\n// `@TenantScoped` would require every tenant to seed the same three rows and\n// would fragment the selection algorithm without a clear use case. See\n// `packages/ads/CLAUDE.md` \"Tenancy\" section for context.\n@smrt({\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class AdDeliveryTier extends SmrtObject {\n /**\n * Display name (e.g., \"Sponsorship\", \"Standard\", \"House\")\n */\n name: string = '';\n\n /**\n * Priority level (lower = higher priority: 1, 2, 3...)\n */\n priority: number = 0;\n\n /**\n * Pricing model for this tier\n */\n pricingModel: PricingModel = PricingModel.CPM;\n\n /**\n * Optional description\n */\n description: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.name !== undefined) this.name = options.name;\n if (options.priority !== undefined) this.priority = options.priority;\n if (options.pricingModel !== undefined)\n this.pricingModel = options.pricingModel;\n if (options.description !== undefined)\n this.description = options.description;\n }\n\n /**\n * Check if this tier is higher priority than another\n */\n isHigherPriorityThan(other: AdDeliveryTier): boolean {\n return this.priority < other.priority;\n }\n\n /**\n * Check if this is a fixed pricing tier\n */\n isFixedPricing(): boolean {\n return this.pricingModel === PricingModel.FIXED;\n }\n\n /**\n * Check if this is a performance-based tier (CPC, CPA)\n */\n isPerformanceBased(): boolean {\n return (\n this.pricingModel === PricingModel.CPC ||\n this.pricingModel === PricingModel.CPA\n );\n }\n}\n\nexport default AdDeliveryTier;\n","/**\n * AdDeliveryTierCollection - Collection manager for AdDeliveryTier objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AdDeliveryTier } from '../models/AdDeliveryTier.js';\nimport { PricingModel } from '../types/index.js';\n\nexport class AdDeliveryTierCollection extends SmrtCollection<AdDeliveryTier> {\n static readonly _itemClass = AdDeliveryTier;\n\n /**\n * Find tiers ordered by priority (ascending, lower = higher priority)\n *\n * @returns Array of tiers ordered by priority\n */\n async findByPriority(): Promise<AdDeliveryTier[]> {\n return await this.list({\n orderBy: 'priority ASC',\n });\n }\n\n /**\n * Find tiers by pricing model\n *\n * @param pricingModel - Pricing model to filter by\n * @returns Array of matching tiers\n */\n async findByPricingModel(\n pricingModel: PricingModel,\n ): Promise<AdDeliveryTier[]> {\n return await this.list({\n where: { pricingModel },\n orderBy: 'priority ASC',\n });\n }\n\n /**\n * Get the highest priority tier\n *\n * @returns Highest priority tier or null\n */\n async getHighestPriority(): Promise<AdDeliveryTier | null> {\n const results = await this.list({\n orderBy: 'priority ASC',\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find fixed pricing tiers\n */\n async findFixedPricing(): Promise<AdDeliveryTier[]> {\n return await this.findByPricingModel(PricingModel.FIXED);\n }\n\n /**\n * Find CPM-based tiers\n */\n async findCPM(): Promise<AdDeliveryTier[]> {\n return await this.findByPricingModel(PricingModel.CPM);\n }\n\n /**\n * Find performance-based tiers (CPC, CPA)\n */\n async findPerformanceBased(): Promise<AdDeliveryTier[]> {\n const cpc = await this.findByPricingModel(PricingModel.CPC);\n const cpa = await this.findByPricingModel(PricingModel.CPA);\n return [...cpc, ...cpa].sort((a, b) => a.priority - b.priority);\n }\n}\n","/**\n * AdEvent model - Immutable event tracking (impressions, clicks)\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { AdEventType } from '../types/index.js';\n\n/**\n * AdEvent tracks ad impressions, clicks, and conversions.\n * Events are immutable (append-only, no update/delete).\n *\n * References:\n * - variationId: FK to AdVariation (within package)\n * - zoneId: String reference to smrt-properties Zone (cross-package)\n * - siteId: Denormalized for query efficiency (cross-package)\n *\n * @example\n * ```typescript\n * // Track an impression\n * const impression = await events.create({\n * variationId: variation.id,\n * zoneId: 'zone-uuid',\n * siteId: 'site-uuid',\n * eventType: AdEventType.IMPRESSION,\n * metadata: JSON.stringify({\n * ip: '192.168.1.1',\n * userAgent: 'Mozilla/5.0...',\n * referrer: 'https://example.com'\n * })\n * });\n *\n * // Track a click\n * const click = await events.create({\n * variationId: variation.id,\n * zoneId: 'zone-uuid',\n * siteId: 'site-uuid',\n * eventType: AdEventType.CLICK\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['create', 'list'] }, // No update/delete (immutable)\n mcp: { include: ['create'] },\n cli: false, // High volume, not useful in CLI\n})\nexport class AdEvent extends SmrtObject {\n /**\n * Tenant ID for multi-tenancy support\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Variation ID (FK to AdVariation)\n */\n @foreignKey('AdVariation')\n variationId: string = '';\n\n /**\n * Zone ID (FK to smrt-properties Zone, cross-package)\n */\n @crossPackageRef('@happyvertical/smrt-properties:Zone')\n zoneId: string = '';\n\n /**\n * Site ID (denormalized from Zone for query efficiency)\n */\n siteId: string = '';\n\n /**\n * Event type (impression, click, conversion)\n */\n eventType: AdEventType = AdEventType.IMPRESSION;\n\n /**\n * Event timestamp\n */\n timestamp: Date = new Date();\n\n /**\n * Analytics metadata as JSON string\n * (IP, user agent, referrer, etc.)\n */\n metadata: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.variationId !== undefined)\n this.variationId = options.variationId;\n if (options.zoneId !== undefined) this.zoneId = options.zoneId;\n if (options.siteId !== undefined) this.siteId = options.siteId;\n if (options.eventType !== undefined) this.eventType = options.eventType;\n if (options.timestamp !== undefined) this.timestamp = options.timestamp;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /**\n * Get metadata as object\n */\n getMetadata(): Record<string, any> {\n if (!this.metadata) return {};\n try {\n return JSON.parse(this.metadata);\n } catch {\n return {};\n }\n }\n\n /**\n * Set metadata from object\n */\n setMetadata(data: Record<string, any>): void {\n this.metadata = JSON.stringify(data);\n }\n\n /**\n * Check if this is an impression event\n */\n isImpression(): boolean {\n return this.eventType === AdEventType.IMPRESSION;\n }\n\n /**\n * Check if this is a click event\n */\n isClick(): boolean {\n return this.eventType === AdEventType.CLICK;\n }\n\n /**\n * Check if this is a conversion event\n */\n isConversion(): boolean {\n return this.eventType === AdEventType.CONVERSION;\n }\n}\n\nexport default AdEvent;\n","/**\n * AdEventCollection - Collection manager for AdEvent objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AdEvent } from '../models/AdEvent.js';\nimport { AdEventType } from '../types/index.js';\n\nexport class AdEventCollection extends SmrtCollection<AdEvent> {\n static readonly _itemClass = AdEvent;\n\n /**\n * Find events by variation\n *\n * @param variationId - Variation ID\n * @returns Array of events\n */\n async findByVariation(variationId: string): Promise<AdEvent[]> {\n return await this.list({\n where: { variationId },\n orderBy: 'timestamp DESC',\n });\n }\n\n /**\n * Find events by zone\n *\n * @param zoneId - Zone ID\n * @returns Array of events\n */\n async findByZone(zoneId: string): Promise<AdEvent[]> {\n return await this.list({\n where: { zoneId },\n orderBy: 'timestamp DESC',\n });\n }\n\n /**\n * Find events by site\n *\n * @param siteId - Site ID\n * @returns Array of events\n */\n async findBySite(siteId: string): Promise<AdEvent[]> {\n return await this.list({\n where: { siteId },\n orderBy: 'timestamp DESC',\n });\n }\n\n /**\n * Find events in date range\n *\n * @param start - Start date\n * @param end - End date\n * @returns Array of events\n */\n async findByDateRange(start: Date, end: Date): Promise<AdEvent[]> {\n return await this.list({\n where: {\n 'timestamp >=': start.toISOString(),\n 'timestamp <=': end.toISOString(),\n },\n orderBy: 'timestamp DESC',\n });\n }\n\n /**\n * Find events by type\n *\n * @param eventType - Event type\n * @returns Array of events\n */\n async findByType(eventType: AdEventType): Promise<AdEvent[]> {\n return await this.list({\n where: { eventType },\n orderBy: 'timestamp DESC',\n });\n }\n\n /**\n * Count events by type for a variation\n *\n * @param variationId - Variation ID\n * @param eventType - Event type\n * @returns Count of events\n */\n async countByType(\n variationId: string,\n eventType: AdEventType,\n ): Promise<number> {\n const events = await this.list({\n where: { variationId, eventType },\n });\n return events.length;\n }\n\n /**\n * Count impressions for a variation\n */\n async countImpressions(variationId: string): Promise<number> {\n return await this.countByType(variationId, AdEventType.IMPRESSION);\n }\n\n /**\n * Count clicks for a variation\n */\n async countClicks(variationId: string): Promise<number> {\n return await this.countByType(variationId, AdEventType.CLICK);\n }\n\n /**\n * Count conversions for a variation\n */\n async countConversions(variationId: string): Promise<number> {\n return await this.countByType(variationId, AdEventType.CONVERSION);\n }\n\n /**\n * Find all impressions\n */\n async findImpressions(): Promise<AdEvent[]> {\n return await this.findByType(AdEventType.IMPRESSION);\n }\n\n /**\n * Find all clicks\n */\n async findClicks(): Promise<AdEvent[]> {\n return await this.findByType(AdEventType.CLICK);\n }\n\n /**\n * Find all conversions\n */\n async findConversions(): Promise<AdEvent[]> {\n return await this.findByType(AdEventType.CONVERSION);\n }\n\n /**\n * Get aggregate stats for a variation\n *\n * @param variationId - Variation ID\n * @returns Stats object with impressions, clicks, conversions, CTR\n */\n async getVariationStats(variationId: string): Promise<{\n impressions: number;\n clicks: number;\n conversions: number;\n ctr: number;\n conversionRate: number;\n }> {\n const impressions = await this.countImpressions(variationId);\n const clicks = await this.countClicks(variationId);\n const conversions = await this.countConversions(variationId);\n\n return {\n impressions,\n clicks,\n conversions,\n ctr: impressions > 0 ? clicks / impressions : 0,\n conversionRate: clicks > 0 ? conversions / clicks : 0,\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tenancy Helper Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Find ad events belonging to a specific tenant\n *\n * @param tenantId - Tenant ID to filter by\n * @returns Array of ad events for the tenant\n */\n async findByTenant(tenantId: string): Promise<AdEvent[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find global ad events (no tenant association)\n *\n * @returns Array of global ad events\n */\n async findGlobal(): Promise<AdEvent[]> {\n return this.list({ where: { tenantId: null } });\n }\n\n /**\n * Find ad events for a tenant including global (shared) events\n *\n * @param tenantId - Tenant ID to include\n * @returns Array of tenant-specific and global ad events\n */\n async findWithGlobals(tenantId: string): Promise<AdEvent[]> {\n return this.query(\n `SELECT * FROM ad_events WHERE tenant_id = ? OR tenant_id IS NULL`,\n [tenantId],\n );\n }\n}\n","/**\n * AdFormat model - Standard IAB ad dimensions and types\n * @packageDocumentation\n */\n\nimport { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { AdFormatType } from '../types/index.js';\n\n/**\n * AdFormat defines standard ad dimensions and types (IAB standards).\n *\n * @example\n * ```typescript\n * const leaderboard = await formats.create({\n * name: 'Leaderboard',\n * width: 728,\n * height: 90,\n * formatType: AdFormatType.BANNER\n * });\n * ```\n */\n// Intentional exception to standards.md §7 (`@TenantScoped`):\n// `AdFormat` is a shared catalog model describing IAB standard ad dimensions\n// (e.g. 728x90 Leaderboard). The dimensions are an industry standard, not a\n// per-tenant configuration. Adding `@TenantScoped` would require every tenant\n// to seed the same IAB rows. See `packages/ads/CLAUDE.md` \"Tenancy\" section\n// for context.\n@smrt({\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class AdFormat extends SmrtObject {\n /**\n * Display name (e.g., \"Leaderboard\", \"Medium Rectangle\")\n */\n name: string = '';\n\n /**\n * Width in pixels\n */\n width: number = 0;\n\n /**\n * Height in pixels\n */\n height: number = 0;\n\n /**\n * Format type (banner, native, video)\n */\n formatType: AdFormatType = AdFormatType.BANNER;\n\n /**\n * Optional description\n */\n description: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.name !== undefined) this.name = options.name;\n if (options.width !== undefined) this.width = options.width;\n if (options.height !== undefined) this.height = options.height;\n if (options.formatType !== undefined) this.formatType = options.formatType;\n if (options.description !== undefined)\n this.description = options.description;\n }\n\n /**\n * Get dimensions as string (e.g., \"728x90\")\n */\n getDimensions(): string {\n return `${this.width}x${this.height}`;\n }\n\n /**\n * Check if format matches specific dimensions\n */\n matchesDimensions(width: number, height: number): boolean {\n return this.width === width && this.height === height;\n }\n}\n\nexport default AdFormat;\n","/**\n * AdFormatCollection - Collection manager for AdFormat objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AdFormat } from '../models/AdFormat.js';\nimport { AdFormatType } from '../types/index.js';\n\nexport class AdFormatCollection extends SmrtCollection<AdFormat> {\n static readonly _itemClass = AdFormat;\n\n /**\n * Find format by dimensions\n *\n * @param width - Width in pixels\n * @param height - Height in pixels\n * @returns Matching format or null\n */\n async findByDimensions(\n width: number,\n height: number,\n ): Promise<AdFormat | null> {\n const results = await this.list({\n where: { width, height },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find formats by type\n *\n * @param formatType - Format type (banner, native, video)\n * @returns Array of matching formats\n */\n async findByType(formatType: AdFormatType): Promise<AdFormat[]> {\n return await this.list({\n where: { formatType },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find all banner formats\n */\n async findBanners(): Promise<AdFormat[]> {\n return await this.findByType(AdFormatType.BANNER);\n }\n\n /**\n * Find all native formats\n */\n async findNative(): Promise<AdFormat[]> {\n return await this.findByType(AdFormatType.NATIVE);\n }\n\n /**\n * Find all video formats\n */\n async findVideo(): Promise<AdFormat[]> {\n return await this.findByType(AdFormatType.VIDEO);\n }\n}\n","/**\n * AdGroup model - Campaign targeting and creative organization\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { AdGroupStatus } from '../types/index.js';\n\n/**\n * AdGroup organizes ad creatives with targeting and budget controls.\n *\n * References:\n * - tierId: FK to AdDeliveryTier (within package)\n * - contractId: String reference to smrt-commerce Contract (cross-package)\n * - zoneIds: JSON array of allowed smrt-properties Zone IDs (cross-package)\n * - verticalSlug: Tag slug from smrt-tags (cross-package)\n *\n * @example\n * ```typescript\n * const adGroup = await groups.create({\n * contractId: 'contract-uuid',\n * tierId: tier.id,\n * name: 'Summer Sale - Desktop',\n * verticalSlug: 'retail',\n * targeting: JSON.stringify({ device: 'desktop' }),\n * zoneIds: JSON.stringify(['zone-1', 'zone-2']),\n * dailyBudget: 100.00,\n * totalBudget: 3000.00,\n * startDate: new Date('2024-06-01'),\n * endDate: new Date('2024-08-31')\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: true,\n})\nexport class AdGroup extends SmrtObject {\n /**\n * Tenant ID for multi-tenancy support\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Contract ID (FK to smrt-commerce Contract, cross-package)\n */\n @crossPackageRef('@happyvertical/smrt-commerce:Contract')\n contractId: string = '';\n\n /**\n * Delivery tier ID (FK to AdDeliveryTier)\n */\n @foreignKey('AdDeliveryTier')\n tierId: string = '';\n\n /**\n * Display name (e.g., \"Summer Sale - Desktop\")\n */\n name: string = '';\n\n /**\n * Vertical/category tag slug from smrt-tags (context=\"advertising\")\n */\n verticalSlug: string = '';\n\n /**\n * Targeting rules as JSON string\n */\n targeting: string = '';\n\n /**\n * Allowed Zone IDs as JSON array string (FK to smrt-properties Zone)\n */\n zoneIds: string = '';\n\n /**\n * Campaign start date\n */\n startDate: Date | null = null;\n\n /**\n * Campaign end date\n */\n endDate: Date | null = null;\n\n /**\n * Daily budget limit\n */\n dailyBudget: number = 0.0;\n\n /**\n * Total campaign budget\n */\n totalBudget: number = 0.0;\n\n /**\n * Current status\n */\n status: AdGroupStatus = AdGroupStatus.DRAFT;\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.contractId !== undefined) this.contractId = options.contractId;\n if (options.tierId !== undefined) this.tierId = options.tierId;\n if (options.name !== undefined) this.name = options.name;\n if (options.verticalSlug !== undefined)\n this.verticalSlug = options.verticalSlug;\n if (options.targeting !== undefined) this.targeting = options.targeting;\n if (options.zoneIds !== undefined) this.zoneIds = options.zoneIds;\n if (options.startDate !== undefined) this.startDate = options.startDate;\n if (options.endDate !== undefined) this.endDate = options.endDate;\n if (options.dailyBudget !== undefined)\n this.dailyBudget = options.dailyBudget;\n if (options.totalBudget !== undefined)\n this.totalBudget = options.totalBudget;\n if (options.status !== undefined) this.status = options.status;\n }\n\n /**\n * Get zone IDs as array\n */\n getZoneIds(): string[] {\n if (!this.zoneIds) return [];\n try {\n return JSON.parse(this.zoneIds);\n } catch {\n return [];\n }\n }\n\n /**\n * Set zone IDs from array\n */\n setZoneIds(ids: string[]): void {\n this.zoneIds = JSON.stringify(ids);\n }\n\n /**\n * Add a zone ID\n */\n addZoneId(zoneId: string): void {\n const ids = this.getZoneIds();\n if (!ids.includes(zoneId)) {\n ids.push(zoneId);\n this.setZoneIds(ids);\n }\n }\n\n /**\n * Remove a zone ID\n */\n removeZoneId(zoneId: string): void {\n const ids = this.getZoneIds().filter((id) => id !== zoneId);\n this.setZoneIds(ids);\n }\n\n /**\n * Check if zone ID is allowed\n */\n hasZoneId(zoneId: string): boolean {\n return this.getZoneIds().includes(zoneId);\n }\n\n /**\n * Get targeting rules as object\n */\n getTargeting(): Record<string, any> {\n if (!this.targeting) return {};\n try {\n return JSON.parse(this.targeting);\n } catch {\n return {};\n }\n }\n\n /**\n * Set targeting rules from object\n */\n setTargeting(rules: Record<string, any>): void {\n this.targeting = JSON.stringify(rules);\n }\n\n /**\n * Check if ad group is currently active\n */\n isActive(): boolean {\n if (this.status !== AdGroupStatus.ACTIVE) return false;\n const now = new Date();\n if (this.startDate && now < this.startDate) return false;\n if (this.endDate && now > this.endDate) return false;\n return true;\n }\n\n /**\n * Check if ad group is in draft state\n */\n isDraft(): boolean {\n return this.status === AdGroupStatus.DRAFT;\n }\n\n /**\n * Check if ad group is paused\n */\n isPaused(): boolean {\n return this.status === AdGroupStatus.PAUSED;\n }\n\n /**\n * Check if ad group is completed\n */\n isCompleted(): boolean {\n return this.status === AdGroupStatus.COMPLETED;\n }\n\n /**\n * Check if ad group has ended (past end date)\n */\n hasEnded(): boolean {\n if (!this.endDate) return false;\n return new Date() > this.endDate;\n }\n\n /**\n * Check if ad group has started\n */\n hasStarted(): boolean {\n if (!this.startDate) return true;\n return new Date() >= this.startDate;\n }\n}\n\nexport default AdGroup;\n","/**\n * AdGroupCollection - Collection manager for AdGroup objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AdGroup } from '../models/AdGroup.js';\nimport { AdGroupStatus } from '../types/index.js';\n\nexport class AdGroupCollection extends SmrtCollection<AdGroup> {\n static readonly _itemClass = AdGroup;\n\n /**\n * Find ad groups by contract\n *\n * @param contractId - Contract ID\n * @returns Array of ad groups\n */\n async findByContract(contractId: string): Promise<AdGroup[]> {\n return await this.list({\n where: { contractId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find ad groups by tier\n *\n * @param tierId - Delivery tier ID\n * @returns Array of ad groups\n */\n async findByTier(tierId: string): Promise<AdGroup[]> {\n return await this.list({\n where: { tierId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find ad groups by status\n *\n * @param status - Ad group status\n * @returns Array of ad groups\n */\n async findByStatus(status: AdGroupStatus): Promise<AdGroup[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find currently active ad groups\n * (status=active, started, not ended)\n *\n * @returns Array of active ad groups\n */\n async findActive(): Promise<AdGroup[]> {\n const results = await this.list({\n where: {\n status: AdGroupStatus.ACTIVE,\n },\n orderBy: 'created_at DESC',\n });\n // Filter by date in memory (complex date logic)\n return results.filter((group) => group.isActive());\n }\n\n /**\n * Find ad groups by vertical slug\n *\n * @param verticalSlug - Tag slug from smrt-tags\n * @returns Array of ad groups\n */\n async findByVertical(verticalSlug: string): Promise<AdGroup[]> {\n return await this.list({\n where: { verticalSlug },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find ad groups that can serve to a specific zone\n *\n * @param zoneId - Zone ID to check\n * @returns Array of ad groups containing zone\n */\n async findByZone(zoneId: string): Promise<AdGroup[]> {\n // Must filter in memory since zoneIds is JSON\n const all = await this.list({\n where: { status: AdGroupStatus.ACTIVE },\n });\n return all.filter((group) => group.hasZoneId(zoneId));\n }\n\n /**\n * Find ad groups eligible to serve for a zone\n * (active, has zone, within date range)\n *\n * @param zoneId - Zone ID to check\n * @returns Array of eligible ad groups\n */\n async findEligibleForZone(zoneId: string): Promise<AdGroup[]> {\n const groups = await this.findByZone(zoneId);\n return groups.filter((group) => group.isActive());\n }\n\n /**\n * Find all draft ad groups\n */\n async findDrafts(): Promise<AdGroup[]> {\n return await this.findByStatus(AdGroupStatus.DRAFT);\n }\n\n /**\n * Find all paused ad groups\n */\n async findPaused(): Promise<AdGroup[]> {\n return await this.findByStatus(AdGroupStatus.PAUSED);\n }\n\n /**\n * Find all completed ad groups\n */\n async findCompleted(): Promise<AdGroup[]> {\n return await this.findByStatus(AdGroupStatus.COMPLETED);\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tenancy Helper Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Find ad groups belonging to a specific tenant\n *\n * @param tenantId - Tenant ID to filter by\n * @returns Array of ad groups for the tenant\n */\n async findByTenant(tenantId: string): Promise<AdGroup[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find global ad groups (no tenant association)\n *\n * @returns Array of global ad groups\n */\n async findGlobal(): Promise<AdGroup[]> {\n return this.list({ where: { tenantId: null } });\n }\n\n /**\n * Find ad groups for a tenant including global (shared) ad groups\n *\n * @param tenantId - Tenant ID to include\n * @returns Array of tenant-specific and global ad groups\n */\n async findWithGlobals(tenantId: string): Promise<AdGroup[]> {\n return this.query(\n `SELECT * FROM ad_groups WHERE tenant_id = ? OR tenant_id IS NULL`,\n [tenantId],\n );\n }\n}\n","/**\n * AdVariation model - Creative asset with A/B testing support\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { AdVariationStatus } from '../types/index.js';\n\n/**\n * AdVariation represents a creative asset within an ad group.\n * Supports A/B testing via weighted selection.\n *\n * References:\n * - groupId: FK to AdGroup (within package)\n * - formatId: FK to AdFormat (within package)\n * - assetId: String reference to smrt-assets Asset (cross-package)\n *\n * @example\n * ```typescript\n * const variation = await variations.create({\n * groupId: adGroup.id,\n * formatId: leaderboard.id,\n * assetId: 'asset-uuid',\n * name: 'Version A - Blue CTA',\n * clickUrl: 'https://example.com/landing',\n * altText: 'Summer Sale - 50% off',\n * weight: 2 // 2x more likely than weight=1\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: true,\n})\nexport class AdVariation extends SmrtObject {\n /**\n * Tenant ID for multi-tenancy support\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Ad group ID (FK to AdGroup)\n */\n @foreignKey('AdGroup')\n groupId: string = '';\n\n /**\n * Ad format ID (FK to AdFormat)\n */\n @foreignKey('AdFormat')\n formatId: string = '';\n\n /**\n * Asset ID (FK to smrt-assets Asset, cross-package)\n */\n @crossPackageRef('@happyvertical/smrt-assets:Asset')\n assetId: string = '';\n\n /**\n * Display name (e.g., \"Version A - Blue CTA\")\n */\n name: string = '';\n\n /**\n * Click destination URL\n */\n clickUrl: string = '';\n\n /**\n * Accessibility alt text\n */\n altText: string = '';\n\n /**\n * A/B testing weight (higher = more likely to be selected)\n */\n weight: number = 1;\n\n /**\n * Current status\n */\n status: AdVariationStatus = AdVariationStatus.DRAFT;\n\n /**\n * Denormalized impression count (updated async)\n */\n impressions: number = 0;\n\n /**\n * Denormalized click count (updated async)\n */\n clicks: number = 0;\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.groupId !== undefined) this.groupId = options.groupId;\n if (options.formatId !== undefined) this.formatId = options.formatId;\n if (options.assetId !== undefined) this.assetId = options.assetId;\n if (options.name !== undefined) this.name = options.name;\n if (options.clickUrl !== undefined) this.clickUrl = options.clickUrl;\n if (options.altText !== undefined) this.altText = options.altText;\n if (options.weight !== undefined) this.weight = options.weight;\n if (options.status !== undefined) this.status = options.status;\n if (options.impressions !== undefined)\n this.impressions = options.impressions;\n if (options.clicks !== undefined) this.clicks = options.clicks;\n }\n\n /**\n * Check if variation is active\n */\n isActive(): boolean {\n return this.status === AdVariationStatus.ACTIVE;\n }\n\n /**\n * Check if variation is in draft state\n */\n isDraft(): boolean {\n return this.status === AdVariationStatus.DRAFT;\n }\n\n /**\n * Check if variation is paused\n */\n isPaused(): boolean {\n return this.status === AdVariationStatus.PAUSED;\n }\n\n /**\n * Calculate click-through rate (CTR)\n */\n getCTR(): number {\n if (this.impressions === 0) return 0;\n return this.clicks / this.impressions;\n }\n\n /**\n * Increment impression count\n */\n recordImpression(): void {\n this.impressions += 1;\n }\n\n /**\n * Increment click count\n */\n recordClick(): void {\n this.clicks += 1;\n }\n}\n\nexport default AdVariation;\n","/**\n * AdVariationCollection - Collection manager for AdVariation objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AdVariation } from '../models/AdVariation.js';\nimport { AdVariationStatus } from '../types/index.js';\n\nexport class AdVariationCollection extends SmrtCollection<AdVariation> {\n static readonly _itemClass = AdVariation;\n\n /**\n * Find variations by ad group\n *\n * @param groupId - Ad group ID\n * @returns Array of variations\n */\n async findByGroup(groupId: string): Promise<AdVariation[]> {\n return await this.list({\n where: { groupId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find variations by format\n *\n * @param formatId - Ad format ID\n * @returns Array of variations\n */\n async findByFormat(formatId: string): Promise<AdVariation[]> {\n return await this.list({\n where: { formatId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find active variations for a group\n *\n * @param groupId - Ad group ID\n * @returns Array of active variations\n */\n async findActiveByGroup(groupId: string): Promise<AdVariation[]> {\n return await this.list({\n where: {\n groupId,\n status: AdVariationStatus.ACTIVE,\n },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Select a variation using weighted random selection\n * Higher weight = more likely to be selected\n *\n * @param groupId - Ad group ID\n * @returns Selected variation or null if none available\n */\n async selectByWeight(groupId: string): Promise<AdVariation | null> {\n const active = await this.findActiveByGroup(groupId);\n if (active.length === 0) return null;\n if (active.length === 1) return active[0];\n\n // Calculate total weight\n const totalWeight = active.reduce((sum, v) => sum + v.weight, 0);\n if (totalWeight <= 0) return active[0];\n\n // Random selection weighted by weight\n let random = Math.random() * totalWeight;\n for (const variation of active) {\n random -= variation.weight;\n if (random <= 0) {\n return variation;\n }\n }\n\n // Fallback to last variation\n return active[active.length - 1];\n }\n\n /**\n * Find variations by status\n *\n * @param status - Variation status\n * @returns Array of variations\n */\n async findByStatus(status: AdVariationStatus): Promise<AdVariation[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all active variations\n */\n async findActive(): Promise<AdVariation[]> {\n return await this.findByStatus(AdVariationStatus.ACTIVE);\n }\n\n /**\n * Find all draft variations\n */\n async findDrafts(): Promise<AdVariation[]> {\n return await this.findByStatus(AdVariationStatus.DRAFT);\n }\n\n /**\n * Find all paused variations\n */\n async findPaused(): Promise<AdVariation[]> {\n return await this.findByStatus(AdVariationStatus.PAUSED);\n }\n\n /**\n * Find top performing variations by CTR\n *\n * @param limit - Maximum number to return\n * @returns Array of variations sorted by CTR descending\n */\n async findTopPerformers(limit: number = 10): Promise<AdVariation[]> {\n const all = await this.list({\n where: { status: AdVariationStatus.ACTIVE },\n });\n // Sort by CTR descending\n return all\n .filter((v) => v.impressions > 0)\n .sort((a, b) => b.getCTR() - a.getCTR())\n .slice(0, limit);\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tenancy Helper Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Find ad variations belonging to a specific tenant\n *\n * @param tenantId - Tenant ID to filter by\n * @returns Array of ad variations for the tenant\n */\n async findByTenant(tenantId: string): Promise<AdVariation[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find global ad variations (no tenant association)\n *\n * @returns Array of global ad variations\n */\n async findGlobal(): Promise<AdVariation[]> {\n return this.list({ where: { tenantId: null } });\n }\n\n /**\n * Find ad variations for a tenant including global (shared) variations\n *\n * @param tenantId - Tenant ID to include\n * @returns Array of tenant-specific and global ad variations\n */\n async findWithGlobals(tenantId: string): Promise<AdVariation[]> {\n return this.query(\n `SELECT * FROM ad_variations WHERE tenant_id = ? OR tenant_id IS NULL`,\n [tenantId],\n );\n }\n}\n"],"names":["AdFormatType","PricingModel","AdGroupStatus","AdVariationStatus","AdEventType","__decorateClass","tenantId"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/__smrt-register__.ts","../src/types/index.ts","../src/models/AdDeliveryTier.ts","../src/collections/AdDeliveryTierCollection.ts","../src/models/AdEvent.ts","../src/collections/AdEventCollection.ts","../src/models/AdFormat.ts","../src/collections/AdFormatCollection.ts","../src/models/AdGroup.ts","../src/collections/AdGroupCollection.ts","../src/models/AdVariation.ts","../src/collections/AdVariationCollection.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// `new URL('./manifest.json', import.meta.url)` resolves at runtime to the\n// manifest sitting next to this module's compiled output. Vite warns at build\n// time that it cannot pre-resolve the URL; that is the intended behavior —\n// the URL must resolve to dist/manifest.json at runtime, not be inlined.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","/**\n * Types and enums for smrt-ads package\n */\n\n/**\n * Ad format types for different creative types\n */\nexport enum AdFormatType {\n BANNER = 'banner',\n NATIVE = 'native',\n VIDEO = 'video',\n}\n\n/**\n * Pricing models for ad delivery tiers\n */\nexport enum PricingModel {\n FIXED = 'fixed',\n CPM = 'cpm',\n CPC = 'cpc',\n CPA = 'cpa',\n}\n\n/**\n * Status for ad groups\n */\nexport enum AdGroupStatus {\n DRAFT = 'draft',\n ACTIVE = 'active',\n PAUSED = 'paused',\n COMPLETED = 'completed',\n}\n\n/**\n * Status for ad variations\n */\nexport enum AdVariationStatus {\n DRAFT = 'draft',\n ACTIVE = 'active',\n PAUSED = 'paused',\n}\n\n/**\n * Event types for ad tracking\n */\nexport enum AdEventType {\n IMPRESSION = 'impression',\n CLICK = 'click',\n CONVERSION = 'conversion',\n}\n","/**\n * AdDeliveryTier model - Priority waterfall for ad selection\n * @packageDocumentation\n */\n\nimport { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { PricingModel } from '../types/index.js';\n\n/**\n * AdDeliveryTier defines priority levels for ad serving.\n *\n * Ads are selected by priority (lower = higher priority):\n * 1. Sponsorship - Guaranteed, premium placements\n * 2. Standard - Regular programmatic ads\n * 3. House - Self-promotional/fallback ads\n *\n * @example\n * ```typescript\n * const sponsorship = await tiers.create({\n * name: 'Sponsorship',\n * priority: 1,\n * pricingModel: PricingModel.FIXED,\n * description: 'Premium guaranteed placements'\n * });\n * ```\n */\n// Intentional exception to standards.md §7 (`@TenantScoped`):\n// `AdDeliveryTier` is a shared catalog model describing the priority waterfall\n// for ad selection (Sponsorship → Standard → House). Tier definitions are part\n// of the package's ad-serving contract, not per-tenant configuration. Adding\n// `@TenantScoped` would require every tenant to seed the same three rows and\n// would fragment the selection algorithm without a clear use case. See\n// `packages/ads/CLAUDE.md` \"Tenancy\" section for context.\n@smrt({\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class AdDeliveryTier extends SmrtObject {\n /**\n * Display name (e.g., \"Sponsorship\", \"Standard\", \"House\")\n */\n name: string = '';\n\n /**\n * Priority level (lower = higher priority: 1, 2, 3...)\n */\n priority: number = 0;\n\n /**\n * Pricing model for this tier\n */\n pricingModel: PricingModel = PricingModel.CPM;\n\n /**\n * Optional description\n */\n description: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.name !== undefined) this.name = options.name;\n if (options.priority !== undefined) this.priority = options.priority;\n if (options.pricingModel !== undefined)\n this.pricingModel = options.pricingModel;\n if (options.description !== undefined)\n this.description = options.description;\n }\n\n /**\n * Check if this tier is higher priority than another\n */\n isHigherPriorityThan(other: AdDeliveryTier): boolean {\n return this.priority < other.priority;\n }\n\n /**\n * Check if this is a fixed pricing tier\n */\n isFixedPricing(): boolean {\n return this.pricingModel === PricingModel.FIXED;\n }\n\n /**\n * Check if this is a performance-based tier (CPC, CPA)\n */\n isPerformanceBased(): boolean {\n return (\n this.pricingModel === PricingModel.CPC ||\n this.pricingModel === PricingModel.CPA\n );\n }\n}\n\nexport default AdDeliveryTier;\n","/**\n * AdDeliveryTierCollection - Collection manager for AdDeliveryTier objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AdDeliveryTier } from '../models/AdDeliveryTier.js';\nimport { PricingModel } from '../types/index.js';\n\nexport class AdDeliveryTierCollection extends SmrtCollection<AdDeliveryTier> {\n static readonly _itemClass = AdDeliveryTier;\n\n /**\n * Find tiers ordered by priority (ascending, lower = higher priority)\n *\n * @returns Array of tiers ordered by priority\n */\n async findByPriority(): Promise<AdDeliveryTier[]> {\n return await this.list({\n orderBy: 'priority ASC',\n });\n }\n\n /**\n * Find tiers by pricing model\n *\n * @param pricingModel - Pricing model to filter by\n * @returns Array of matching tiers\n */\n async findByPricingModel(\n pricingModel: PricingModel,\n ): Promise<AdDeliveryTier[]> {\n return await this.list({\n where: { pricingModel },\n orderBy: 'priority ASC',\n });\n }\n\n /**\n * Get the highest priority tier\n *\n * @returns Highest priority tier or null\n */\n async getHighestPriority(): Promise<AdDeliveryTier | null> {\n const results = await this.list({\n orderBy: 'priority ASC',\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find fixed pricing tiers\n */\n async findFixedPricing(): Promise<AdDeliveryTier[]> {\n return await this.findByPricingModel(PricingModel.FIXED);\n }\n\n /**\n * Find CPM-based tiers\n */\n async findCPM(): Promise<AdDeliveryTier[]> {\n return await this.findByPricingModel(PricingModel.CPM);\n }\n\n /**\n * Find performance-based tiers (CPC, CPA)\n */\n async findPerformanceBased(): Promise<AdDeliveryTier[]> {\n const cpc = await this.findByPricingModel(PricingModel.CPC);\n const cpa = await this.findByPricingModel(PricingModel.CPA);\n return [...cpc, ...cpa].sort((a, b) => a.priority - b.priority);\n }\n}\n","/**\n * AdEvent model - Immutable event tracking (impressions, clicks)\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { AdEventType } from '../types/index.js';\n\n/**\n * AdEvent tracks ad impressions, clicks, and conversions.\n * Events are immutable (append-only, no update/delete).\n *\n * References:\n * - variationId: FK to AdVariation (within package)\n * - zoneId: String reference to smrt-properties Zone (cross-package)\n * - siteId: Denormalized for query efficiency (cross-package)\n *\n * @example\n * ```typescript\n * // Track an impression\n * const impression = await events.create({\n * variationId: variation.id,\n * zoneId: 'zone-uuid',\n * siteId: 'site-uuid',\n * eventType: AdEventType.IMPRESSION,\n * metadata: JSON.stringify({\n * ip: '192.168.1.1',\n * userAgent: 'Mozilla/5.0...',\n * referrer: 'https://example.com'\n * })\n * });\n *\n * // Track a click\n * const click = await events.create({\n * variationId: variation.id,\n * zoneId: 'zone-uuid',\n * siteId: 'site-uuid',\n * eventType: AdEventType.CLICK\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['create', 'list'] }, // No update/delete (immutable)\n mcp: { include: ['create'] },\n cli: false, // High volume, not useful in CLI\n})\nexport class AdEvent extends SmrtObject {\n /**\n * Tenant ID for multi-tenancy support\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Variation ID (FK to AdVariation)\n */\n @foreignKey('AdVariation')\n variationId: string = '';\n\n /**\n * Zone ID (FK to smrt-properties Zone, cross-package)\n */\n @crossPackageRef('@happyvertical/smrt-properties:Zone')\n zoneId: string = '';\n\n /**\n * Site ID (denormalized from Zone for query efficiency)\n */\n siteId: string = '';\n\n /**\n * Event type (impression, click, conversion)\n */\n eventType: AdEventType = AdEventType.IMPRESSION;\n\n /**\n * Event timestamp\n */\n timestamp: Date = new Date();\n\n /**\n * Analytics metadata as JSON string\n * (IP, user agent, referrer, etc.)\n */\n metadata: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.variationId !== undefined)\n this.variationId = options.variationId;\n if (options.zoneId !== undefined) this.zoneId = options.zoneId;\n if (options.siteId !== undefined) this.siteId = options.siteId;\n if (options.eventType !== undefined) this.eventType = options.eventType;\n if (options.timestamp !== undefined) this.timestamp = options.timestamp;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /**\n * Get metadata as object\n */\n getMetadata(): Record<string, any> {\n if (!this.metadata) return {};\n try {\n return JSON.parse(this.metadata);\n } catch {\n return {};\n }\n }\n\n /**\n * Set metadata from object\n */\n setMetadata(data: Record<string, any>): void {\n this.metadata = JSON.stringify(data);\n }\n\n /**\n * Check if this is an impression event\n */\n isImpression(): boolean {\n return this.eventType === AdEventType.IMPRESSION;\n }\n\n /**\n * Check if this is a click event\n */\n isClick(): boolean {\n return this.eventType === AdEventType.CLICK;\n }\n\n /**\n * Check if this is a conversion event\n */\n isConversion(): boolean {\n return this.eventType === AdEventType.CONVERSION;\n }\n}\n\nexport default AdEvent;\n","/**\n * AdEventCollection - Collection manager for AdEvent objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { AdEvent } from '../models/AdEvent.js';\nimport { AdEventType } from '../types/index.js';\n\nexport class AdEventCollection extends SmrtCollection<AdEvent> {\n static readonly _itemClass = AdEvent;\n\n /**\n * Find events by variation\n *\n * @param variationId - Variation ID\n * @returns Array of events\n */\n async findByVariation(variationId: string): Promise<AdEvent[]> {\n return await this.list({\n where: { variationId },\n orderBy: 'timestamp DESC',\n });\n }\n\n /**\n * Find events by zone\n *\n * @param zoneId - Zone ID\n * @returns Array of events\n */\n async findByZone(zoneId: string): Promise<AdEvent[]> {\n return await this.list({\n where: { zoneId },\n orderBy: 'timestamp DESC',\n });\n }\n\n /**\n * Find events by site\n *\n * @param siteId - Site ID\n * @returns Array of events\n */\n async findBySite(siteId: string): Promise<AdEvent[]> {\n return await this.list({\n where: { siteId },\n orderBy: 'timestamp DESC',\n });\n }\n\n /**\n * Find events in date range\n *\n * @param start - Start date\n * @param end - End date\n * @returns Array of events\n */\n async findByDateRange(start: Date, end: Date): Promise<AdEvent[]> {\n return await this.list({\n where: {\n 'timestamp >=': start.toISOString(),\n 'timestamp <=': end.toISOString(),\n },\n orderBy: 'timestamp DESC',\n });\n }\n\n /**\n * Find events by type\n *\n * @param eventType - Event type\n * @returns Array of events\n */\n async findByType(eventType: AdEventType): Promise<AdEvent[]> {\n return await this.list({\n where: { eventType },\n orderBy: 'timestamp DESC',\n });\n }\n\n /**\n * Count events by type for a variation\n *\n * @param variationId - Variation ID\n * @param eventType - Event type\n * @returns Count of events\n */\n async countByType(\n variationId: string,\n eventType: AdEventType,\n ): Promise<number> {\n const events = await this.list({\n where: { variationId, eventType },\n });\n return events.length;\n }\n\n /**\n * Count impressions for a variation\n */\n async countImpressions(variationId: string): Promise<number> {\n return await this.countByType(variationId, AdEventType.IMPRESSION);\n }\n\n /**\n * Count clicks for a variation\n */\n async countClicks(variationId: string): Promise<number> {\n return await this.countByType(variationId, AdEventType.CLICK);\n }\n\n /**\n * Count conversions for a variation\n */\n async countConversions(variationId: string): Promise<number> {\n return await this.countByType(variationId, AdEventType.CONVERSION);\n }\n\n /**\n * Find all impressions\n */\n async findImpressions(): Promise<AdEvent[]> {\n return await this.findByType(AdEventType.IMPRESSION);\n }\n\n /**\n * Find all clicks\n */\n async findClicks(): Promise<AdEvent[]> {\n return await this.findByType(AdEventType.CLICK);\n }\n\n /**\n * Find all conversions\n */\n async findConversions(): Promise<AdEvent[]> {\n return await this.findByType(AdEventType.CONVERSION);\n }\n\n /**\n * Get aggregate stats for a variation\n *\n * @param variationId - Variation ID\n * @returns Stats object with impressions, clicks, conversions, CTR\n */\n async getVariationStats(variationId: string): Promise<{\n impressions: number;\n clicks: number;\n conversions: number;\n ctr: number;\n conversionRate: number;\n }> {\n const impressions = await this.countImpressions(variationId);\n const clicks = await this.countClicks(variationId);\n const conversions = await this.countConversions(variationId);\n\n return {\n impressions,\n clicks,\n conversions,\n ctr: impressions > 0 ? clicks / impressions : 0,\n conversionRate: clicks > 0 ? conversions / clicks : 0,\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tenancy Helper Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Find ad events belonging to a specific tenant\n *\n * @param tenantId - Tenant ID to filter by\n * @returns Array of ad events for the tenant\n */\n async findByTenant(tenantId: string): Promise<AdEvent[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find global ad events (no tenant association).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global ad events\n */\n async findGlobal(): Promise<AdEvent[]> {\n return queryGlobal<AdEvent>(this);\n }\n\n /**\n * Find ad events for a tenant including global (shared) events.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - Tenant ID to include\n * @returns Array of tenant-specific and global ad events\n */\n async findWithGlobals(tenantId: string): Promise<AdEvent[]> {\n return queryWithGlobals<AdEvent>(this, tenantId, 'AdEvent.findWithGlobals');\n }\n}\n","/**\n * AdFormat model - Standard IAB ad dimensions and types\n * @packageDocumentation\n */\n\nimport { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { AdFormatType } from '../types/index.js';\n\n/**\n * AdFormat defines standard ad dimensions and types (IAB standards).\n *\n * @example\n * ```typescript\n * const leaderboard = await formats.create({\n * name: 'Leaderboard',\n * width: 728,\n * height: 90,\n * formatType: AdFormatType.BANNER\n * });\n * ```\n */\n// Intentional exception to standards.md §7 (`@TenantScoped`):\n// `AdFormat` is a shared catalog model describing IAB standard ad dimensions\n// (e.g. 728x90 Leaderboard). The dimensions are an industry standard, not a\n// per-tenant configuration. Adding `@TenantScoped` would require every tenant\n// to seed the same IAB rows. See `packages/ads/CLAUDE.md` \"Tenancy\" section\n// for context.\n@smrt({\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class AdFormat extends SmrtObject {\n /**\n * Display name (e.g., \"Leaderboard\", \"Medium Rectangle\")\n */\n name: string = '';\n\n /**\n * Width in pixels\n */\n width: number = 0;\n\n /**\n * Height in pixels\n */\n height: number = 0;\n\n /**\n * Format type (banner, native, video)\n */\n formatType: AdFormatType = AdFormatType.BANNER;\n\n /**\n * Optional description\n */\n description: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.name !== undefined) this.name = options.name;\n if (options.width !== undefined) this.width = options.width;\n if (options.height !== undefined) this.height = options.height;\n if (options.formatType !== undefined) this.formatType = options.formatType;\n if (options.description !== undefined)\n this.description = options.description;\n }\n\n /**\n * Get dimensions as string (e.g., \"728x90\")\n */\n getDimensions(): string {\n return `${this.width}x${this.height}`;\n }\n\n /**\n * Check if format matches specific dimensions\n */\n matchesDimensions(width: number, height: number): boolean {\n return this.width === width && this.height === height;\n }\n}\n\nexport default AdFormat;\n","/**\n * AdFormatCollection - Collection manager for AdFormat objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AdFormat } from '../models/AdFormat.js';\nimport { AdFormatType } from '../types/index.js';\n\nexport class AdFormatCollection extends SmrtCollection<AdFormat> {\n static readonly _itemClass = AdFormat;\n\n /**\n * Find format by dimensions\n *\n * @param width - Width in pixels\n * @param height - Height in pixels\n * @returns Matching format or null\n */\n async findByDimensions(\n width: number,\n height: number,\n ): Promise<AdFormat | null> {\n const results = await this.list({\n where: { width, height },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find formats by type\n *\n * @param formatType - Format type (banner, native, video)\n * @returns Array of matching formats\n */\n async findByType(formatType: AdFormatType): Promise<AdFormat[]> {\n return await this.list({\n where: { formatType },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find all banner formats\n */\n async findBanners(): Promise<AdFormat[]> {\n return await this.findByType(AdFormatType.BANNER);\n }\n\n /**\n * Find all native formats\n */\n async findNative(): Promise<AdFormat[]> {\n return await this.findByType(AdFormatType.NATIVE);\n }\n\n /**\n * Find all video formats\n */\n async findVideo(): Promise<AdFormat[]> {\n return await this.findByType(AdFormatType.VIDEO);\n }\n}\n","/**\n * AdGroup model - Campaign targeting and creative organization\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { AdGroupStatus } from '../types/index.js';\n\n/**\n * AdGroup organizes ad creatives with targeting and budget controls.\n *\n * References:\n * - tierId: FK to AdDeliveryTier (within package)\n * - contractId: String reference to smrt-commerce Contract (cross-package)\n * - zoneIds: JSON array of allowed smrt-properties Zone IDs (cross-package)\n * - verticalSlug: Tag slug from smrt-tags (cross-package)\n *\n * @example\n * ```typescript\n * const adGroup = await groups.create({\n * contractId: 'contract-uuid',\n * tierId: tier.id,\n * name: 'Summer Sale - Desktop',\n * verticalSlug: 'retail',\n * targeting: JSON.stringify({ device: 'desktop' }),\n * zoneIds: JSON.stringify(['zone-1', 'zone-2']),\n * dailyBudget: 100.00,\n * totalBudget: 3000.00,\n * startDate: new Date('2024-06-01'),\n * endDate: new Date('2024-08-31')\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: true,\n})\nexport class AdGroup extends SmrtObject {\n /**\n * Tenant ID for multi-tenancy support\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Contract ID (FK to smrt-commerce Contract, cross-package)\n */\n @crossPackageRef('@happyvertical/smrt-commerce:Contract')\n contractId: string = '';\n\n /**\n * Delivery tier ID (FK to AdDeliveryTier)\n */\n @foreignKey('AdDeliveryTier')\n tierId: string = '';\n\n /**\n * Display name (e.g., \"Summer Sale - Desktop\")\n */\n name: string = '';\n\n /**\n * Vertical/category tag slug from smrt-tags (context=\"advertising\")\n */\n verticalSlug: string = '';\n\n /**\n * Targeting rules as JSON string\n */\n targeting: string = '';\n\n /**\n * Allowed Zone IDs as JSON array string (FK to smrt-properties Zone)\n */\n zoneIds: string = '';\n\n /**\n * Campaign start date\n */\n startDate: Date | null = null;\n\n /**\n * Campaign end date\n */\n endDate: Date | null = null;\n\n /**\n * Daily budget limit\n */\n dailyBudget: number = 0.0;\n\n /**\n * Total campaign budget\n */\n totalBudget: number = 0.0;\n\n /**\n * Current status\n */\n status: AdGroupStatus = AdGroupStatus.DRAFT;\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.contractId !== undefined) this.contractId = options.contractId;\n if (options.tierId !== undefined) this.tierId = options.tierId;\n if (options.name !== undefined) this.name = options.name;\n if (options.verticalSlug !== undefined)\n this.verticalSlug = options.verticalSlug;\n if (options.targeting !== undefined) this.targeting = options.targeting;\n if (options.zoneIds !== undefined) this.zoneIds = options.zoneIds;\n if (options.startDate !== undefined) this.startDate = options.startDate;\n if (options.endDate !== undefined) this.endDate = options.endDate;\n if (options.dailyBudget !== undefined)\n this.dailyBudget = options.dailyBudget;\n if (options.totalBudget !== undefined)\n this.totalBudget = options.totalBudget;\n if (options.status !== undefined) this.status = options.status;\n }\n\n /**\n * Get zone IDs as array\n */\n getZoneIds(): string[] {\n if (!this.zoneIds) return [];\n try {\n return JSON.parse(this.zoneIds);\n } catch {\n return [];\n }\n }\n\n /**\n * Set zone IDs from array\n */\n setZoneIds(ids: string[]): void {\n this.zoneIds = JSON.stringify(ids);\n }\n\n /**\n * Add a zone ID\n */\n addZoneId(zoneId: string): void {\n const ids = this.getZoneIds();\n if (!ids.includes(zoneId)) {\n ids.push(zoneId);\n this.setZoneIds(ids);\n }\n }\n\n /**\n * Remove a zone ID\n */\n removeZoneId(zoneId: string): void {\n const ids = this.getZoneIds().filter((id) => id !== zoneId);\n this.setZoneIds(ids);\n }\n\n /**\n * Check if zone ID is allowed\n */\n hasZoneId(zoneId: string): boolean {\n return this.getZoneIds().includes(zoneId);\n }\n\n /**\n * Get targeting rules as object\n */\n getTargeting(): Record<string, any> {\n if (!this.targeting) return {};\n try {\n return JSON.parse(this.targeting);\n } catch {\n return {};\n }\n }\n\n /**\n * Set targeting rules from object\n */\n setTargeting(rules: Record<string, any>): void {\n this.targeting = JSON.stringify(rules);\n }\n\n /**\n * Check if ad group is currently active\n */\n isActive(): boolean {\n if (this.status !== AdGroupStatus.ACTIVE) return false;\n const now = new Date();\n if (this.startDate && now < this.startDate) return false;\n if (this.endDate && now > this.endDate) return false;\n return true;\n }\n\n /**\n * Check if ad group is in draft state\n */\n isDraft(): boolean {\n return this.status === AdGroupStatus.DRAFT;\n }\n\n /**\n * Check if ad group is paused\n */\n isPaused(): boolean {\n return this.status === AdGroupStatus.PAUSED;\n }\n\n /**\n * Check if ad group is completed\n */\n isCompleted(): boolean {\n return this.status === AdGroupStatus.COMPLETED;\n }\n\n /**\n * Check if ad group has ended (past end date)\n */\n hasEnded(): boolean {\n if (!this.endDate) return false;\n return new Date() > this.endDate;\n }\n\n /**\n * Check if ad group has started\n */\n hasStarted(): boolean {\n if (!this.startDate) return true;\n return new Date() >= this.startDate;\n }\n}\n\nexport default AdGroup;\n","/**\n * AdGroupCollection - Collection manager for AdGroup objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { AdGroup } from '../models/AdGroup.js';\nimport { AdGroupStatus } from '../types/index.js';\n\nexport class AdGroupCollection extends SmrtCollection<AdGroup> {\n static readonly _itemClass = AdGroup;\n\n /**\n * Find ad groups by contract\n *\n * @param contractId - Contract ID\n * @returns Array of ad groups\n */\n async findByContract(contractId: string): Promise<AdGroup[]> {\n return await this.list({\n where: { contractId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find ad groups by tier\n *\n * @param tierId - Delivery tier ID\n * @returns Array of ad groups\n */\n async findByTier(tierId: string): Promise<AdGroup[]> {\n return await this.list({\n where: { tierId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find ad groups by status\n *\n * @param status - Ad group status\n * @returns Array of ad groups\n */\n async findByStatus(status: AdGroupStatus): Promise<AdGroup[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find currently active ad groups\n * (status=active, started, not ended)\n *\n * @returns Array of active ad groups\n */\n async findActive(): Promise<AdGroup[]> {\n const results = await this.list({\n where: {\n status: AdGroupStatus.ACTIVE,\n },\n orderBy: 'created_at DESC',\n });\n // Filter by date in memory (complex date logic)\n return results.filter((group) => group.isActive());\n }\n\n /**\n * Find ad groups by vertical slug\n *\n * @param verticalSlug - Tag slug from smrt-tags\n * @returns Array of ad groups\n */\n async findByVertical(verticalSlug: string): Promise<AdGroup[]> {\n return await this.list({\n where: { verticalSlug },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find ad groups that can serve to a specific zone\n *\n * @param zoneId - Zone ID to check\n * @returns Array of ad groups containing zone\n */\n async findByZone(zoneId: string): Promise<AdGroup[]> {\n // Must filter in memory since zoneIds is JSON\n const all = await this.list({\n where: { status: AdGroupStatus.ACTIVE },\n });\n return all.filter((group) => group.hasZoneId(zoneId));\n }\n\n /**\n * Find ad groups eligible to serve for a zone\n * (active, has zone, within date range)\n *\n * @param zoneId - Zone ID to check\n * @returns Array of eligible ad groups\n */\n async findEligibleForZone(zoneId: string): Promise<AdGroup[]> {\n const groups = await this.findByZone(zoneId);\n return groups.filter((group) => group.isActive());\n }\n\n /**\n * Find all draft ad groups\n */\n async findDrafts(): Promise<AdGroup[]> {\n return await this.findByStatus(AdGroupStatus.DRAFT);\n }\n\n /**\n * Find all paused ad groups\n */\n async findPaused(): Promise<AdGroup[]> {\n return await this.findByStatus(AdGroupStatus.PAUSED);\n }\n\n /**\n * Find all completed ad groups\n */\n async findCompleted(): Promise<AdGroup[]> {\n return await this.findByStatus(AdGroupStatus.COMPLETED);\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tenancy Helper Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Find ad groups belonging to a specific tenant\n *\n * @param tenantId - Tenant ID to filter by\n * @returns Array of ad groups for the tenant\n */\n async findByTenant(tenantId: string): Promise<AdGroup[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find global ad groups (no tenant association).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global ad groups\n */\n async findGlobal(): Promise<AdGroup[]> {\n return queryGlobal<AdGroup>(this);\n }\n\n /**\n * Find ad groups for a tenant including global (shared) ad groups.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - Tenant ID to include\n * @returns Array of tenant-specific and global ad groups\n */\n async findWithGlobals(tenantId: string): Promise<AdGroup[]> {\n return queryWithGlobals<AdGroup>(this, tenantId, 'AdGroup.findWithGlobals');\n }\n}\n","/**\n * AdVariation model - Creative asset with A/B testing support\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { AdVariationStatus } from '../types/index.js';\n\n/**\n * AdVariation represents a creative asset within an ad group.\n * Supports A/B testing via weighted selection.\n *\n * References:\n * - groupId: FK to AdGroup (within package)\n * - formatId: FK to AdFormat (within package)\n * - assetId: String reference to smrt-assets Asset (cross-package)\n *\n * @example\n * ```typescript\n * const variation = await variations.create({\n * groupId: adGroup.id,\n * formatId: leaderboard.id,\n * assetId: 'asset-uuid',\n * name: 'Version A - Blue CTA',\n * clickUrl: 'https://example.com/landing',\n * altText: 'Summer Sale - 50% off',\n * weight: 2 // 2x more likely than weight=1\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: true,\n})\nexport class AdVariation extends SmrtObject {\n /**\n * Tenant ID for multi-tenancy support\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Ad group ID (FK to AdGroup)\n */\n @foreignKey('AdGroup')\n groupId: string = '';\n\n /**\n * Ad format ID (FK to AdFormat)\n */\n @foreignKey('AdFormat')\n formatId: string = '';\n\n /**\n * Asset ID (FK to smrt-assets Asset, cross-package)\n */\n @crossPackageRef('@happyvertical/smrt-assets:Asset')\n assetId: string = '';\n\n /**\n * Display name (e.g., \"Version A - Blue CTA\")\n */\n name: string = '';\n\n /**\n * Click destination URL\n */\n clickUrl: string = '';\n\n /**\n * Accessibility alt text\n */\n altText: string = '';\n\n /**\n * A/B testing weight (higher = more likely to be selected)\n */\n weight: number = 1;\n\n /**\n * Current status\n */\n status: AdVariationStatus = AdVariationStatus.DRAFT;\n\n /**\n * Denormalized impression count (updated async)\n */\n impressions: number = 0;\n\n /**\n * Denormalized click count (updated async)\n */\n clicks: number = 0;\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.groupId !== undefined) this.groupId = options.groupId;\n if (options.formatId !== undefined) this.formatId = options.formatId;\n if (options.assetId !== undefined) this.assetId = options.assetId;\n if (options.name !== undefined) this.name = options.name;\n if (options.clickUrl !== undefined) this.clickUrl = options.clickUrl;\n if (options.altText !== undefined) this.altText = options.altText;\n if (options.weight !== undefined) this.weight = options.weight;\n if (options.status !== undefined) this.status = options.status;\n if (options.impressions !== undefined)\n this.impressions = options.impressions;\n if (options.clicks !== undefined) this.clicks = options.clicks;\n }\n\n /**\n * Check if variation is active\n */\n isActive(): boolean {\n return this.status === AdVariationStatus.ACTIVE;\n }\n\n /**\n * Check if variation is in draft state\n */\n isDraft(): boolean {\n return this.status === AdVariationStatus.DRAFT;\n }\n\n /**\n * Check if variation is paused\n */\n isPaused(): boolean {\n return this.status === AdVariationStatus.PAUSED;\n }\n\n /**\n * Calculate click-through rate (CTR)\n */\n getCTR(): number {\n if (this.impressions === 0) return 0;\n return this.clicks / this.impressions;\n }\n\n /**\n * Increment impression count\n */\n recordImpression(): void {\n this.impressions += 1;\n }\n\n /**\n * Increment click count\n */\n recordClick(): void {\n this.clicks += 1;\n }\n}\n\nexport default AdVariation;\n","/**\n * AdVariationCollection - Collection manager for AdVariation objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { AdVariation } from '../models/AdVariation.js';\nimport { AdVariationStatus } from '../types/index.js';\n\nexport class AdVariationCollection extends SmrtCollection<AdVariation> {\n static readonly _itemClass = AdVariation;\n\n /**\n * Find variations by ad group\n *\n * @param groupId - Ad group ID\n * @returns Array of variations\n */\n async findByGroup(groupId: string): Promise<AdVariation[]> {\n return await this.list({\n where: { groupId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find variations by format\n *\n * @param formatId - Ad format ID\n * @returns Array of variations\n */\n async findByFormat(formatId: string): Promise<AdVariation[]> {\n return await this.list({\n where: { formatId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find active variations for a group\n *\n * @param groupId - Ad group ID\n * @returns Array of active variations\n */\n async findActiveByGroup(groupId: string): Promise<AdVariation[]> {\n return await this.list({\n where: {\n groupId,\n status: AdVariationStatus.ACTIVE,\n },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Select a variation using weighted random selection\n * Higher weight = more likely to be selected\n *\n * @param groupId - Ad group ID\n * @returns Selected variation or null if none available\n */\n async selectByWeight(groupId: string): Promise<AdVariation | null> {\n const active = await this.findActiveByGroup(groupId);\n if (active.length === 0) return null;\n if (active.length === 1) return active[0];\n\n // Calculate total weight\n const totalWeight = active.reduce((sum, v) => sum + v.weight, 0);\n if (totalWeight <= 0) return active[0];\n\n // Random selection weighted by weight\n let random = Math.random() * totalWeight;\n for (const variation of active) {\n random -= variation.weight;\n if (random <= 0) {\n return variation;\n }\n }\n\n // Fallback to last variation\n return active[active.length - 1];\n }\n\n /**\n * Find variations by status\n *\n * @param status - Variation status\n * @returns Array of variations\n */\n async findByStatus(status: AdVariationStatus): Promise<AdVariation[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all active variations\n */\n async findActive(): Promise<AdVariation[]> {\n return await this.findByStatus(AdVariationStatus.ACTIVE);\n }\n\n /**\n * Find all draft variations\n */\n async findDrafts(): Promise<AdVariation[]> {\n return await this.findByStatus(AdVariationStatus.DRAFT);\n }\n\n /**\n * Find all paused variations\n */\n async findPaused(): Promise<AdVariation[]> {\n return await this.findByStatus(AdVariationStatus.PAUSED);\n }\n\n /**\n * Find top performing variations by CTR\n *\n * @param limit - Maximum number to return\n * @returns Array of variations sorted by CTR descending\n */\n async findTopPerformers(limit: number = 10): Promise<AdVariation[]> {\n const all = await this.list({\n where: { status: AdVariationStatus.ACTIVE },\n });\n // Sort by CTR descending\n return all\n .filter((v) => v.impressions > 0)\n .sort((a, b) => b.getCTR() - a.getCTR())\n .slice(0, limit);\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tenancy Helper Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Find ad variations belonging to a specific tenant\n *\n * @param tenantId - Tenant ID to filter by\n * @returns Array of ad variations for the tenant\n */\n async findByTenant(tenantId: string): Promise<AdVariation[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find global ad variations (no tenant association).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global ad variations\n */\n async findGlobal(): Promise<AdVariation[]> {\n return queryGlobal<AdVariation>(this);\n }\n\n /**\n * Find ad variations for a tenant including global (shared) variations.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - Tenant ID to include\n * @returns Array of tenant-specific and global ad variations\n */\n async findWithGlobals(tenantId: string): Promise<AdVariation[]> {\n return queryWithGlobals<AdVariation>(\n this,\n tenantId,\n 'AdVariation.findWithGlobals',\n );\n }\n}\n"],"names":["AdFormatType","PricingModel","AdGroupStatus","AdVariationStatus","AdEventType","__decorateClass","tenantId"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": "1.0.0",
3
- "timestamp": 1782241014135,
3
+ "timestamp": 1782248748973,
4
4
  "packageName": "@happyvertical/smrt-ads",
5
- "packageVersion": "0.34.0",
5
+ "packageVersion": "0.34.1",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-ads:AdDeliveryTierCollection": {
8
8
  "name": "addeliverytiercollection",
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-06-23T18:56:55.550Z",
3
+ "generatedAt": "2026-06-23T21:05:50.577Z",
4
4
  "packageName": "@happyvertical/smrt-ads",
5
- "packageVersion": "0.34.0",
5
+ "packageVersion": "0.34.1",
6
6
  "sourceManifestPath": "dist/manifest.json",
7
7
  "agentDocPath": "AGENTS.md",
8
8
  "sourceHashes": {
9
- "manifest": "0d93e92a94454d657f9516886f8bc2bf0ff35ed104a85ae1a34fcf6cec42668e",
10
- "packageJson": "d1a262209bce8fd73a6e778d37fd9e44b025b20fb73674eb1922ee33d4a7819c",
9
+ "manifest": "8dfdbe713892c63fa5eb3e0c92a51ebea413d4880696efba34816b03d74ec18d",
10
+ "packageJson": "3575a6094c2272fe1bee84cb9cb12db1f197b5934f9d895c3f09704c7f560b46",
11
11
  "agents": "38d00ca384af711a76359907e4d9806733168d7b4b31f7610cd0c418fc72d566"
12
12
  },
13
13
  "exports": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-ads",
3
- "version": "0.34.0",
3
+ "version": "0.34.1",
4
4
  "description": "Advertising delivery and tracking models for SMRT framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,8 +19,8 @@
19
19
  "./manifest.json": "./dist/manifest.json"
20
20
  },
21
21
  "dependencies": {
22
- "@happyvertical/smrt-core": "0.34.0",
23
- "@happyvertical/smrt-tenancy": "0.34.0"
22
+ "@happyvertical/smrt-core": "0.34.1",
23
+ "@happyvertical/smrt-tenancy": "0.34.1"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@happyvertical/sql": "^0.74.7",
@@ -29,8 +29,8 @@
29
29
  "typescript": "^5.9.3",
30
30
  "vite": "^7.3.1",
31
31
  "vitest": "^4.0.17",
32
- "@happyvertical/smrt-vitest": "0.34.0",
33
- "@happyvertical/smrt-commerce": "0.34.0"
32
+ "@happyvertical/smrt-vitest": "0.34.1",
33
+ "@happyvertical/smrt-commerce": "0.34.1"
34
34
  },
35
35
  "keywords": [
36
36
  "ai",