ad2app-lib 1.47.0 → 1.49.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.
- package/dist/analytics/index.d.ts +44 -0
- package/dist/analytics/index.js +17 -0
- package/dist/types/I_Offer.d.ts +3 -1
- package/dist/types/I_Offer.js +2 -0
- package/dist/types/agency-campaigns.d.ts +391 -0
- package/dist/types/agency-campaigns.js +426 -0
- package/dist/types/agency-threads.d.ts +87 -0
- package/dist/types/agency-threads.js +60 -0
- package/dist/types/agency.d.ts +2 -0
- package/dist/types/agency.js +2 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/package.json +1 -1
- package/src/analytics/index.test.ts +41 -1
- package/src/analytics/index.ts +39 -0
- package/src/types/I_Offer.ts +2 -0
- package/src/types/agency-campaigns.test.ts +85 -0
- package/src/types/agency-campaigns.ts +663 -0
- package/src/types/agency-threads.ts +103 -0
- package/src/types/agency.ts +2 -0
- package/src/types/index.ts +2 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.UpdateSettlementFormDto = exports.CreateAgencyMilestoneDto = exports.ConfirmMaterialUploadDto = exports.PrepareMaterialUploadDto = exports.MATERIAL_CONTENT_TYPE = exports.MATERIAL_IMAGE_TYPES = exports.MATERIAL_MAX_BYTES = exports.JoinCampaignDto = exports.CampaignMaterialActDto = exports.CampaignOfferActDto = exports.SendCampaignOfferDto = exports.InviteCampaignCreatorDto = exports.UpdateAgencyCampaignDto = exports.CreateAgencyCampaignDto = exports.AGENCY_CAMPAIGN_CODES = exports.AGENCY_CAMPAIGN_EVENT_KINDS = exports.AGENCY_MILESTONE_MEDIA_RULES = exports.AGENCY_MILESTONE_OFFER_RULES = exports.AGENCY_MILESTONE_KINDS = exports.AGENCY_CAMPAIGN_STEPS = exports.AGENCY_MATERIAL_ACTIONS = exports.AGENCY_OFFER_ACTIONS = exports.AGENCY_OFFER_STATUSES = exports.AGENCY_CAMPAIGN_MEMBER_STATES = exports.SETTLEMENT_FORMS = exports.isCampaignPlacement = exports.CAMPAIGN_PLATFORMS = exports.CAMPAIGN_PLACEMENTS = void 0;
|
|
13
|
+
const class_validator_1 = require("class-validator");
|
|
14
|
+
const I_Media_1 = require("./I_Media");
|
|
15
|
+
const I_Offer_1 = require("./I_Offer");
|
|
16
|
+
const agency_1 = require("./agency");
|
|
17
|
+
/**
|
|
18
|
+
* The campaign loop (spec 175 US7) — an organization runs a campaign with the
|
|
19
|
+
* creators on its roster or in the directory.
|
|
20
|
+
*
|
|
21
|
+
* Two steps, never one (Jan, 2026-09-28):
|
|
22
|
+
*
|
|
23
|
+
* 1. MEMBERSHIP — the agency invites a creator; the creator joins or
|
|
24
|
+
* declines. Joining answers any access request folded into the
|
|
25
|
+
* invitation. No money is named here.
|
|
26
|
+
* 2. OFFER — once joined, the agency sends a rate. It is countered, accepted,
|
|
27
|
+
* confirmed or rejected; a creator who leaves withdraws it.
|
|
28
|
+
*
|
|
29
|
+
* The server owns every transition. Each view carries `availableActions`, and
|
|
30
|
+
* a screen renders exactly those acts — it never derives them from a status,
|
|
31
|
+
* so the two can never disagree.
|
|
32
|
+
*
|
|
33
|
+
* See specs/speckit/175-agency-roster/contracts/agency-api.md → US7.
|
|
34
|
+
*/
|
|
35
|
+
// ── The ONE placements list (FR-025) ────────────────────────────────────────
|
|
36
|
+
/**
|
|
37
|
+
* What a deliverable may be, per platform. A named list in one place, so a
|
|
38
|
+
* "placement" can never be free text that drifts per campaign.
|
|
39
|
+
*/
|
|
40
|
+
exports.CAMPAIGN_PLACEMENTS = {
|
|
41
|
+
instagram: ['reel', 'post', 'carousel', 'stories', 'collab_post'],
|
|
42
|
+
tiktok: ['video', 'repost', 'duet', 'stories'],
|
|
43
|
+
youtube: ['video', 'short', 'integration', 'community_post'],
|
|
44
|
+
facebook: ['post', 'reel', 'stories'],
|
|
45
|
+
linkedin: ['post', 'article'],
|
|
46
|
+
};
|
|
47
|
+
exports.CAMPAIGN_PLATFORMS = Object.keys(exports.CAMPAIGN_PLACEMENTS);
|
|
48
|
+
const isCampaignPlacement = (platform, placement) => Object.prototype.hasOwnProperty.call(exports.CAMPAIGN_PLACEMENTS, platform) &&
|
|
49
|
+
exports.CAMPAIGN_PLACEMENTS[platform].includes(placement);
|
|
50
|
+
exports.isCampaignPlacement = isCampaignPlacement;
|
|
51
|
+
// ── The settlement form (FR-027) ─────────────────────────────────────────────
|
|
52
|
+
/** The creator's own setting. The agency reads it and can never set it. */
|
|
53
|
+
exports.SETTLEMENT_FORMS = ['invoice', 'contract', 'barter'];
|
|
54
|
+
// ── States ───────────────────────────────────────────────────────────────────
|
|
55
|
+
exports.AGENCY_CAMPAIGN_MEMBER_STATES = ['invited', 'joined', 'declined', 'left', 'removed'];
|
|
56
|
+
/** The offer statuses the agency loop writes. `draft`, `deleted` and `alternativeSuggested` are legacy-only. */
|
|
57
|
+
exports.AGENCY_OFFER_STATUSES = [
|
|
58
|
+
I_Offer_1.I_OfferStatus.SENT,
|
|
59
|
+
I_Offer_1.I_OfferStatus.INFLUENCER_NEGOTIATED,
|
|
60
|
+
I_Offer_1.I_OfferStatus.AGENCY_NEGOTIATED,
|
|
61
|
+
I_Offer_1.I_OfferStatus.ACCEPTED,
|
|
62
|
+
I_Offer_1.I_OfferStatus.CONFIRMED,
|
|
63
|
+
I_Offer_1.I_OfferStatus.REJECTED,
|
|
64
|
+
I_Offer_1.I_OfferStatus.WITHDRAWN,
|
|
65
|
+
];
|
|
66
|
+
/**
|
|
67
|
+
* Every act on an offer, by either side. Which of them a given caller may take
|
|
68
|
+
* right now is the server's `availableActions`, never a client guess.
|
|
69
|
+
*
|
|
70
|
+
* agency: counter · confirm · reject
|
|
71
|
+
* creator: accept · counter · reject
|
|
72
|
+
*/
|
|
73
|
+
exports.AGENCY_OFFER_ACTIONS = ['accept', 'counter', 'confirm', 'reject'];
|
|
74
|
+
/** The agency's acts on the latest material; the creator's only act is to upload. */
|
|
75
|
+
exports.AGENCY_MATERIAL_ACTIONS = ['approve', 'request_changes', 'reject'];
|
|
76
|
+
/**
|
|
77
|
+
* The board's columns, in order (FR-024). A creator stands in exactly one.
|
|
78
|
+
* `declined`, `left`, `removed` and `rejected` are OFF the board: those rows
|
|
79
|
+
* stay in the list with their date (US7 scenario 9), never in a column.
|
|
80
|
+
*/
|
|
81
|
+
exports.AGENCY_CAMPAIGN_STEPS = [
|
|
82
|
+
'invited',
|
|
83
|
+
'joined',
|
|
84
|
+
'offer_sent',
|
|
85
|
+
'negotiating',
|
|
86
|
+
'confirmed',
|
|
87
|
+
'material_in',
|
|
88
|
+
'approved',
|
|
89
|
+
'published',
|
|
90
|
+
];
|
|
91
|
+
exports.AGENCY_MILESTONE_KINDS = ['draft_due', 'live_by', 'custom'];
|
|
92
|
+
/** A milestone may wait on one rule: every ACTIVE offer at a status, or every active creator's latest material at a stage. */
|
|
93
|
+
exports.AGENCY_MILESTONE_OFFER_RULES = [I_Offer_1.I_OfferStatus.CONFIRMED];
|
|
94
|
+
exports.AGENCY_MILESTONE_MEDIA_RULES = [I_Media_1.I_MediaStage.UPLOADED, I_Media_1.I_MediaStage.ACCEPTED];
|
|
95
|
+
exports.AGENCY_CAMPAIGN_EVENT_KINDS = [
|
|
96
|
+
'invited',
|
|
97
|
+
'joined',
|
|
98
|
+
'declined',
|
|
99
|
+
'left',
|
|
100
|
+
'removed',
|
|
101
|
+
'offer_sent',
|
|
102
|
+
'offer_countered',
|
|
103
|
+
'offer_accepted',
|
|
104
|
+
'offer_confirmed',
|
|
105
|
+
'offer_rejected',
|
|
106
|
+
'offer_withdrawn',
|
|
107
|
+
'material_uploaded',
|
|
108
|
+
'material_approved',
|
|
109
|
+
'material_changes_asked',
|
|
110
|
+
'material_rejected',
|
|
111
|
+
'published',
|
|
112
|
+
'milestone_confirmed',
|
|
113
|
+
];
|
|
114
|
+
/** Refusal codes the frontend owes its own copy for. */
|
|
115
|
+
exports.AGENCY_CAMPAIGN_CODES = {
|
|
116
|
+
/** The transition is not open at this state, or not for this side. */
|
|
117
|
+
TRANSITION_REFUSED: 'agency-campaign-transition-refused',
|
|
118
|
+
/** A campaign sent to a creator must carry its brief text (FR-025). */
|
|
119
|
+
BRIEF_REQUIRED: 'agency-campaign-brief-required',
|
|
120
|
+
/** A deliverable on a platform the creator has not connected (FR-025). */
|
|
121
|
+
PLATFORM_NOT_CONNECTED: 'agency-campaign-platform-not-connected',
|
|
122
|
+
/** The creator is already on this campaign. */
|
|
123
|
+
ALREADY_MEMBER: 'agency-campaign-already-member',
|
|
124
|
+
/** The upload was confirmed but no stored file exists at its path (FR-029). */
|
|
125
|
+
MATERIAL_NOT_STORED: 'agency-campaign-material-not-stored',
|
|
126
|
+
/** A demo creator cannot be invited or mailed (spec 179). */
|
|
127
|
+
DEMO_CREATOR: 'agency-campaign-demo-creator',
|
|
128
|
+
};
|
|
129
|
+
// ── Bodies ───────────────────────────────────────────────────────────────────
|
|
130
|
+
//
|
|
131
|
+
// Flat on purpose: nested bodies need class-transformer's `@Type`, and a
|
|
132
|
+
// linked lib carries its own copy whose metadata the backend's ValidationPipe
|
|
133
|
+
// never reads. `deliverables` is an array of plain objects checked item by
|
|
134
|
+
// item in the service against CAMPAIGN_PLACEMENTS — the pair is the rule.
|
|
135
|
+
/** The brief's fields, shared by create and update. All optional on the wire. */
|
|
136
|
+
class AgencyCampaignBriefFields {
|
|
137
|
+
assignBrief(data) {
|
|
138
|
+
this.text = data?.text;
|
|
139
|
+
this.deliverables = data?.deliverables;
|
|
140
|
+
this.draftDueAt = data?.draftDueAt;
|
|
141
|
+
this.liveByAt = data?.liveByAt;
|
|
142
|
+
this.licenceMonths = data?.licenceMonths;
|
|
143
|
+
this.paidPromoMonths = data?.paidPromoMonths;
|
|
144
|
+
this.revisionRounds = data?.revisionRounds;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
__decorate([
|
|
148
|
+
(0, class_validator_1.IsOptional)(),
|
|
149
|
+
(0, class_validator_1.IsString)(),
|
|
150
|
+
(0, class_validator_1.MaxLength)(10000),
|
|
151
|
+
__metadata("design:type", String)
|
|
152
|
+
], AgencyCampaignBriefFields.prototype, "text", void 0);
|
|
153
|
+
__decorate([
|
|
154
|
+
(0, class_validator_1.IsOptional)(),
|
|
155
|
+
(0, class_validator_1.IsArray)(),
|
|
156
|
+
(0, class_validator_1.ArrayMaxSize)(30),
|
|
157
|
+
__metadata("design:type", Array)
|
|
158
|
+
], AgencyCampaignBriefFields.prototype, "deliverables", void 0);
|
|
159
|
+
__decorate([
|
|
160
|
+
(0, class_validator_1.IsOptional)(),
|
|
161
|
+
(0, class_validator_1.IsDateString)(),
|
|
162
|
+
__metadata("design:type", String)
|
|
163
|
+
], AgencyCampaignBriefFields.prototype, "draftDueAt", void 0);
|
|
164
|
+
__decorate([
|
|
165
|
+
(0, class_validator_1.IsOptional)(),
|
|
166
|
+
(0, class_validator_1.IsDateString)(),
|
|
167
|
+
__metadata("design:type", String)
|
|
168
|
+
], AgencyCampaignBriefFields.prototype, "liveByAt", void 0);
|
|
169
|
+
__decorate([
|
|
170
|
+
(0, class_validator_1.IsOptional)(),
|
|
171
|
+
(0, class_validator_1.IsInt)(),
|
|
172
|
+
(0, class_validator_1.Min)(0),
|
|
173
|
+
(0, class_validator_1.Max)(120),
|
|
174
|
+
__metadata("design:type", Number)
|
|
175
|
+
], AgencyCampaignBriefFields.prototype, "licenceMonths", void 0);
|
|
176
|
+
__decorate([
|
|
177
|
+
(0, class_validator_1.IsOptional)(),
|
|
178
|
+
(0, class_validator_1.IsInt)(),
|
|
179
|
+
(0, class_validator_1.Min)(0),
|
|
180
|
+
(0, class_validator_1.Max)(120),
|
|
181
|
+
__metadata("design:type", Number)
|
|
182
|
+
], AgencyCampaignBriefFields.prototype, "paidPromoMonths", void 0);
|
|
183
|
+
__decorate([
|
|
184
|
+
(0, class_validator_1.IsOptional)(),
|
|
185
|
+
(0, class_validator_1.IsInt)(),
|
|
186
|
+
(0, class_validator_1.Min)(0),
|
|
187
|
+
(0, class_validator_1.Max)(20),
|
|
188
|
+
__metadata("design:type", Number)
|
|
189
|
+
], AgencyCampaignBriefFields.prototype, "revisionRounds", void 0);
|
|
190
|
+
class CreateAgencyCampaignDto extends AgencyCampaignBriefFields {
|
|
191
|
+
constructor(data) {
|
|
192
|
+
super();
|
|
193
|
+
this.name = data?.name ?? '';
|
|
194
|
+
this.assignBrief(data);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
exports.CreateAgencyCampaignDto = CreateAgencyCampaignDto;
|
|
198
|
+
__decorate([
|
|
199
|
+
(0, class_validator_1.IsString)(),
|
|
200
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
201
|
+
(0, class_validator_1.MaxLength)(128),
|
|
202
|
+
__metadata("design:type", String)
|
|
203
|
+
], CreateAgencyCampaignDto.prototype, "name", void 0);
|
|
204
|
+
class UpdateAgencyCampaignDto extends AgencyCampaignBriefFields {
|
|
205
|
+
constructor(data) {
|
|
206
|
+
super();
|
|
207
|
+
this.name = data?.name;
|
|
208
|
+
this.isDraft = data?.isDraft;
|
|
209
|
+
this.assignBrief(data);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
exports.UpdateAgencyCampaignDto = UpdateAgencyCampaignDto;
|
|
213
|
+
__decorate([
|
|
214
|
+
(0, class_validator_1.IsOptional)(),
|
|
215
|
+
(0, class_validator_1.IsString)(),
|
|
216
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
217
|
+
(0, class_validator_1.MaxLength)(128),
|
|
218
|
+
__metadata("design:type", String)
|
|
219
|
+
], UpdateAgencyCampaignDto.prototype, "name", void 0);
|
|
220
|
+
__decorate([
|
|
221
|
+
(0, class_validator_1.IsOptional)(),
|
|
222
|
+
(0, class_validator_1.IsBoolean)(),
|
|
223
|
+
__metadata("design:type", Boolean)
|
|
224
|
+
], UpdateAgencyCampaignDto.prototype, "isDraft", void 0);
|
|
225
|
+
class InviteCampaignCreatorDto {
|
|
226
|
+
constructor(data) {
|
|
227
|
+
this.creatorUserId = data?.creatorUserId ?? '';
|
|
228
|
+
this.creatorBrief = data?.creatorBrief;
|
|
229
|
+
this.scopes = data?.scopes;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
exports.InviteCampaignCreatorDto = InviteCampaignCreatorDto;
|
|
233
|
+
__decorate([
|
|
234
|
+
(0, class_validator_1.IsString)(),
|
|
235
|
+
(0, class_validator_1.Matches)(/^[A-Za-z0-9-]{1,128}$/),
|
|
236
|
+
__metadata("design:type", String)
|
|
237
|
+
], InviteCampaignCreatorDto.prototype, "creatorUserId", void 0);
|
|
238
|
+
__decorate([
|
|
239
|
+
(0, class_validator_1.IsOptional)(),
|
|
240
|
+
(0, class_validator_1.IsString)(),
|
|
241
|
+
(0, class_validator_1.MaxLength)(5000),
|
|
242
|
+
__metadata("design:type", String)
|
|
243
|
+
], InviteCampaignCreatorDto.prototype, "creatorBrief", void 0);
|
|
244
|
+
__decorate([
|
|
245
|
+
(0, class_validator_1.IsOptional)(),
|
|
246
|
+
(0, class_validator_1.IsArray)(),
|
|
247
|
+
(0, class_validator_1.IsIn)(agency_1.AGENCY_GRANT_SCOPES, { each: true }),
|
|
248
|
+
__metadata("design:type", Array)
|
|
249
|
+
], InviteCampaignCreatorDto.prototype, "scopes", void 0);
|
|
250
|
+
class SendCampaignOfferDto {
|
|
251
|
+
constructor(data) {
|
|
252
|
+
this.priceMinor = data?.priceMinor ?? 0;
|
|
253
|
+
this.currency = data?.currency ?? 'PLN';
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
exports.SendCampaignOfferDto = SendCampaignOfferDto;
|
|
257
|
+
__decorate([
|
|
258
|
+
(0, class_validator_1.IsInt)(),
|
|
259
|
+
(0, class_validator_1.Min)(0),
|
|
260
|
+
(0, class_validator_1.Max)(1000000000),
|
|
261
|
+
__metadata("design:type", Number)
|
|
262
|
+
], SendCampaignOfferDto.prototype, "priceMinor", void 0);
|
|
263
|
+
__decorate([
|
|
264
|
+
(0, class_validator_1.IsString)(),
|
|
265
|
+
(0, class_validator_1.Matches)(/^[A-Z]{3}$/),
|
|
266
|
+
__metadata("design:type", String)
|
|
267
|
+
], SendCampaignOfferDto.prototype, "currency", void 0);
|
|
268
|
+
/** One act on an offer. `counter` needs `priceMinor`; any act may carry a note. */
|
|
269
|
+
class CampaignOfferActDto {
|
|
270
|
+
constructor(data) {
|
|
271
|
+
this.action = data?.action ?? 'accept';
|
|
272
|
+
this.priceMinor = data?.priceMinor;
|
|
273
|
+
this.note = data?.note;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
exports.CampaignOfferActDto = CampaignOfferActDto;
|
|
277
|
+
__decorate([
|
|
278
|
+
(0, class_validator_1.IsIn)(exports.AGENCY_OFFER_ACTIONS),
|
|
279
|
+
__metadata("design:type", String)
|
|
280
|
+
], CampaignOfferActDto.prototype, "action", void 0);
|
|
281
|
+
__decorate([
|
|
282
|
+
(0, class_validator_1.IsOptional)(),
|
|
283
|
+
(0, class_validator_1.IsInt)(),
|
|
284
|
+
(0, class_validator_1.Min)(0),
|
|
285
|
+
(0, class_validator_1.Max)(1000000000),
|
|
286
|
+
__metadata("design:type", Number)
|
|
287
|
+
], CampaignOfferActDto.prototype, "priceMinor", void 0);
|
|
288
|
+
__decorate([
|
|
289
|
+
(0, class_validator_1.IsOptional)(),
|
|
290
|
+
(0, class_validator_1.IsString)(),
|
|
291
|
+
(0, class_validator_1.MaxLength)(2000),
|
|
292
|
+
__metadata("design:type", String)
|
|
293
|
+
], CampaignOfferActDto.prototype, "note", void 0);
|
|
294
|
+
/** One agency act on a material. `request_changes` requires the note. */
|
|
295
|
+
class CampaignMaterialActDto {
|
|
296
|
+
constructor(data) {
|
|
297
|
+
this.action = data?.action ?? 'approve';
|
|
298
|
+
this.note = data?.note;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
exports.CampaignMaterialActDto = CampaignMaterialActDto;
|
|
302
|
+
__decorate([
|
|
303
|
+
(0, class_validator_1.IsIn)(exports.AGENCY_MATERIAL_ACTIONS),
|
|
304
|
+
__metadata("design:type", String)
|
|
305
|
+
], CampaignMaterialActDto.prototype, "action", void 0);
|
|
306
|
+
__decorate([
|
|
307
|
+
(0, class_validator_1.IsOptional)(),
|
|
308
|
+
(0, class_validator_1.IsString)(),
|
|
309
|
+
(0, class_validator_1.MaxLength)(2000),
|
|
310
|
+
__metadata("design:type", String)
|
|
311
|
+
], CampaignMaterialActDto.prototype, "note", void 0);
|
|
312
|
+
class JoinCampaignDto {
|
|
313
|
+
constructor(data) {
|
|
314
|
+
this.scopes = data?.scopes;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
exports.JoinCampaignDto = JoinCampaignDto;
|
|
318
|
+
__decorate([
|
|
319
|
+
(0, class_validator_1.IsOptional)(),
|
|
320
|
+
(0, class_validator_1.IsArray)(),
|
|
321
|
+
(0, class_validator_1.IsIn)(agency_1.AGENCY_GRANT_SCOPES, { each: true }),
|
|
322
|
+
__metadata("design:type", Array)
|
|
323
|
+
], JoinCampaignDto.prototype, "scopes", void 0);
|
|
324
|
+
/** Material files: images and video only, at most 2 GB. */
|
|
325
|
+
exports.MATERIAL_MAX_BYTES = 2 * 1024 * 1024 * 1024;
|
|
326
|
+
/**
|
|
327
|
+
* The photo formats a material may be. Raster only: no SVG or HTML, because a
|
|
328
|
+
* material is opened in the browser. Shared so the upload picker, the
|
|
329
|
+
* creator's own check and the API refuse exactly the same files.
|
|
330
|
+
*/
|
|
331
|
+
exports.MATERIAL_IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/gif', 'image/webp', 'image/heic', 'image/heif', 'image/avif'];
|
|
332
|
+
/** A material's content type: one of MATERIAL_IMAGE_TYPES, or any video. */
|
|
333
|
+
exports.MATERIAL_CONTENT_TYPE = new RegExp(`^(${exports.MATERIAL_IMAGE_TYPES.map((type) => type.replace(/[/+.]/g, (c) => `\\${c}`)).join('|')}|video\\/[A-Za-z0-9.+-]+)$`);
|
|
334
|
+
class PrepareMaterialUploadDto {
|
|
335
|
+
constructor(data) {
|
|
336
|
+
this.fileName = data?.fileName ?? '';
|
|
337
|
+
this.contentType = data?.contentType ?? '';
|
|
338
|
+
this.sizeBytes = data?.sizeBytes ?? 0;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
exports.PrepareMaterialUploadDto = PrepareMaterialUploadDto;
|
|
342
|
+
__decorate([
|
|
343
|
+
(0, class_validator_1.IsString)(),
|
|
344
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
345
|
+
(0, class_validator_1.MaxLength)(255),
|
|
346
|
+
__metadata("design:type", String)
|
|
347
|
+
], PrepareMaterialUploadDto.prototype, "fileName", void 0);
|
|
348
|
+
__decorate([
|
|
349
|
+
(0, class_validator_1.IsString)()
|
|
350
|
+
/** Raster images and video (MATERIAL_CONTENT_TYPE). */
|
|
351
|
+
,
|
|
352
|
+
(0, class_validator_1.Matches)(exports.MATERIAL_CONTENT_TYPE),
|
|
353
|
+
__metadata("design:type", String)
|
|
354
|
+
], PrepareMaterialUploadDto.prototype, "contentType", void 0);
|
|
355
|
+
__decorate([
|
|
356
|
+
(0, class_validator_1.IsInt)(),
|
|
357
|
+
(0, class_validator_1.Min)(1),
|
|
358
|
+
(0, class_validator_1.Max)(exports.MATERIAL_MAX_BYTES),
|
|
359
|
+
__metadata("design:type", Number)
|
|
360
|
+
], PrepareMaterialUploadDto.prototype, "sizeBytes", void 0);
|
|
361
|
+
class ConfirmMaterialUploadDto {
|
|
362
|
+
constructor(data) {
|
|
363
|
+
this.uploadId = data?.uploadId ?? '';
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
exports.ConfirmMaterialUploadDto = ConfirmMaterialUploadDto;
|
|
367
|
+
__decorate([
|
|
368
|
+
(0, class_validator_1.IsString)(),
|
|
369
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
370
|
+
(0, class_validator_1.MaxLength)(512),
|
|
371
|
+
__metadata("design:type", String)
|
|
372
|
+
], ConfirmMaterialUploadDto.prototype, "uploadId", void 0);
|
|
373
|
+
class CreateAgencyMilestoneDto {
|
|
374
|
+
constructor(data) {
|
|
375
|
+
this.kind = data?.kind ?? 'custom';
|
|
376
|
+
this.label = data?.label;
|
|
377
|
+
this.dueAt = data?.dueAt;
|
|
378
|
+
this.creatorUserId = data?.creatorUserId;
|
|
379
|
+
this.offerRule = data?.offerRule;
|
|
380
|
+
this.mediaRule = data?.mediaRule;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
exports.CreateAgencyMilestoneDto = CreateAgencyMilestoneDto;
|
|
384
|
+
__decorate([
|
|
385
|
+
(0, class_validator_1.IsIn)(exports.AGENCY_MILESTONE_KINDS),
|
|
386
|
+
__metadata("design:type", String)
|
|
387
|
+
], CreateAgencyMilestoneDto.prototype, "kind", void 0);
|
|
388
|
+
__decorate([
|
|
389
|
+
(0, class_validator_1.IsOptional)(),
|
|
390
|
+
(0, class_validator_1.IsString)(),
|
|
391
|
+
(0, class_validator_1.MaxLength)(256),
|
|
392
|
+
__metadata("design:type", String)
|
|
393
|
+
], CreateAgencyMilestoneDto.prototype, "label", void 0);
|
|
394
|
+
__decorate([
|
|
395
|
+
(0, class_validator_1.IsOptional)(),
|
|
396
|
+
(0, class_validator_1.IsDateString)(),
|
|
397
|
+
__metadata("design:type", String)
|
|
398
|
+
], CreateAgencyMilestoneDto.prototype, "dueAt", void 0);
|
|
399
|
+
__decorate([
|
|
400
|
+
(0, class_validator_1.IsOptional)(),
|
|
401
|
+
(0, class_validator_1.IsString)(),
|
|
402
|
+
(0, class_validator_1.Matches)(/^[A-Za-z0-9-]{1,128}$/),
|
|
403
|
+
__metadata("design:type", String)
|
|
404
|
+
], CreateAgencyMilestoneDto.prototype, "creatorUserId", void 0);
|
|
405
|
+
__decorate([
|
|
406
|
+
(0, class_validator_1.IsOptional)(),
|
|
407
|
+
(0, class_validator_1.IsIn)(exports.AGENCY_MILESTONE_OFFER_RULES),
|
|
408
|
+
__metadata("design:type", Object)
|
|
409
|
+
], CreateAgencyMilestoneDto.prototype, "offerRule", void 0);
|
|
410
|
+
__decorate([
|
|
411
|
+
(0, class_validator_1.IsOptional)(),
|
|
412
|
+
(0, class_validator_1.IsIn)(exports.AGENCY_MILESTONE_MEDIA_RULES),
|
|
413
|
+
__metadata("design:type", Object)
|
|
414
|
+
], CreateAgencyMilestoneDto.prototype, "mediaRule", void 0);
|
|
415
|
+
/** `PATCH /users/me/settlement-form` — the creator's own setting (FR-027). */
|
|
416
|
+
class UpdateSettlementFormDto {
|
|
417
|
+
constructor(data) {
|
|
418
|
+
this.settlementForm = data?.settlementForm ?? null;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
exports.UpdateSettlementFormDto = UpdateSettlementFormDto;
|
|
422
|
+
__decorate([
|
|
423
|
+
(0, class_validator_1.IsOptional)(),
|
|
424
|
+
(0, class_validator_1.IsIn)(exports.SETTLEMENT_FORMS),
|
|
425
|
+
__metadata("design:type", String)
|
|
426
|
+
], UpdateSettlementFormDto.prototype, "settlementForm", void 0);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { CampaignSide, I_CampaignCreatorIdentity } from './agency-campaigns';
|
|
2
|
+
/**
|
|
3
|
+
* Agency ↔ creator threads (spec 175 US8, FR-023): one conversation per
|
|
4
|
+
* organization and creator, inside ad2app.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately NOT the creator's platform inbox: nothing here reaches a social
|
|
7
|
+
* platform, and no grant scope opens a thread (SC-015). A thread is the two
|
|
8
|
+
* parties' own; a grant is a read of the creator's numbers, and the two never
|
|
9
|
+
* meet.
|
|
10
|
+
*
|
|
11
|
+
* "Unanswered" is derived: the other side wrote last. There is no read state
|
|
12
|
+
* and no mark-read step.
|
|
13
|
+
*/
|
|
14
|
+
export declare const AGENCY_THREAD_CODES: {
|
|
15
|
+
/** No live relationship any more: the history stays readable, nothing can be sent (FR-023). */
|
|
16
|
+
readonly CLOSED: "agency-thread-closed";
|
|
17
|
+
/** Too many messages from one person in the window (THREAD_POST_LIMIT). */
|
|
18
|
+
readonly RATE_LIMITED: "agency-thread-rate-limited";
|
|
19
|
+
};
|
|
20
|
+
/** Messages one person may post across all their threads per window: a conversation pace, never a script's. */
|
|
21
|
+
export declare const THREAD_POST_LIMIT: {
|
|
22
|
+
readonly limit: 60;
|
|
23
|
+
readonly windowMinutes: 10;
|
|
24
|
+
};
|
|
25
|
+
/** Long enough for a real message, short enough to stay a message. */
|
|
26
|
+
export declare const THREAD_MESSAGE_MAX = 4000;
|
|
27
|
+
/** Messages per read: the newest page with the thread, earlier pages on request. */
|
|
28
|
+
export declare const THREAD_PAGE_SIZE = 100;
|
|
29
|
+
export interface I_AgencyThreadMessage {
|
|
30
|
+
id: string;
|
|
31
|
+
side: CampaignSide;
|
|
32
|
+
body: string;
|
|
33
|
+
at: string;
|
|
34
|
+
}
|
|
35
|
+
export interface I_AgencyThreadSummary {
|
|
36
|
+
id: string;
|
|
37
|
+
/** Agency side: who the thread is with. */
|
|
38
|
+
creator: I_CampaignCreatorIdentity;
|
|
39
|
+
/** Creator side: which organization. */
|
|
40
|
+
organization: {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
};
|
|
44
|
+
lastMessage: {
|
|
45
|
+
side: CampaignSide;
|
|
46
|
+
body: string;
|
|
47
|
+
at: string;
|
|
48
|
+
} | null;
|
|
49
|
+
/** The other side wrote last, seen from the caller's side. */
|
|
50
|
+
unanswered: boolean;
|
|
51
|
+
/** No live relationship: read-only for whoever still exists. */
|
|
52
|
+
closed: boolean;
|
|
53
|
+
}
|
|
54
|
+
export interface I_AgencyThread {
|
|
55
|
+
summary: I_AgencyThreadSummary;
|
|
56
|
+
/** The newest page, oldest first. */
|
|
57
|
+
messages: I_AgencyThreadMessage[];
|
|
58
|
+
/** Older messages exist: read them with `…/messages?before=<oldest id>`. */
|
|
59
|
+
hasEarlier: boolean;
|
|
60
|
+
}
|
|
61
|
+
/** `GET …/threads/:id/messages?before=<message id>` — the page before a message. */
|
|
62
|
+
export interface I_AgencyThreadPage {
|
|
63
|
+
/** Oldest first. */
|
|
64
|
+
messages: I_AgencyThreadMessage[];
|
|
65
|
+
hasEarlier: boolean;
|
|
66
|
+
}
|
|
67
|
+
/** `GET /agency/threads` — the agency's inbox. */
|
|
68
|
+
export interface I_AgencyThreads {
|
|
69
|
+
items: I_AgencyThreadSummary[];
|
|
70
|
+
/** Creators on the roster the agency has no open thread with yet. */
|
|
71
|
+
startable: I_CampaignCreatorIdentity[];
|
|
72
|
+
/** The Inbox rail count. */
|
|
73
|
+
unanswered: number;
|
|
74
|
+
}
|
|
75
|
+
/** `GET /agency/creator-threads/mine` — the creator's threads, in the Agency tab. */
|
|
76
|
+
export interface I_CreatorThreads {
|
|
77
|
+
items: I_AgencyThreadSummary[];
|
|
78
|
+
unanswered: number;
|
|
79
|
+
}
|
|
80
|
+
export declare class StartAgencyThreadDto {
|
|
81
|
+
creatorUserId: string;
|
|
82
|
+
constructor(data?: Partial<StartAgencyThreadDto>);
|
|
83
|
+
}
|
|
84
|
+
export declare class PostThreadMessageDto {
|
|
85
|
+
text: string;
|
|
86
|
+
constructor(data?: Partial<PostThreadMessageDto>);
|
|
87
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.PostThreadMessageDto = exports.StartAgencyThreadDto = exports.THREAD_PAGE_SIZE = exports.THREAD_MESSAGE_MAX = exports.THREAD_POST_LIMIT = exports.AGENCY_THREAD_CODES = void 0;
|
|
13
|
+
const class_validator_1 = require("class-validator");
|
|
14
|
+
/**
|
|
15
|
+
* Agency ↔ creator threads (spec 175 US8, FR-023): one conversation per
|
|
16
|
+
* organization and creator, inside ad2app.
|
|
17
|
+
*
|
|
18
|
+
* Deliberately NOT the creator's platform inbox: nothing here reaches a social
|
|
19
|
+
* platform, and no grant scope opens a thread (SC-015). A thread is the two
|
|
20
|
+
* parties' own; a grant is a read of the creator's numbers, and the two never
|
|
21
|
+
* meet.
|
|
22
|
+
*
|
|
23
|
+
* "Unanswered" is derived: the other side wrote last. There is no read state
|
|
24
|
+
* and no mark-read step.
|
|
25
|
+
*/
|
|
26
|
+
exports.AGENCY_THREAD_CODES = {
|
|
27
|
+
/** No live relationship any more: the history stays readable, nothing can be sent (FR-023). */
|
|
28
|
+
CLOSED: 'agency-thread-closed',
|
|
29
|
+
/** Too many messages from one person in the window (THREAD_POST_LIMIT). */
|
|
30
|
+
RATE_LIMITED: 'agency-thread-rate-limited',
|
|
31
|
+
};
|
|
32
|
+
/** Messages one person may post across all their threads per window: a conversation pace, never a script's. */
|
|
33
|
+
exports.THREAD_POST_LIMIT = { limit: 60, windowMinutes: 10 };
|
|
34
|
+
/** Long enough for a real message, short enough to stay a message. */
|
|
35
|
+
exports.THREAD_MESSAGE_MAX = 4000;
|
|
36
|
+
/** Messages per read: the newest page with the thread, earlier pages on request. */
|
|
37
|
+
exports.THREAD_PAGE_SIZE = 100;
|
|
38
|
+
class StartAgencyThreadDto {
|
|
39
|
+
constructor(data) {
|
|
40
|
+
this.creatorUserId = data?.creatorUserId ?? '';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.StartAgencyThreadDto = StartAgencyThreadDto;
|
|
44
|
+
__decorate([
|
|
45
|
+
(0, class_validator_1.IsString)(),
|
|
46
|
+
(0, class_validator_1.Matches)(/^[A-Za-z0-9-]{1,128}$/),
|
|
47
|
+
__metadata("design:type", String)
|
|
48
|
+
], StartAgencyThreadDto.prototype, "creatorUserId", void 0);
|
|
49
|
+
class PostThreadMessageDto {
|
|
50
|
+
constructor(data) {
|
|
51
|
+
this.text = data?.text ?? '';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.PostThreadMessageDto = PostThreadMessageDto;
|
|
55
|
+
__decorate([
|
|
56
|
+
(0, class_validator_1.IsString)(),
|
|
57
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
58
|
+
(0, class_validator_1.MaxLength)(exports.THREAD_MESSAGE_MAX),
|
|
59
|
+
__metadata("design:type", String)
|
|
60
|
+
], PostThreadMessageDto.prototype, "text", void 0);
|
package/dist/types/agency.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export declare const AGENCY_ROUTE_PATHS: {
|
|
|
33
33
|
readonly join: "/agency/join";
|
|
34
34
|
/** A creator answering an access request (FR-016). */
|
|
35
35
|
readonly requests: "/agency/requests";
|
|
36
|
+
/** A creator's campaign invitations and campaigns: the Agency tab of their Inbox (FR-023, FR-026). */
|
|
37
|
+
readonly creatorCampaigns: "/inbox/agency";
|
|
36
38
|
};
|
|
37
39
|
/**
|
|
38
40
|
* The complete set of reads a grant may carry. A CEILING, not a starting point.
|
package/dist/types/agency.js
CHANGED
|
@@ -32,6 +32,8 @@ exports.AGENCY_ROUTE_PATHS = {
|
|
|
32
32
|
join: '/agency/join',
|
|
33
33
|
/** A creator answering an access request (FR-016). */
|
|
34
34
|
requests: '/agency/requests',
|
|
35
|
+
/** A creator's campaign invitations and campaigns: the Agency tab of their Inbox (FR-023, FR-026). */
|
|
36
|
+
creatorCampaigns: '/inbox/agency',
|
|
35
37
|
};
|
|
36
38
|
// ── Scopes ───────────────────────────────────────────────────────────────────
|
|
37
39
|
/**
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
|
@@ -57,5 +57,7 @@ __exportStar(require("./scheduling"), exports);
|
|
|
57
57
|
__exportStar(require("./agent"), exports);
|
|
58
58
|
// ── Agency domain (organizations, memberships, creator access grants, spec 175)
|
|
59
59
|
__exportStar(require("./agency"), exports);
|
|
60
|
+
__exportStar(require("./agency-campaigns"), exports);
|
|
61
|
+
__exportStar(require("./agency-threads"), exports);
|
|
60
62
|
// ── Access control ────────────────────────────────────────────────────────────
|
|
61
63
|
__exportStar(require("./I_AccessDenial"), exports);
|
package/package.json
CHANGED
|
@@ -196,11 +196,51 @@ const EVENT_PROPERTY_WITNESS: { [E in keyof EventProperties]: EventProperties[E]
|
|
|
196
196
|
actor_role: "member",
|
|
197
197
|
origin: "agency_request",
|
|
198
198
|
},
|
|
199
|
+
[EVENTS.AGENCY_CAMPAIGN_CREATED]: { organization_id: "org-1", actor_role: "member", campaign_id: "c-1" },
|
|
200
|
+
[EVENTS.AGENCY_CAMPAIGN_CREATOR_INVITED]: {
|
|
201
|
+
organization_id: "org-1",
|
|
202
|
+
actor_role: "member",
|
|
203
|
+
campaign_id: "c-1",
|
|
204
|
+
creator_user_id: "u-1",
|
|
205
|
+
with_access_request: true,
|
|
206
|
+
},
|
|
207
|
+
[EVENTS.AGENCY_CAMPAIGN_JOINED]: { organization_id: "org-1", actor_role: "creator", campaign_id: "c-1" },
|
|
208
|
+
[EVENTS.AGENCY_CAMPAIGN_DECLINED]: { organization_id: "org-1", actor_role: "creator", campaign_id: "c-1" },
|
|
209
|
+
[EVENTS.AGENCY_CAMPAIGN_LEFT]: { organization_id: "org-1", actor_role: "creator", campaign_id: "c-1" },
|
|
210
|
+
[EVENTS.AGENCY_CAMPAIGN_CREATOR_REMOVED]: {
|
|
211
|
+
organization_id: "org-1",
|
|
212
|
+
actor_role: "owner",
|
|
213
|
+
campaign_id: "c-1",
|
|
214
|
+
creator_user_id: "u-1",
|
|
215
|
+
},
|
|
216
|
+
[EVENTS.AGENCY_CAMPAIGN_OFFER_SENT]: { organization_id: "org-1", actor_role: "member", campaign_id: "c-1" },
|
|
217
|
+
[EVENTS.AGENCY_CAMPAIGN_OFFER_ACTED]: {
|
|
218
|
+
organization_id: "org-1",
|
|
219
|
+
actor_role: "creator",
|
|
220
|
+
campaign_id: "c-1",
|
|
221
|
+
action: "counter",
|
|
222
|
+
},
|
|
223
|
+
[EVENTS.AGENCY_CAMPAIGN_MATERIAL_UPLOADED]: {
|
|
224
|
+
organization_id: "org-1",
|
|
225
|
+
actor_role: "creator",
|
|
226
|
+
campaign_id: "c-1",
|
|
227
|
+
version: 2,
|
|
228
|
+
},
|
|
229
|
+
[EVENTS.AGENCY_CAMPAIGN_MATERIAL_REVIEWED]: {
|
|
230
|
+
organization_id: "org-1",
|
|
231
|
+
actor_role: "member",
|
|
232
|
+
campaign_id: "c-1",
|
|
233
|
+
action: "request_changes",
|
|
234
|
+
},
|
|
235
|
+
[EVENTS.AGENCY_CAMPAIGN_PUBLISHED]: { organization_id: "org-1", actor_role: "member", campaign_id: "c-1" },
|
|
236
|
+
[EVENTS.AGENCY_CAMPAIGN_MILESTONE_CONFIRMED]: { organization_id: "org-1", actor_role: "owner", campaign_id: "c-1" },
|
|
237
|
+
[EVENTS.AGENCY_THREAD_STARTED]: { organization_id: "org-1", actor_role: "member", thread_id: "t-1" },
|
|
238
|
+
[EVENTS.AGENCY_THREAD_MESSAGE_SENT]: { organization_id: "org-1", actor_role: "creator", thread_id: "t-1" },
|
|
199
239
|
};
|
|
200
240
|
|
|
201
241
|
test("no agency event may carry the discover query — only whether it matched (FR-015)", () => {
|
|
202
242
|
const agencyEvents = Object.values(EVENTS).filter((name) => name.startsWith("agency_"));
|
|
203
|
-
assert.equal(agencyEvents.length,
|
|
243
|
+
assert.equal(agencyEvents.length, 29, "the 175 set is 15 roster events, 12 campaign-loop events (US7) and 2 thread events (US8)");
|
|
204
244
|
for (const name of agencyEvents) {
|
|
205
245
|
const props = EVENT_PROPERTY_WITNESS[name as keyof typeof EVENT_PROPERTY_WITNESS];
|
|
206
246
|
for (const key of Object.keys(props)) {
|