@jskit-ai/rewarded-core 0.1.120

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.
@@ -0,0 +1,566 @@
1
+ import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
2
+ import { normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
3
+ import { simplifyJsonApiDocument } from "@jskit-ai/http-runtime/shared";
4
+
5
+ const REWARDED_SURFACE = "app";
6
+
7
+ function resolveActionUser(context, input) {
8
+ const payload = normalizeObject(input);
9
+ const request = context?.requestMeta?.request || null;
10
+ return payload.user || request?.user || context?.actor || null;
11
+ }
12
+
13
+ function normalizeRecordList(document = null) {
14
+ const simplified = simplifyJsonApiDocument(document);
15
+ return Array.isArray(simplified) ? simplified.filter(Boolean) : [];
16
+ }
17
+
18
+ function normalizeRecord(document = null) {
19
+ const simplified = simplifyJsonApiDocument(document);
20
+ return simplified && typeof simplified === "object" && !Array.isArray(simplified) ? simplified : null;
21
+ }
22
+
23
+ function normalizeGateKey(value = "") {
24
+ return normalizeText(value);
25
+ }
26
+
27
+ function normalizeSurface(value = "", { fallback = REWARDED_SURFACE } = {}) {
28
+ const normalized = normalizeText(value).toLowerCase();
29
+ return normalized || fallback;
30
+ }
31
+
32
+ function toIsoOrNull(value = null) {
33
+ if (!value) {
34
+ return null;
35
+ }
36
+ const date = value instanceof Date ? value : new Date(value);
37
+ return Number.isNaN(date.getTime()) ? null : date.toISOString();
38
+ }
39
+
40
+ function parseDate(value = null) {
41
+ if (!value) {
42
+ return null;
43
+ }
44
+ const date = value instanceof Date ? value : new Date(value);
45
+ return Number.isNaN(date.getTime()) ? null : date;
46
+ }
47
+
48
+ function addMinutes(date, minutes) {
49
+ const amount = Number(minutes || 0);
50
+ return new Date(date.getTime() + Math.max(0, amount) * 60 * 1000);
51
+ }
52
+
53
+ function startOfUtcDay(date) {
54
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
55
+ }
56
+
57
+ function requireActor(context, input) {
58
+ const actor = resolveActionUser(context, input);
59
+ const actorId = actor?.id == null ? "" : String(actor.id).trim();
60
+ if (!actorId) {
61
+ throw new AppError(401, "Authentication required.");
62
+ }
63
+ return actor;
64
+ }
65
+
66
+ function requireWorkspaceSlug(input = {}) {
67
+ const workspaceSlug = normalizeText(input?.workspaceSlug).toLowerCase();
68
+ if (!workspaceSlug) {
69
+ throw new AppError(400, "workspaceSlug is required.");
70
+ }
71
+ return workspaceSlug;
72
+ }
73
+
74
+ function requireGateKey(input = {}) {
75
+ const gateKey = normalizeGateKey(input?.gateKey);
76
+ if (!gateKey) {
77
+ throw new AppError(400, "gateKey is required.");
78
+ }
79
+ return gateKey;
80
+ }
81
+
82
+ function formatRule(rule = null) {
83
+ if (!rule) {
84
+ return null;
85
+ }
86
+
87
+ return {
88
+ id: rule.id || null,
89
+ gateKey: normalizeGateKey(rule.gateKey),
90
+ surface: normalizeSurface(rule.surface, { fallback: REWARDED_SURFACE }),
91
+ enabled: rule.enabled === true,
92
+ unlockMinutes: Number(rule.unlockMinutes || 0),
93
+ cooldownMinutes: Number(rule.cooldownMinutes || 0),
94
+ dailyLimit: rule.dailyLimit == null ? null : Number(rule.dailyLimit),
95
+ title: normalizeText(rule.title),
96
+ description: normalizeText(rule.description)
97
+ };
98
+ }
99
+
100
+ function formatProviderConfig(record = null) {
101
+ if (!record) {
102
+ return null;
103
+ }
104
+
105
+ return {
106
+ id: record.id || null,
107
+ surface: normalizeSurface(record.surface, { fallback: REWARDED_SURFACE }),
108
+ enabled: record.enabled === true,
109
+ placement: normalizeText(record.placement),
110
+ provider: normalizeText(record.provider)
111
+ };
112
+ }
113
+
114
+ function formatWatchSession(record = null) {
115
+ if (!record) {
116
+ return null;
117
+ }
118
+
119
+ return {
120
+ id: record.id || null,
121
+ gateKey: normalizeGateKey(record.gateKey),
122
+ providerConfigId: record.providerConfigId || null,
123
+ status: normalizeSurface(record.status, { fallback: "started" }),
124
+ startedAt: toIsoOrNull(record.startedAt),
125
+ rewardedAt: toIsoOrNull(record.rewardedAt),
126
+ completedAt: toIsoOrNull(record.completedAt),
127
+ closedAt: toIsoOrNull(record.closedAt)
128
+ };
129
+ }
130
+
131
+ function formatUnlockReceipt(record = null) {
132
+ if (!record) {
133
+ return null;
134
+ }
135
+
136
+ return {
137
+ id: record.id || null,
138
+ gateKey: normalizeGateKey(record.gateKey),
139
+ providerConfigId: record.providerConfigId || null,
140
+ watchSessionId: record.watchSessionId || null,
141
+ grantedAt: toIsoOrNull(record.grantedAt),
142
+ unlockedUntil: toIsoOrNull(record.unlockedUntil)
143
+ };
144
+ }
145
+
146
+ function isFutureDate(value = null, now = new Date()) {
147
+ const date = parseDate(value);
148
+ return !!date && date.getTime() > now.getTime();
149
+ }
150
+
151
+ function createService({
152
+ authorizeGrant,
153
+ rewardedRulesRepository,
154
+ rewardedProviderConfigsRepository,
155
+ rewardedWatchSessionsRepository,
156
+ rewardedUnlockReceiptsRepository
157
+ } = {}) {
158
+ if (typeof authorizeGrant !== "function") {
159
+ throw new TypeError("createService requires application authorizeGrant policy.");
160
+ }
161
+ if (!rewardedRulesRepository) {
162
+ throw new TypeError("createService requires rewardedRulesRepository.");
163
+ }
164
+ if (!rewardedProviderConfigsRepository) {
165
+ throw new TypeError("createService requires rewardedProviderConfigsRepository.");
166
+ }
167
+ if (!rewardedWatchSessionsRepository) {
168
+ throw new TypeError("createService requires rewardedWatchSessionsRepository.");
169
+ }
170
+ if (!rewardedUnlockReceiptsRepository) {
171
+ throw new TypeError("createService requires rewardedUnlockReceiptsRepository.");
172
+ }
173
+
174
+ async function queryFirst(repository, query = {}, options = {}) {
175
+ const rows = normalizeRecordList(await repository.queryDocuments(query, options));
176
+ return rows[0] || null;
177
+ }
178
+
179
+ async function listRecords(repository, query = {}, options = {}) {
180
+ return normalizeRecordList(await repository.queryDocuments(query, options));
181
+ }
182
+
183
+ async function resolveRule({ gateKey, surface, context, trx }) {
184
+ const filters = {
185
+ gateKey,
186
+ enabled: "true"
187
+ };
188
+ if (surface) {
189
+ filters.surface = surface;
190
+ }
191
+ return queryFirst(rewardedRulesRepository, {
192
+ ...filters,
193
+ sort: ["-updatedAt"],
194
+ limit: 1
195
+ }, {
196
+ context,
197
+ trx
198
+ });
199
+ }
200
+
201
+ async function resolveProviderConfig({ surface, context, trx }) {
202
+ return queryFirst(rewardedProviderConfigsRepository, {
203
+ surface,
204
+ enabled: "true",
205
+ sort: ["-updatedAt"],
206
+ limit: 1
207
+ }, {
208
+ context,
209
+ trx
210
+ });
211
+ }
212
+
213
+ async function resolveUnlockReceipts({ gateKey, context, trx, limit = 25 }) {
214
+ return listRecords(rewardedUnlockReceiptsRepository, {
215
+ gateKey,
216
+ sort: ["-grantedAt"],
217
+ limit
218
+ }, {
219
+ context,
220
+ trx
221
+ });
222
+ }
223
+
224
+ async function evaluateGate(input = {}, options = {}) {
225
+ requireActor(options?.context || null, input);
226
+ const workspaceSlug = requireWorkspaceSlug(input);
227
+ const gateKey = requireGateKey(input);
228
+ const surface = REWARDED_SURFACE;
229
+ const now = new Date();
230
+
231
+ const ruleRecord = await resolveRule({
232
+ gateKey,
233
+ surface,
234
+ context: options?.context || null,
235
+ trx: options?.trx || null
236
+ });
237
+
238
+ if (!ruleRecord) {
239
+ return {
240
+ gateKey,
241
+ workspaceSlug,
242
+ surface,
243
+ enabled: false,
244
+ available: false,
245
+ blocked: false,
246
+ reason: "rule-not-configured",
247
+ rule: null,
248
+ providerConfig: null,
249
+ unlock: null,
250
+ cooldownUntil: null,
251
+ dailyLimitRemaining: null
252
+ };
253
+ }
254
+
255
+ const providerConfigRecord = await resolveProviderConfig({
256
+ surface,
257
+ context: options?.context || null,
258
+ trx: options?.trx || null
259
+ });
260
+ if (!providerConfigRecord) {
261
+ return {
262
+ gateKey,
263
+ workspaceSlug,
264
+ surface,
265
+ enabled: false,
266
+ available: false,
267
+ blocked: false,
268
+ reason: "provider-not-configured",
269
+ rule: formatRule(ruleRecord),
270
+ providerConfig: null,
271
+ unlock: null,
272
+ cooldownUntil: null,
273
+ dailyLimitRemaining: null
274
+ };
275
+ }
276
+
277
+ const rule = formatRule(ruleRecord);
278
+ const providerConfig = formatProviderConfig(providerConfigRecord);
279
+ const receipts = await resolveUnlockReceipts({
280
+ gateKey,
281
+ context: options?.context || null,
282
+ trx: options?.trx || null
283
+ });
284
+ const activeReceiptRecord = receipts.find((entry) => isFutureDate(entry?.unlockedUntil, now)) || null;
285
+ const activeUnlock = formatUnlockReceipt(activeReceiptRecord);
286
+
287
+ if (activeUnlock) {
288
+ return {
289
+ gateKey,
290
+ workspaceSlug,
291
+ surface,
292
+ enabled: true,
293
+ available: true,
294
+ blocked: false,
295
+ reason: "already-unlocked",
296
+ rule,
297
+ providerConfig,
298
+ unlock: activeUnlock,
299
+ cooldownUntil: null,
300
+ dailyLimitRemaining: rule.dailyLimit
301
+ };
302
+ }
303
+
304
+ const latestReceipt = receipts[0] || null;
305
+ let cooldownUntil = null;
306
+ if (latestReceipt && Number(rule.cooldownMinutes || 0) > 0) {
307
+ cooldownUntil = addMinutes(parseDate(latestReceipt.grantedAt) || now, rule.cooldownMinutes);
308
+ if (cooldownUntil.getTime() > now.getTime()) {
309
+ return {
310
+ gateKey,
311
+ workspaceSlug,
312
+ surface,
313
+ enabled: true,
314
+ available: false,
315
+ blocked: false,
316
+ reason: "cooldown-active",
317
+ rule,
318
+ providerConfig,
319
+ unlock: null,
320
+ cooldownUntil: cooldownUntil.toISOString(),
321
+ dailyLimitRemaining: rule.dailyLimit
322
+ };
323
+ }
324
+ }
325
+
326
+ if (rule.dailyLimit != null) {
327
+ const dayStart = startOfUtcDay(now);
328
+ const todaysCount = receipts.filter((entry) => {
329
+ const grantedAt = parseDate(entry?.grantedAt);
330
+ return grantedAt && grantedAt.getTime() >= dayStart.getTime();
331
+ }).length;
332
+ const remaining = Math.max(0, Number(rule.dailyLimit) - todaysCount);
333
+ if (remaining < 1) {
334
+ return {
335
+ gateKey,
336
+ workspaceSlug,
337
+ surface,
338
+ enabled: true,
339
+ available: false,
340
+ blocked: false,
341
+ reason: "daily-limit-reached",
342
+ rule,
343
+ providerConfig,
344
+ unlock: null,
345
+ cooldownUntil: null,
346
+ dailyLimitRemaining: 0
347
+ };
348
+ }
349
+
350
+ return {
351
+ gateKey,
352
+ workspaceSlug,
353
+ surface,
354
+ enabled: true,
355
+ available: true,
356
+ blocked: true,
357
+ reason: "reward-required",
358
+ rule,
359
+ providerConfig,
360
+ unlock: null,
361
+ cooldownUntil: null,
362
+ dailyLimitRemaining: remaining
363
+ };
364
+ }
365
+
366
+ return {
367
+ gateKey,
368
+ workspaceSlug,
369
+ surface,
370
+ enabled: true,
371
+ available: true,
372
+ blocked: true,
373
+ reason: "reward-required",
374
+ rule,
375
+ providerConfig,
376
+ unlock: null,
377
+ cooldownUntil: null,
378
+ dailyLimitRemaining: null
379
+ };
380
+ }
381
+
382
+ async function getCurrentState(input = {}, options = {}) {
383
+ return evaluateGate(input, options);
384
+ }
385
+
386
+ async function startGate(input = {}, options = {}) {
387
+ const state = await evaluateGate(input, options);
388
+ if (!state.enabled || !state.available || !state.blocked) {
389
+ return {
390
+ ...state,
391
+ session: null
392
+ };
393
+ }
394
+
395
+ const createdSession = normalizeRecord(await rewardedWatchSessionsRepository.createDocument(
396
+ {
397
+ gateKey: state.gateKey,
398
+ providerConfigId: state.providerConfig?.id || null,
399
+ status: "started",
400
+ startedAt: new Date().toISOString()
401
+ },
402
+ {
403
+ context: options?.context || null,
404
+ trx: options?.trx || null
405
+ }
406
+ ));
407
+
408
+ return {
409
+ ...state,
410
+ session: formatWatchSession(createdSession)
411
+ };
412
+ }
413
+
414
+ async function grantReward(input = {}, options = {}) {
415
+ requireActor(options?.context || null, input);
416
+ const workspaceSlug = requireWorkspaceSlug(input);
417
+ const sessionId = input?.sessionId == null ? "" : String(input.sessionId).trim();
418
+ if (!sessionId) {
419
+ throw new AppError(400, "sessionId is required.");
420
+ }
421
+
422
+ return rewardedWatchSessionsRepository.withTransaction(async (trx) => {
423
+ const sessionRecord = normalizeRecord(await rewardedWatchSessionsRepository.getDocumentById(sessionId, {}, {
424
+ context: options?.context || null,
425
+ trx
426
+ }));
427
+ if (!sessionRecord) {
428
+ throw new AppError(404, "Watch session not found.");
429
+ }
430
+
431
+ const authorized = await authorizeGrant({
432
+ session: formatWatchSession(sessionRecord),
433
+ context: options?.context || null,
434
+ trx
435
+ });
436
+ if (authorized !== true) {
437
+ throw new AppError(403, "Reward grant is not authorized.");
438
+ }
439
+
440
+ if (normalizeText(sessionRecord.status).toLowerCase() === "closed") {
441
+ throw new AppError(409, "Watch session is already closed.");
442
+ }
443
+ if (sessionRecord.rewardedAt || normalizeText(sessionRecord.status).toLowerCase() === "rewarded") {
444
+ const receiptRecord = await queryFirst(rewardedUnlockReceiptsRepository, {
445
+ watchSessionId: sessionId,
446
+ sort: ["-grantedAt"],
447
+ limit: 1
448
+ }, {
449
+ context: options?.context || null,
450
+ trx
451
+ });
452
+ return {
453
+ unlocked: true,
454
+ workspaceSlug,
455
+ gateKey: normalizeGateKey(sessionRecord.gateKey),
456
+ unlock: formatUnlockReceipt(receiptRecord),
457
+ session: formatWatchSession(sessionRecord)
458
+ };
459
+ }
460
+
461
+ const ruleRecord = await resolveRule({
462
+ gateKey: normalizeGateKey(sessionRecord.gateKey),
463
+ surface: REWARDED_SURFACE,
464
+ context: options?.context || null,
465
+ trx
466
+ });
467
+ if (!ruleRecord) {
468
+ throw new AppError(409, "No rewarded rule is configured for this session.");
469
+ }
470
+
471
+ const now = new Date();
472
+ const nowIso = now.toISOString();
473
+ const unlockedUntil = addMinutes(now, Number(ruleRecord.unlockMinutes || 0));
474
+
475
+ const updatedSession = normalizeRecord(await rewardedWatchSessionsRepository.patchDocumentById(
476
+ sessionId,
477
+ {
478
+ status: "rewarded",
479
+ rewardedAt: nowIso,
480
+ completedAt: nowIso
481
+ },
482
+ {
483
+ context: options?.context || null,
484
+ trx
485
+ }
486
+ ));
487
+
488
+ const createdReceipt = normalizeRecord(await rewardedUnlockReceiptsRepository.createDocument(
489
+ {
490
+ gateKey: normalizeGateKey(sessionRecord.gateKey),
491
+ providerConfigId: sessionRecord.providerConfigId || null,
492
+ watchSessionId: sessionId,
493
+ grantedAt: nowIso,
494
+ unlockedUntil: unlockedUntil.toISOString()
495
+ },
496
+ {
497
+ context: options?.context || null,
498
+ trx
499
+ }
500
+ ));
501
+
502
+ return {
503
+ unlocked: true,
504
+ workspaceSlug,
505
+ gateKey: normalizeGateKey(sessionRecord.gateKey),
506
+ unlock: formatUnlockReceipt(createdReceipt),
507
+ session: formatWatchSession(updatedSession)
508
+ };
509
+ });
510
+ }
511
+
512
+ async function closeSession(input = {}, options = {}) {
513
+ requireActor(options?.context || null, input);
514
+ const workspaceSlug = requireWorkspaceSlug(input);
515
+ const sessionId = input?.sessionId == null ? "" : String(input.sessionId).trim();
516
+ if (!sessionId) {
517
+ throw new AppError(400, "sessionId is required.");
518
+ }
519
+
520
+ const sessionRecord = normalizeRecord(await rewardedWatchSessionsRepository.getDocumentById(sessionId, {}, {
521
+ context: options?.context || null,
522
+ trx: options?.trx || null
523
+ }));
524
+ if (!sessionRecord) {
525
+ throw new AppError(404, "Watch session not found.");
526
+ }
527
+
528
+ if (sessionRecord.rewardedAt || normalizeText(sessionRecord.status).toLowerCase() === "rewarded") {
529
+ return {
530
+ closed: false,
531
+ workspaceSlug,
532
+ gateKey: normalizeGateKey(sessionRecord.gateKey),
533
+ session: formatWatchSession(sessionRecord),
534
+ reason: "already-rewarded"
535
+ };
536
+ }
537
+
538
+ const closedSession = normalizeRecord(await rewardedWatchSessionsRepository.patchDocumentById(
539
+ sessionId,
540
+ {
541
+ status: "closed",
542
+ closedAt: new Date().toISOString()
543
+ },
544
+ {
545
+ context: options?.context || null,
546
+ trx: options?.trx || null
547
+ }
548
+ ));
549
+
550
+ return {
551
+ closed: true,
552
+ workspaceSlug,
553
+ gateKey: normalizeGateKey(closedSession?.gateKey || sessionRecord.gateKey),
554
+ session: formatWatchSession(closedSession)
555
+ };
556
+ }
557
+
558
+ return Object.freeze({
559
+ getCurrentState,
560
+ startGate,
561
+ grantReward,
562
+ closeSession
563
+ });
564
+ }
565
+
566
+ export { createService };
@@ -0,0 +1,142 @@
1
+ import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
2
+ import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
3
+
4
+ const REWARDED_SURFACE = "app";
5
+
6
+ const REWARDED_GATE_CONFIGURATION_REASONS = new Set([
7
+ "rule-not-configured",
8
+ "provider-not-configured"
9
+ ]);
10
+ const REWARDED_GATE_NON_BLOCKING_REASONS = new Set([
11
+ "already-unlocked",
12
+ "cooldown-active",
13
+ "daily-limit-reached",
14
+ ...REWARDED_GATE_CONFIGURATION_REASONS
15
+ ]);
16
+
17
+ const REWARDED_GATE_REASON_ERROR_MAP = Object.freeze({
18
+ "reward-required": {
19
+ status: 423,
20
+ code: "rewarded_unlock_required",
21
+ message: "Rewarded unlock required."
22
+ },
23
+ "cooldown-active": {
24
+ status: 423,
25
+ code: "rewarded_cooldown_active",
26
+ message: "Rewarded unlock is cooling down."
27
+ },
28
+ "daily-limit-reached": {
29
+ status: 423,
30
+ code: "rewarded_daily_limit_reached",
31
+ message: "Rewarded unlock limit reached."
32
+ },
33
+ "rule-not-configured": {
34
+ status: 503,
35
+ code: "rewarded_not_configured",
36
+ message: "Rewarded gate is not configured."
37
+ },
38
+ "provider-not-configured": {
39
+ status: 503,
40
+ code: "rewarded_not_configured",
41
+ message: "Rewarded gate is not configured."
42
+ }
43
+ });
44
+
45
+ function normalizeReason(value = "") {
46
+ return normalizeText(value).toLowerCase();
47
+ }
48
+
49
+ function isUnlockSatisfied(gateState = null) {
50
+ const reason = normalizeReason(gateState?.reason);
51
+ return Boolean(gateState?.unlock) || reason === "already-unlocked";
52
+ }
53
+
54
+ function hasBooleanGateFlag(gateState, key) {
55
+ return gateState && typeof gateState === "object" && typeof gateState[key] === "boolean";
56
+ }
57
+
58
+ function assertWellFormedGateState(gateState = null) {
59
+ if (hasBooleanGateFlag(gateState, "enabled") && hasBooleanGateFlag(gateState, "blocked")) {
60
+ if (
61
+ gateState.blocked === true ||
62
+ isUnlockSatisfied(gateState) ||
63
+ REWARDED_GATE_NON_BLOCKING_REASONS.has(normalizeReason(gateState?.reason))
64
+ ) {
65
+ return;
66
+ }
67
+ }
68
+
69
+ throw new AppError(503, "Rewarded gate returned an invalid state.", {
70
+ code: "rewarded_gate_state_invalid",
71
+ details: {
72
+ rewardedGate: gateState
73
+ }
74
+ });
75
+ }
76
+
77
+ function isConfigurationBypassAllowed(gateState = null, requireConfigured = false) {
78
+ if (requireConfigured === true) {
79
+ return false;
80
+ }
81
+
82
+ if (gateState?.enabled === true) {
83
+ return false;
84
+ }
85
+
86
+ const reason = normalizeReason(gateState?.reason);
87
+ return REWARDED_GATE_CONFIGURATION_REASONS.has(reason) || !reason;
88
+ }
89
+
90
+ function createRewardedGateError(gateState = null, {
91
+ errorCode = "",
92
+ errorMessage = ""
93
+ } = {}) {
94
+ const reason = normalizeReason(gateState?.reason);
95
+ const defaults = REWARDED_GATE_REASON_ERROR_MAP[reason] || {
96
+ status: 423,
97
+ code: "rewarded_unlock_blocked",
98
+ message: "Rewarded unlock is required before this action can continue."
99
+ };
100
+
101
+ return new AppError(defaults.status, errorMessage || defaults.message, {
102
+ code: errorCode || defaults.code,
103
+ details: {
104
+ rewardedGate: gateState
105
+ }
106
+ });
107
+ }
108
+
109
+ async function requireRewardedUnlock(rewardedService, input = {}, {
110
+ context = null,
111
+ requireConfigured = false,
112
+ errorCode = "",
113
+ errorMessage = ""
114
+ } = {}) {
115
+ if (!rewardedService || typeof rewardedService.getCurrentState !== "function") {
116
+ throw new TypeError("requireRewardedUnlock requires rewardedService.getCurrentState().");
117
+ }
118
+
119
+ const gateState = await rewardedService.getCurrentState(
120
+ {
121
+ ...input,
122
+ surface: REWARDED_SURFACE
123
+ },
124
+ {
125
+ context
126
+ }
127
+ );
128
+ assertWellFormedGateState(gateState);
129
+
130
+ if (isUnlockSatisfied(gateState) || isConfigurationBypassAllowed(gateState, requireConfigured)) {
131
+ return gateState;
132
+ }
133
+
134
+ throw createRewardedGateError(gateState, {
135
+ errorCode,
136
+ errorMessage
137
+ });
138
+ }
139
+
140
+ export {
141
+ requireRewardedUnlock
142
+ };
@@ -0,0 +1,12 @@
1
+ export {
2
+ resource as rewardedRuleResource
3
+ } from "./rewardedRuleResource.js";
4
+ export {
5
+ resource as rewardedProviderConfigResource
6
+ } from "./rewardedProviderConfigResource.js";
7
+ export {
8
+ resource as rewardedWatchSessionResource
9
+ } from "./rewardedWatchSessionResource.js";
10
+ export {
11
+ resource as rewardedUnlockReceiptResource
12
+ } from "./rewardedUnlockReceiptResource.js";