@endora-commerce/mod-meta-ads 0.0.0-stage → 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -2
  3. package/dist/admin/api/meta-ads-client.d.ts +32 -0
  4. package/dist/admin/api/meta-ads-client.d.ts.map +1 -0
  5. package/dist/admin/api/meta-ads-client.js +34 -0
  6. package/dist/admin/api/meta-ads-client.js.map +1 -0
  7. package/dist/admin/index.d.ts +30 -0
  8. package/dist/admin/index.d.ts.map +1 -0
  9. package/dist/admin/index.js +59 -0
  10. package/dist/admin/index.js.map +1 -0
  11. package/dist/admin/pages/CustomEventMappingEditPage.d.ts +4 -0
  12. package/dist/admin/pages/CustomEventMappingEditPage.d.ts.map +1 -0
  13. package/dist/admin/pages/CustomEventMappingEditPage.js +85 -0
  14. package/dist/admin/pages/CustomEventMappingEditPage.js.map +1 -0
  15. package/dist/admin/pages/CustomEventMappingsListPage.d.ts +7 -0
  16. package/dist/admin/pages/CustomEventMappingsListPage.d.ts.map +1 -0
  17. package/dist/admin/pages/CustomEventMappingsListPage.js +90 -0
  18. package/dist/admin/pages/CustomEventMappingsListPage.js.map +1 -0
  19. package/dist/backend/entities/meta-custom-event-mapping.entity.d.ts +24 -0
  20. package/dist/backend/entities/meta-custom-event-mapping.entity.d.ts.map +1 -0
  21. package/dist/backend/entities/meta-custom-event-mapping.entity.js +75 -0
  22. package/dist/backend/entities/meta-custom-event-mapping.entity.js.map +1 -0
  23. package/dist/backend/index.d.ts +67 -0
  24. package/dist/backend/index.d.ts.map +1 -0
  25. package/dist/backend/index.js +56 -0
  26. package/dist/backend/index.js.map +1 -0
  27. package/dist/backend/routes.admin.d.ts +16 -0
  28. package/dist/backend/routes.admin.d.ts.map +1 -0
  29. package/dist/backend/routes.admin.js +31 -0
  30. package/dist/backend/routes.admin.js.map +1 -0
  31. package/dist/backend/routes.storefront.d.ts +11 -0
  32. package/dist/backend/routes.storefront.d.ts.map +1 -0
  33. package/dist/backend/routes.storefront.js +13 -0
  34. package/dist/backend/routes.storefront.js.map +1 -0
  35. package/dist/backend/services/custom-event-mappings.service.d.ts +40 -0
  36. package/dist/backend/services/custom-event-mappings.service.d.ts.map +1 -0
  37. package/dist/backend/services/custom-event-mappings.service.js +134 -0
  38. package/dist/backend/services/custom-event-mappings.service.js.map +1 -0
  39. package/dist/backend/services/meta-config.service.d.ts +22 -0
  40. package/dist/backend/services/meta-config.service.d.ts.map +1 -0
  41. package/dist/backend/services/meta-config.service.js +55 -0
  42. package/dist/backend/services/meta-config.service.js.map +1 -0
  43. package/dist/manifest.d.ts +194 -0
  44. package/dist/manifest.d.ts.map +1 -0
  45. package/dist/manifest.js +102 -0
  46. package/dist/manifest.js.map +1 -0
  47. package/dist/migrations/20260728T002715_meta_ads_init.d.ts +12 -0
  48. package/dist/migrations/20260728T002715_meta_ads_init.d.ts.map +1 -0
  49. package/dist/migrations/20260728T002715_meta_ads_init.js +35 -0
  50. package/dist/migrations/20260728T002715_meta_ads_init.js.map +1 -0
  51. package/dist/migrations/index.d.ts +27 -0
  52. package/dist/migrations/index.d.ts.map +1 -0
  53. package/dist/migrations/index.js +29 -0
  54. package/dist/migrations/index.js.map +1 -0
  55. package/docs/meta-ads.md +82 -0
  56. package/i18n/en.json +35 -0
  57. package/i18n/pl.json +35 -0
  58. package/package.json +89 -3
  59. package/tailwind.css +14 -0
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Meta Ads module (feature 064). Puts the Meta Pixel on the storefront per sales
3
+ * channel, reports Meta's standard commerce events, and lets an operator add
4
+ * custom events alongside them.
5
+ *
6
+ * Per-channel configuration is stored through the Settings module. Server-side
7
+ * reporting through Meta's Conversions API is deliberately out of v1 — see
8
+ * specs/064-meta-ads/research.md §R7.
9
+ */
10
+ export declare const metaAdsSettingsManifest: {
11
+ moduleCode: string;
12
+ groups: {
13
+ code: string;
14
+ name: string;
15
+ salesChannelCodes?: string[] | undefined;
16
+ isSystemProtected?: boolean | undefined;
17
+ }[];
18
+ settings: {
19
+ code: string;
20
+ name: string;
21
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
22
+ defaultValue: unknown;
23
+ description?: string | undefined;
24
+ groupCode?: string | undefined;
25
+ previousDefaultValues?: unknown[] | undefined;
26
+ salesChannelCodes?: string[] | undefined;
27
+ enumOptions?: string[] | undefined;
28
+ configurationType?: string | undefined;
29
+ hidden?: boolean | undefined;
30
+ }[];
31
+ };
32
+ export declare const META_ADS_READ_PERMISSION = "meta_ads:read";
33
+ export declare const META_ADS_WRITE_PERMISSION = "meta_ads:write";
34
+ export declare const manifest: {
35
+ id: string;
36
+ name: string;
37
+ version: string;
38
+ dependencies: string[];
39
+ description?: string | undefined;
40
+ acknowledgedDependencies?: {
41
+ moduleId: string;
42
+ port: string;
43
+ reason: string;
44
+ }[] | undefined;
45
+ nonBindingDependencies?: {
46
+ moduleId: string;
47
+ name: string;
48
+ kind: "contributes-to" | "degrades-without" | "refuses-without";
49
+ reason: string;
50
+ whenAbsent?: string | undefined;
51
+ }[] | undefined;
52
+ activation?: {
53
+ settingCode: string;
54
+ default: boolean;
55
+ } | {
56
+ nonDeactivatable: true;
57
+ reason: string;
58
+ } | undefined;
59
+ settings?: {
60
+ moduleCode: string;
61
+ groups: {
62
+ code: string;
63
+ name: string;
64
+ salesChannelCodes?: string[] | undefined;
65
+ isSystemProtected?: boolean | undefined;
66
+ }[];
67
+ settings: {
68
+ code: string;
69
+ name: string;
70
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
71
+ defaultValue: unknown;
72
+ description?: string | undefined;
73
+ groupCode?: string | undefined;
74
+ previousDefaultValues?: unknown[] | undefined;
75
+ salesChannelCodes?: string[] | undefined;
76
+ enumOptions?: string[] | undefined;
77
+ configurationType?: string | undefined;
78
+ hidden?: boolean | undefined;
79
+ }[];
80
+ } | undefined;
81
+ i18n?: {
82
+ bundlesDir: string;
83
+ } | undefined;
84
+ docs?: false | {
85
+ dir: string;
86
+ } | undefined;
87
+ demo?: false | {
88
+ summary: string;
89
+ seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
90
+ reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
91
+ after?: readonly string[] | undefined;
92
+ package?: string | undefined;
93
+ } | undefined;
94
+ actions?: {
95
+ id: string;
96
+ labelKey: string;
97
+ icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
98
+ targetRoute: string;
99
+ keywords: string[];
100
+ weight: number;
101
+ descriptionKey?: string | undefined;
102
+ requiredPermission?: string | undefined;
103
+ }[] | undefined;
104
+ permissions?: {
105
+ code: string;
106
+ label: string;
107
+ module?: string | undefined;
108
+ description?: string | undefined;
109
+ requires?: string[] | undefined;
110
+ }[] | undefined;
111
+ transactionalEmails?: {
112
+ code: string;
113
+ name: string;
114
+ variables: {
115
+ key: string;
116
+ label: string;
117
+ sampleValue?: string | undefined;
118
+ description?: string | undefined;
119
+ }[];
120
+ description?: string | undefined;
121
+ group?: string | undefined;
122
+ }[] | undefined;
123
+ capabilities?: string[] | undefined;
124
+ exclusiveCapabilities?: {
125
+ key: string;
126
+ errorCode: string;
127
+ }[] | undefined;
128
+ errorCodes?: {
129
+ code: string;
130
+ tokens?: string[] | undefined;
131
+ }[] | undefined;
132
+ blocks?: {
133
+ name: string;
134
+ labelKey: string;
135
+ category: string;
136
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
137
+ fields: Record<string, {
138
+ type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
139
+ label?: string | undefined;
140
+ required?: boolean | undefined;
141
+ options?: {
142
+ label: string;
143
+ value: string | number;
144
+ }[] | undefined;
145
+ refKind?: string | undefined;
146
+ }>;
147
+ descriptionKey?: string | undefined;
148
+ defaultProps?: Record<string, unknown> | undefined;
149
+ responsiveFields?: string[] | undefined;
150
+ previewIcon?: string | undefined;
151
+ weight?: number | undefined;
152
+ }[] | undefined;
153
+ blockCategories?: {
154
+ key: string;
155
+ titleKey: string;
156
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
157
+ weight?: number | undefined;
158
+ visible?: boolean | undefined;
159
+ }[] | undefined;
160
+ env?: {
161
+ name: string;
162
+ describes: {
163
+ en: string;
164
+ pl: string;
165
+ };
166
+ requirement: {
167
+ kind: "required";
168
+ } | {
169
+ kind: "requiredWhen";
170
+ input: string;
171
+ equals: string;
172
+ } | {
173
+ kind: "optional";
174
+ without: {
175
+ en: string;
176
+ pl: string;
177
+ };
178
+ };
179
+ secret: boolean;
180
+ generable: boolean;
181
+ owner: {
182
+ kind: "platform";
183
+ } | {
184
+ kind: "application";
185
+ application: "admin" | "backend" | "storefront";
186
+ } | {
187
+ kind: "module";
188
+ moduleId: string;
189
+ };
190
+ consumers: ("admin" | "backend" | "storefront")[];
191
+ addressOf: "admin" | "backend" | "storefront" | null;
192
+ }[] | undefined;
193
+ };
194
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;CAgDlC,CAAC;AAEH,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AACxD,eAAO,MAAM,yBAAyB,mBAAmB,CAAC;AAE1D,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CnB,CAAC"}
@@ -0,0 +1,102 @@
1
+ import { defineModuleManifest, defineModuleSettingsManifest } from '@endora-commerce/contracts';
2
+ import { META_ADS_SETTING_CODES } from '@endora-commerce/contracts';
3
+ /**
4
+ * Meta Ads module (feature 064). Puts the Meta Pixel on the storefront per sales
5
+ * channel, reports Meta's standard commerce events, and lets an operator add
6
+ * custom events alongside them.
7
+ *
8
+ * Per-channel configuration is stored through the Settings module. Server-side
9
+ * reporting through Meta's Conversions API is deliberately out of v1 — see
10
+ * specs/064-meta-ads/research.md §R7.
11
+ */
12
+ export const metaAdsSettingsManifest = defineModuleSettingsManifest({
13
+ moduleCode: 'meta_ads',
14
+ groups: [{ code: 'meta_ads', name: 'Meta Ads' }],
15
+ settings: [
16
+ {
17
+ // Feature 073 — the operator's activation control. **Platform-wide, and
18
+ // deliberately NOT `meta_ads.enabled`.** That code already exists and
19
+ // means something else: it is per-sales-channel and answers "is the
20
+ // pixel is live on a given storefront channel". This one answers "does this client have the Meta Ads
21
+ // capability at all". Adopting the existing code would have collapsed two
22
+ // orthogonal questions into one switch — and, because an activation
23
+ // control may only be written through the activation endpoint, would have
24
+ // made the per-channel setting unwritable through the settings screen.
25
+ code: 'meta_ads.module_enabled',
26
+ name: 'Meta Ads module enabled',
27
+ description: 'Switches Meta Pixel injection, the custom-event mappings and the admin screen on or off for the whole platform. Separate from the per-channel switch, which decides where the tag actually loads. Nothing is dropped: mappings stay in the database and every setting keeps its value.',
28
+ groupCode: 'meta_ads',
29
+ valueType: 'boolean',
30
+ defaultValue: true,
31
+ },
32
+ {
33
+ code: META_ADS_SETTING_CODES.ENABLED,
34
+ name: 'Enable Meta Ads',
35
+ description: 'Master switch for the module. Per-channel overridable.',
36
+ groupCode: 'meta_ads',
37
+ valueType: 'boolean',
38
+ defaultValue: false,
39
+ },
40
+ {
41
+ code: META_ADS_SETTING_CODES.PIXEL_ID,
42
+ name: 'Pixel ID',
43
+ description: 'Meta Pixel ID from Events Manager. Blank means the channel is untracked.',
44
+ groupCode: 'meta_ads',
45
+ valueType: 'string',
46
+ defaultValue: '',
47
+ },
48
+ {
49
+ code: META_ADS_SETTING_CODES.REQUIRE_CONSENT,
50
+ name: 'Require analytics consent',
51
+ description: 'When enabled, nothing Meta-related runs until the visitor accepts the cookie banner.',
52
+ groupCode: 'meta_ads',
53
+ valueType: 'boolean',
54
+ defaultValue: true,
55
+ },
56
+ ],
57
+ });
58
+ export const META_ADS_READ_PERMISSION = 'meta_ads:read';
59
+ export const META_ADS_WRITE_PERMISSION = 'meta_ads:write';
60
+ export const manifest = defineModuleManifest({
61
+ id: 'meta_ads',
62
+ name: 'Meta Ads',
63
+ description: 'Meta Ads integration: per-sales-channel Meta Pixel, consent-gated tracking, standard commerce events, and configurable custom events.',
64
+ version: '1.0.0',
65
+ // `auth` owns the `requireAdmin` port the admin routes are gated by; feature
66
+ // 072 made it a container resolution rather than a constructor argument.
67
+ dependencies: ['audit_logs', 'sales_channels', 'settings', 'auth'],
68
+ settings: metaAdsSettingsManifest,
69
+ i18n: { bundlesDir: 'i18n' },
70
+ docs: { dir: 'docs' },
71
+ permissions: [
72
+ { code: META_ADS_READ_PERMISSION, label: 'View Meta Ads configuration' },
73
+ {
74
+ code: META_ADS_WRITE_PERMISSION,
75
+ label: 'Manage Meta Ads configuration and custom events',
76
+ },
77
+ ],
78
+ actions: [
79
+ {
80
+ id: 'open-meta-ads',
81
+ labelKey: 'actions.openMetaAds.label',
82
+ descriptionKey: 'actions.openMetaAds.description',
83
+ icon: 'Sparkles',
84
+ targetRoute: '/meta-ads',
85
+ requiredPermission: META_ADS_READ_PERMISSION,
86
+ keywords: ['meta', 'facebook', 'instagram', 'pixel', 'ads', 'reklamy'],
87
+ weight: 244,
88
+ },
89
+ {
90
+ id: 'new-meta-custom-event',
91
+ labelKey: 'actions.newCustomEvent.label',
92
+ descriptionKey: 'actions.newCustomEvent.description',
93
+ icon: 'Plus',
94
+ targetRoute: '/meta-ads/new',
95
+ requiredPermission: META_ADS_WRITE_PERMISSION,
96
+ keywords: ['meta', 'pixel', 'event', 'zdarzenie', 'custom'],
97
+ weight: 245,
98
+ },
99
+ ],
100
+ activation: { settingCode: 'meta_ads.module_enabled', default: true },
101
+ });
102
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * Feature 064 — Meta Ads. Creates `meta_custom_event_mappings`.
4
+ *
5
+ * Channel configuration (enabled, pixel id, consent) lives in the Settings
6
+ * module and needs no schema here.
7
+ */
8
+ export declare class Migration20260728T002715MetaAdsInit extends Migration {
9
+ up(): Promise<void>;
10
+ down(): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=20260728T002715_meta_ads_init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260728T002715_meta_ads_init.d.ts","sourceRoot":"","sources":["../../src/migrations/20260728T002715_meta_ads_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;GAKG;AACH,qBAAa,mCAAoC,SAAQ,SAAS;IACjD,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
@@ -0,0 +1,35 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * Feature 064 — Meta Ads. Creates `meta_custom_event_mappings`.
4
+ *
5
+ * Channel configuration (enabled, pixel id, consent) lives in the Settings
6
+ * module and needs no schema here.
7
+ */
8
+ export class Migration20260728T002715MetaAdsInit extends Migration {
9
+ async up() {
10
+ this.addSql(`
11
+ create table "meta_custom_event_mappings" (
12
+ "id" uuid not null,
13
+ "sales_channel_id" uuid null,
14
+ "trigger_action" varchar(32) not null,
15
+ "event_name" varchar(64) not null,
16
+ "enabled" boolean not null default true,
17
+ "version" int not null default 1,
18
+ "created_at" timestamptz not null,
19
+ "updated_at" timestamptz not null,
20
+ constraint "meta_custom_event_mappings_pkey" primary key ("id")
21
+ );
22
+ `);
23
+ this.addSql(`
24
+ alter table "meta_custom_event_mappings"
25
+ add constraint "meta_custom_event_mappings_sales_channel_id_foreign"
26
+ foreign key ("sales_channel_id") references "sales_channels" ("id")
27
+ on update cascade on delete cascade;
28
+ `);
29
+ this.addSql('create index "meta_custom_event_mappings_channel_action_idx" on "meta_custom_event_mappings" ("sales_channel_id", "trigger_action");');
30
+ }
31
+ async down() {
32
+ this.addSql('drop table if exists "meta_custom_event_mappings" cascade;');
33
+ }
34
+ }
35
+ //# sourceMappingURL=20260728T002715_meta_ads_init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260728T002715_meta_ads_init.js","sourceRoot":"","sources":["../../src/migrations/20260728T002715_meta_ads_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,OAAO,mCAAoC,SAAQ,SAAS;IACvD,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;KAYX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC;;;;;KAKX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,sIAAsI,CACvI,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,4DAA4D,CAAC,CAAC;IAC5E,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The `./migrations` subpath — every migration class this module owns, as one
3
+ * ordered `migrations` array.
4
+ *
5
+ * The array is what the platform reads when this module is **installed**:
6
+ * `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
7
+ * the package outright when it is absent (D-168).
8
+ *
9
+ * Listed in ascending timestamp, which is the order of this module's own
10
+ * migrations and of nothing else (feature 081): a manifest `dependencies` array
11
+ * is the only thing ordering this block against another module's.
12
+ *
13
+ * The **named** exports stay beside the array, and the asymmetry with
14
+ * `./backend` — which publishes an array and no named class (D-168) — is
15
+ * deliberate. `db/migrations-registry.generated.ts` imports each class by name
16
+ * from this specifier, and a migration class name is contract in a way an entity
17
+ * class name is not: `mikro_orm_migrations` persists it, so it is a string every
18
+ * already-migrated database holds.
19
+ *
20
+ * A class that is in neither the array nor the barrel is a migration that does
21
+ * not run: `migration:pending` reports nothing pending and the first symptom is
22
+ * a query against a table nobody created.
23
+ */
24
+ import { Migration20260728T002715MetaAdsInit } from './20260728T002715_meta_ads_init.js';
25
+ export declare const migrations: (typeof Migration20260728T002715MetaAdsInit)[];
26
+ export { Migration20260728T002715MetaAdsInit, };
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,mCAAmC,EAAE,MAAM,oCAAoC,CAAC;AAEzF,eAAO,MAAM,UAAU,gDAEtB,CAAC;AAEF,OAAO,EACL,mCAAmC,GACpC,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The `./migrations` subpath — every migration class this module owns, as one
3
+ * ordered `migrations` array.
4
+ *
5
+ * The array is what the platform reads when this module is **installed**:
6
+ * `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
7
+ * the package outright when it is absent (D-168).
8
+ *
9
+ * Listed in ascending timestamp, which is the order of this module's own
10
+ * migrations and of nothing else (feature 081): a manifest `dependencies` array
11
+ * is the only thing ordering this block against another module's.
12
+ *
13
+ * The **named** exports stay beside the array, and the asymmetry with
14
+ * `./backend` — which publishes an array and no named class (D-168) — is
15
+ * deliberate. `db/migrations-registry.generated.ts` imports each class by name
16
+ * from this specifier, and a migration class name is contract in a way an entity
17
+ * class name is not: `mikro_orm_migrations` persists it, so it is a string every
18
+ * already-migrated database holds.
19
+ *
20
+ * A class that is in neither the array nor the barrel is a migration that does
21
+ * not run: `migration:pending` reports nothing pending and the first symptom is
22
+ * a query against a table nobody created.
23
+ */
24
+ import { Migration20260728T002715MetaAdsInit } from './20260728T002715_meta_ads_init.js';
25
+ export const migrations = [
26
+ Migration20260728T002715MetaAdsInit,
27
+ ];
28
+ export { Migration20260728T002715MetaAdsInit, };
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,mCAAmC,EAAE,MAAM,oCAAoC,CAAC;AAEzF,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,mCAAmC;CACpC,CAAC;AAEF,OAAO,EACL,mCAAmC,GACpC,CAAC"}
@@ -0,0 +1,82 @@
1
+ ---
2
+ title: Meta Ads
3
+ description: Meta Pixel per sales channel, with the standard commerce events and optional custom events on top
4
+ ---
5
+
6
+ # Meta Ads
7
+
8
+ Puts the **Meta Pixel** on the storefront per sales channel and reports Meta's standard commerce
9
+ events, with optional custom events on top.
10
+
11
+ Module id `meta_ads`.
12
+
13
+ ## Configuration (Settings module)
14
+
15
+ All values live in the **Meta Ads** settings group and are overridable per sales channel.
16
+
17
+ | Setting | Type | Default | Meaning |
18
+ | --- | --- | --- | --- |
19
+ | `meta_ads.enabled` | boolean | `false` | Master switch for the channel. |
20
+ | `meta_ads.pixel_id` | string | `''` | Pixel ID from Events Manager. |
21
+ | `meta_ads.require_consent` | boolean | `true` | Gate all Meta behaviour on the cookie-banner decision. |
22
+
23
+ A channel is tracked only when the master switch is on **and** the Pixel ID is non-blank. A blank
24
+ Pixel ID is "not configured", never an error the visitor sees, so clearing it is a safe way to pause
25
+ a channel.
26
+
27
+ ## Storefront behaviour
28
+
29
+ The Pixel loads asynchronously and never blocks the first paint. Nothing Meta-related runs — no
30
+ script, no cookie, no request — until the visitor accepts the cookie banner, unless the operator has
31
+ turned the consent requirement off. A visitor who accepts mid-session is tracked from that moment
32
+ without a reload.
33
+
34
+ Consent is the storefront's single decision, shared with Google Analytics and LinkedIn Ads, so there
35
+ is only ever one banner and one stored answer. Each platform gates independently: turning Meta on or
36
+ off does not affect the others.
37
+
38
+ ## Standard events
39
+
40
+ These fire automatically for the storefront actions the platform already emits, with the parameters
41
+ Meta expects for catalogue matching and ROAS reporting:
42
+
43
+ | Storefront action | Meta event |
44
+ | --- | --- |
45
+ | Product viewed | `ViewContent` |
46
+ | Added to cart | `AddToCart` |
47
+ | Checkout started | `InitiateCheckout` |
48
+ | Order completed | `Purchase` |
49
+ | Added to quote request | `Lead` |
50
+ | Contact form submitted | `Lead` |
51
+ | Added to shopping list | *(none)* |
52
+ | Place Order clicked | *(none)* |
53
+
54
+ Names are fixed in code, not editable — Meta's optimisation and catalogue matching key off these
55
+ exact names, so renaming them would quietly downgrade your reporting.
56
+
57
+ ## Custom events
58
+
59
+ Under **Admin → Meta Ads** you can report any of those storefront actions under your own event name
60
+ too — for example, "added to quote request" as `SubmitQuote` for a B2B-specific audience.
61
+
62
+ A custom event is **additive**: the standard event still fires, and yours fires beside it. It never
63
+ replaces the standard event, so adding an audience event cannot cost you your `Purchase` reporting.
64
+ The two actions with no standard event accept custom mappings, which is exactly what they are for.
65
+
66
+ A mapping can target one sales channel or all of them, and can be disabled without deleting it.
67
+
68
+ ## Permissions
69
+
70
+ | Code | Grants |
71
+ | --- | --- |
72
+ | `meta_ads:read` | View custom events and the module page. |
73
+ | `meta_ads:write` | Create, edit, and delete custom events. |
74
+
75
+ Both appear on `/admin-roles`. Changes are audited with the acting operator.
76
+
77
+ ## Not yet implemented
78
+
79
+ - **Conversions API (server-side reporting).** Browser pixels are heavily ad-blocked, so expect
80
+ reported conversions to under-count until this lands. It needs an access token and an event ID
81
+ shared with the browser event for deduplication, and is worth doing once for both ad platforms.
82
+ - **Conversion retraction** for refunded or cancelled orders.
package/i18n/en.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "actions.openMetaAds.label": "Meta Ads",
3
+ "actions.openMetaAds.description": "Configure Meta Pixel custom events",
4
+ "actions.newCustomEvent.label": "New Meta custom event",
5
+ "actions.newCustomEvent.description": "Report a storefront action as a custom Meta event",
6
+ "nav.metaAds.label": "Meta Ads",
7
+ "customEvents.title": "Custom events",
8
+ "customEvents.subtitle": "Report a storefront action under your own Meta event name. Standard events (ViewContent, AddToCart, Purchase…) are always reported and are not affected by these.",
9
+ "customEvents.empty": "No custom events yet",
10
+ "customEvents.new": "New custom event",
11
+ "customEvents.edit": "Edit custom event",
12
+ "customEvents.triggerAction": "Storefront action",
13
+ "customEvents.eventName": "Meta event name",
14
+ "customEvents.eventNameHint": "Letters, digits and underscores, e.g. SubmitQuote.",
15
+ "customEvents.standardEvent": "Standard event",
16
+ "customEvents.standardEventNone": "none",
17
+ "customEvents.salesChannel": "Sales channel",
18
+ "customEvents.allChannels": "All channels",
19
+ "customEvents.enabled": "Enabled",
20
+ "customEvents.save": "Save",
21
+ "customEvents.delete": "Delete",
22
+ "customEvents.deleteConfirm": "Delete this custom event?",
23
+ "customEvents.action.product_viewed": "Product viewed",
24
+ "customEvents.action.add_to_cart": "Added to cart",
25
+ "customEvents.action.add_to_quote_request": "Added to quote request",
26
+ "customEvents.action.add_to_shopping_list": "Added to shopping list",
27
+ "customEvents.action.begin_checkout": "Checkout started",
28
+ "customEvents.action.place_order_clicked": "Place Order clicked",
29
+ "customEvents.action.purchase": "Order completed",
30
+ "customEvents.action.contact_form_submitted": "Contact form submitted",
31
+ "error.generic": "Something went wrong.",
32
+ "error.notFound": "Custom event not found.",
33
+ "error.versionConflict": "Someone else changed this custom event. Reload and reapply your edit.",
34
+ "error.validationFailed": "Some fields are invalid."
35
+ }
package/i18n/pl.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "actions.openMetaAds.label": "Meta Ads",
3
+ "actions.openMetaAds.description": "Konfiguruj własne zdarzenia Meta Pixel",
4
+ "actions.newCustomEvent.label": "Nowe zdarzenie własne Meta",
5
+ "actions.newCustomEvent.description": "Raportuj akcję w Storefront jako własne zdarzenie Meta",
6
+ "nav.metaAds.label": "Meta Ads",
7
+ "customEvents.title": "Zdarzenia własne",
8
+ "customEvents.subtitle": "Raportuj akcję w Storefront pod własną nazwą zdarzenia Meta. Zdarzenia standardowe (ViewContent, AddToCart, Purchase…) są wysyłane zawsze i nie zależą od tych ustawień.",
9
+ "customEvents.empty": "Brak zdarzeń własnych",
10
+ "customEvents.new": "Nowe zdarzenie własne",
11
+ "customEvents.edit": "Edytuj zdarzenie własne",
12
+ "customEvents.triggerAction": "Akcja w Storefront",
13
+ "customEvents.eventName": "Nazwa zdarzenia Meta",
14
+ "customEvents.eventNameHint": "Litery, cyfry i podkreślenia, np. SubmitQuote.",
15
+ "customEvents.standardEvent": "Zdarzenie standardowe",
16
+ "customEvents.standardEventNone": "brak",
17
+ "customEvents.salesChannel": "Kanał sprzedaży",
18
+ "customEvents.allChannels": "Wszystkie kanały",
19
+ "customEvents.enabled": "Aktywne",
20
+ "customEvents.save": "Zapisz",
21
+ "customEvents.delete": "Usuń",
22
+ "customEvents.deleteConfirm": "Usunąć to zdarzenie własne?",
23
+ "customEvents.action.product_viewed": "Wyświetlenie produktu",
24
+ "customEvents.action.add_to_cart": "Dodanie do koszyka",
25
+ "customEvents.action.add_to_quote_request": "Dodanie do zapytania ofertowego",
26
+ "customEvents.action.add_to_shopping_list": "Dodanie do listy zakupowej",
27
+ "customEvents.action.begin_checkout": "Rozpoczęcie zamówienia",
28
+ "customEvents.action.place_order_clicked": "Kliknięcie „Złóż zamówienie”",
29
+ "customEvents.action.purchase": "Złożenie zamówienia",
30
+ "customEvents.action.contact_form_submitted": "Zapis formularza kontaktowego",
31
+ "error.generic": "Coś poszło nie tak.",
32
+ "error.notFound": "Nie znaleziono zdarzenia własnego.",
33
+ "error.versionConflict": "Ktoś inny zmienił to zdarzenie. Odśwież stronę i ponów zmianę.",
34
+ "error.validationFailed": "Niektóre pola są nieprawidłowe."
35
+ }