@jskit-ai/google-rewarded-core 0.1.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.
Files changed (42) hide show
  1. package/README.md +98 -0
  2. package/docs/protecting-server-actions.md +190 -0
  3. package/package.descriptor.mjs +186 -0
  4. package/package.json +10 -0
  5. package/src/server/GoogleRewardedCoreProvider.js +51 -0
  6. package/src/server/actions.js +102 -0
  7. package/src/server/inputSchemas.js +394 -0
  8. package/src/server/providerConfigs/GoogleRewardedProviderConfigsProvider.js +98 -0
  9. package/src/server/providerConfigs/actions.js +157 -0
  10. package/src/server/providerConfigs/registerRoutes.js +176 -0
  11. package/src/server/providerConfigs/repository.js +107 -0
  12. package/src/server/providerConfigs/service.js +61 -0
  13. package/src/server/registerRoutes.js +169 -0
  14. package/src/server/rules/GoogleRewardedRulesProvider.js +98 -0
  15. package/src/server/rules/actions.js +157 -0
  16. package/src/server/rules/registerRoutes.js +176 -0
  17. package/src/server/rules/repository.js +107 -0
  18. package/src/server/rules/service.js +61 -0
  19. package/src/server/service.js +552 -0
  20. package/src/server/support/requireGoogleRewardedUnlock.js +142 -0
  21. package/src/server/unlockReceipts/GoogleRewardedUnlockReceiptsProvider.js +98 -0
  22. package/src/server/unlockReceipts/actions.js +157 -0
  23. package/src/server/unlockReceipts/registerRoutes.js +176 -0
  24. package/src/server/unlockReceipts/repository.js +107 -0
  25. package/src/server/unlockReceipts/service.js +61 -0
  26. package/src/server/watchSessions/GoogleRewardedWatchSessionsProvider.js +98 -0
  27. package/src/server/watchSessions/actions.js +157 -0
  28. package/src/server/watchSessions/registerRoutes.js +176 -0
  29. package/src/server/watchSessions/repository.js +107 -0
  30. package/src/server/watchSessions/service.js +61 -0
  31. package/src/shared/googleRewardedProviderConfigResource.js +94 -0
  32. package/src/shared/googleRewardedRuleResource.js +136 -0
  33. package/src/shared/googleRewardedUnlockReceiptResource.js +113 -0
  34. package/src/shared/googleRewardedWatchSessionResource.js +139 -0
  35. package/src/shared/index.js +12 -0
  36. package/templates/migrations/google_rewarded_provider_configs_initial.cjs +27 -0
  37. package/templates/migrations/google_rewarded_rules_initial.cjs +31 -0
  38. package/templates/migrations/google_rewarded_unlock_receipts_initial.cjs +35 -0
  39. package/templates/migrations/google_rewarded_watch_sessions_initial.cjs +36 -0
  40. package/test/requireGoogleRewardedUnlock.test.js +274 -0
  41. package/test/routes.test.js +248 -0
  42. package/test/service.test.js +315 -0
@@ -0,0 +1,176 @@
1
+ import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
2
+ import { createCrudJsonApiRouteContracts } from "@jskit-ai/crud-core/server/routeContracts";
3
+ import { checkRouteVisibility } from "@jskit-ai/kernel/shared/support/visibility";
4
+ import { resolveScopedApiBasePath } from "@jskit-ai/kernel/shared/surface";
5
+ import { resource } from "../../shared/googleRewardedProviderConfigResource.js";
6
+ import { routeParamsValidator } from "@jskit-ai/workspaces-core/server/validators/routeParamsValidator";
7
+ import { buildWorkspaceInputFromRouteParams } from "@jskit-ai/workspaces-core/server/support/workspaceRouteInput";
8
+
9
+ const {
10
+ listRouteContract,
11
+ viewRouteContract,
12
+ createRouteContract,
13
+ updateRouteContract,
14
+ deleteRouteContract,
15
+ recordRouteParamsValidator
16
+ } = createCrudJsonApiRouteContracts({
17
+ resource,
18
+ routeParamsValidator
19
+ });
20
+
21
+ function registerRoutes(
22
+ app,
23
+ {
24
+ routeOwnershipFilter = "public",
25
+ routeSurface = "",
26
+ routeRelativePath = ""
27
+ } = {}
28
+ ) {
29
+ const router = app.make("jskit.http.router");
30
+ const normalizedRouteSurface = normalizeSurfaceId(routeSurface);
31
+ const routeBase = resolveScopedApiBasePath({
32
+ routeBase: "/w/:workspaceSlug",
33
+ relativePath: routeRelativePath,
34
+ strictParams: false
35
+ });
36
+
37
+ router.register(
38
+ "GET",
39
+ routeBase,
40
+ {
41
+ auth: "required",
42
+ surface: normalizedRouteSurface,
43
+ internal: true,
44
+ visibility: checkRouteVisibility(routeOwnershipFilter),
45
+ meta: {
46
+ tags: ["crud"],
47
+ summary: "List records."
48
+ },
49
+ ...listRouteContract,
50
+ params: routeParamsValidator,
51
+ },
52
+ async function (request, reply) {
53
+ const listInput = {
54
+ ...buildWorkspaceInputFromRouteParams(request.input.params),
55
+ ...(request.input.query || {})
56
+ };
57
+ const response = await request.executeAction({
58
+ actionId: "crud.google_rewarded_provider_configs.list",
59
+ input: listInput
60
+ });
61
+ reply.code(200).send(response);
62
+ }
63
+ );
64
+
65
+ router.register(
66
+ "GET",
67
+ `${routeBase}/:recordId`,
68
+ {
69
+ auth: "required",
70
+ surface: normalizedRouteSurface,
71
+ internal: true,
72
+ visibility: checkRouteVisibility(routeOwnershipFilter),
73
+ meta: {
74
+ tags: ["crud"],
75
+ summary: "View a record."
76
+ },
77
+ ...viewRouteContract,
78
+ params: recordRouteParamsValidator,
79
+ },
80
+ async function (request, reply) {
81
+ const response = await request.executeAction({
82
+ actionId: "crud.google_rewarded_provider_configs.view",
83
+ input: {
84
+ ...buildWorkspaceInputFromRouteParams(request.input.params),
85
+ recordId: request.input.params.recordId,
86
+ ...(request.input.query || {})
87
+ }
88
+ });
89
+ reply.code(200).send(response);
90
+ }
91
+ );
92
+
93
+ router.register(
94
+ "POST",
95
+ routeBase,
96
+ {
97
+ auth: "required",
98
+ surface: normalizedRouteSurface,
99
+ internal: true,
100
+ visibility: checkRouteVisibility(routeOwnershipFilter),
101
+ meta: {
102
+ tags: ["crud"],
103
+ summary: "Create a record."
104
+ },
105
+ ...createRouteContract,
106
+ params: routeParamsValidator,
107
+ },
108
+ async function (request, reply) {
109
+ const response = await request.executeAction({
110
+ actionId: "crud.google_rewarded_provider_configs.create",
111
+ input: {
112
+ ...buildWorkspaceInputFromRouteParams(request.input.params),
113
+ ...(request.input.body || {})
114
+ }
115
+ });
116
+ reply.code(201).send(response);
117
+ }
118
+ );
119
+
120
+ router.register(
121
+ "PATCH",
122
+ `${routeBase}/:recordId`,
123
+ {
124
+ auth: "required",
125
+ surface: normalizedRouteSurface,
126
+ internal: true,
127
+ visibility: checkRouteVisibility(routeOwnershipFilter),
128
+ meta: {
129
+ tags: ["crud"],
130
+ summary: "Update a record."
131
+ },
132
+ ...updateRouteContract,
133
+ params: recordRouteParamsValidator,
134
+ },
135
+ async function (request, reply) {
136
+ const response = await request.executeAction({
137
+ actionId: "crud.google_rewarded_provider_configs.update",
138
+ input: {
139
+ ...buildWorkspaceInputFromRouteParams(request.input.params),
140
+ recordId: request.input.params.recordId,
141
+ ...(request.input.body || {})
142
+ }
143
+ });
144
+ reply.code(200).send(response);
145
+ }
146
+ );
147
+
148
+ router.register(
149
+ "DELETE",
150
+ `${routeBase}/:recordId`,
151
+ {
152
+ auth: "required",
153
+ surface: normalizedRouteSurface,
154
+ internal: true,
155
+ visibility: checkRouteVisibility(routeOwnershipFilter),
156
+ meta: {
157
+ tags: ["crud"],
158
+ summary: "Delete a record."
159
+ },
160
+ ...deleteRouteContract,
161
+ params: recordRouteParamsValidator,
162
+ },
163
+ async function (request, reply) {
164
+ const response = await request.executeAction({
165
+ actionId: "crud.google_rewarded_provider_configs.delete",
166
+ input: {
167
+ ...buildWorkspaceInputFromRouteParams(request.input.params),
168
+ recordId: request.input.params.recordId
169
+ }
170
+ });
171
+ reply.code(204).send(response);
172
+ }
173
+ );
174
+ }
175
+
176
+ export { registerRoutes };
@@ -0,0 +1,107 @@
1
+ import { createWithTransaction } from "@jskit-ai/database-runtime/shared";
2
+ import {
3
+ buildJsonRestQueryParams,
4
+ createJsonApiInputRecord,
5
+ createJsonRestContext,
6
+ returnNullWhenJsonRestResourceMissing
7
+ } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
8
+ import { resource } from "../../shared/googleRewardedProviderConfigResource.js";
9
+ const JSON_REST_SCOPE_NAME = "googleRewardedProviderConfigs";
10
+
11
+ function createRepository({ api, knex } = {}) {
12
+ const withTransaction = createWithTransaction(knex);
13
+
14
+ async function queryDocuments(query = {}, options = {}) {
15
+ return api.resources.googleRewardedProviderConfigs.query(
16
+ {
17
+ queryParams: buildJsonRestQueryParams(JSON_REST_SCOPE_NAME, query),
18
+ transaction: options?.trx || null,
19
+ simplified: false
20
+ },
21
+ createJsonRestContext(options?.context || null)
22
+ );
23
+ }
24
+
25
+ async function getDocumentById(recordId, options = {}) {
26
+ return returnNullWhenJsonRestResourceMissing(() =>
27
+ api.resources.googleRewardedProviderConfigs.get(
28
+ {
29
+ id: recordId,
30
+ queryParams: buildJsonRestQueryParams(JSON_REST_SCOPE_NAME, {}, {
31
+ include: options?.include
32
+ }),
33
+ transaction: options?.trx || null,
34
+ simplified: false
35
+ },
36
+ createJsonRestContext(options?.context || null)
37
+ )
38
+ );
39
+ }
40
+
41
+ async function createDocument(payload = {}, options = {}) {
42
+ return api.resources.googleRewardedProviderConfigs.post(
43
+ {
44
+ inputRecord: createJsonApiInputRecord(JSON_REST_SCOPE_NAME, payload, {
45
+ resource
46
+ }),
47
+ transaction: options?.trx || null,
48
+ simplified: false
49
+ },
50
+ createJsonRestContext(options?.context || null)
51
+ );
52
+ }
53
+
54
+ async function patchDocumentById(recordId, patch = {}, options = {}) {
55
+ const sourcePatch = patch && typeof patch === "object" && !Array.isArray(patch) ? patch : {};
56
+ if (Object.keys(sourcePatch).length < 1) {
57
+ return getDocumentById(recordId, options);
58
+ }
59
+
60
+ return returnNullWhenJsonRestResourceMissing(() =>
61
+ api.resources.googleRewardedProviderConfigs.patch(
62
+ {
63
+ id: recordId,
64
+ inputRecord: createJsonApiInputRecord(
65
+ JSON_REST_SCOPE_NAME,
66
+ {
67
+ ...sourcePatch,
68
+ updatedAt: new Date()
69
+ },
70
+ {
71
+ resource
72
+ }
73
+ ),
74
+ transaction: options?.trx || null,
75
+ simplified: false
76
+ },
77
+ createJsonRestContext(options?.context || null)
78
+ )
79
+ );
80
+ }
81
+
82
+ async function deleteDocumentById(recordId, options = {}) {
83
+ return returnNullWhenJsonRestResourceMissing(async () => {
84
+ await api.resources.googleRewardedProviderConfigs.delete(
85
+ {
86
+ id: recordId,
87
+ transaction: options?.trx || null,
88
+ simplified: false
89
+ },
90
+ createJsonRestContext(options?.context || null)
91
+ );
92
+
93
+ return null;
94
+ });
95
+ }
96
+
97
+ return Object.freeze({
98
+ withTransaction,
99
+ queryDocuments,
100
+ getDocumentById,
101
+ createDocument,
102
+ patchDocumentById,
103
+ deleteDocumentById
104
+ });
105
+ }
106
+
107
+ export { createRepository };
@@ -0,0 +1,61 @@
1
+ import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
2
+ import { returnJsonApiDocument } from "@jskit-ai/http-runtime/shared";
3
+
4
+ function return404IfNotFound(document = null) {
5
+ if (!document) {
6
+ throw new AppError(404, "Document not found.");
7
+ }
8
+ return document;
9
+ }
10
+
11
+ function createService({ googleRewardedProviderConfigsRepository } = {}) {
12
+ if (!googleRewardedProviderConfigsRepository) {
13
+ throw new TypeError("createService requires googleRewardedProviderConfigsRepository.");
14
+ }
15
+
16
+ async function queryDocuments(query = {}, options = {}) {
17
+ return returnJsonApiDocument(await googleRewardedProviderConfigsRepository.queryDocuments(query, {
18
+ trx: options?.trx || null,
19
+ context: options?.context || null
20
+ }));
21
+ }
22
+
23
+ async function getDocumentById(recordId, options = {}) {
24
+ return returnJsonApiDocument(return404IfNotFound(await googleRewardedProviderConfigsRepository.getDocumentById(recordId, {
25
+ trx: options?.trx || null,
26
+ context: options?.context || null,
27
+ include: options?.include
28
+ })));
29
+ }
30
+
31
+ async function createDocument(payload = {}, options = {}) {
32
+ return returnJsonApiDocument(await googleRewardedProviderConfigsRepository.createDocument(payload, {
33
+ trx: options?.trx || null,
34
+ context: options?.context || null
35
+ }));
36
+ }
37
+
38
+ async function patchDocumentById(recordId, payload = {}, options = {}) {
39
+ return returnJsonApiDocument(return404IfNotFound(await googleRewardedProviderConfigsRepository.patchDocumentById(recordId, payload, {
40
+ trx: options?.trx || null,
41
+ context: options?.context || null
42
+ })));
43
+ }
44
+
45
+ async function deleteDocumentById(recordId, options = {}) {
46
+ return googleRewardedProviderConfigsRepository.deleteDocumentById(recordId, {
47
+ trx: options?.trx || null,
48
+ context: options?.context || null
49
+ });
50
+ }
51
+
52
+ return Object.freeze({
53
+ queryDocuments,
54
+ getDocumentById,
55
+ createDocument,
56
+ patchDocumentById,
57
+ deleteDocumentById
58
+ });
59
+ }
60
+
61
+ export { createService };
@@ -0,0 +1,169 @@
1
+ import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
2
+ import { checkRouteVisibility } from "@jskit-ai/kernel/shared/support/visibility";
3
+ import { resolveScopedApiBasePath } from "@jskit-ai/kernel/shared/surface";
4
+ import {
5
+ createTransportResponseSchema,
6
+ withStandardErrorResponses
7
+ } from "@jskit-ai/http-runtime/shared/validators/errorResponses";
8
+ import { routeParamsValidator } from "@jskit-ai/workspaces-core/server/validators/routeParamsValidator";
9
+ import { buildWorkspaceInputFromRouteParams } from "@jskit-ai/workspaces-core/server/support/workspaceRouteInput";
10
+ import {
11
+ ACTION_CURRENT,
12
+ ACTION_START,
13
+ ACTION_GRANT,
14
+ ACTION_CLOSE
15
+ } from "./actions.js";
16
+ import {
17
+ currentQueryInputValidator,
18
+ startCommandInputValidator,
19
+ grantCommandInputValidator,
20
+ closeCommandInputValidator,
21
+ currentStateOutputValidator,
22
+ startGateOutputValidator,
23
+ grantRewardOutputValidator,
24
+ closeSessionOutputValidator
25
+ } from "./inputSchemas.js";
26
+
27
+ function createWorkflowResponses(outputValidator) {
28
+ return withStandardErrorResponses({
29
+ 200: createTransportResponseSchema(
30
+ outputValidator.schema.toJsonSchema({
31
+ mode: outputValidator.mode
32
+ })
33
+ )
34
+ }, {
35
+ includeValidation400: true
36
+ });
37
+ }
38
+
39
+ function registerRoutes(
40
+ app,
41
+ {
42
+ routeOwnershipFilter = "workspace_user",
43
+ routeSurface = "app",
44
+ routeRelativePath = "google-rewarded"
45
+ } = {}
46
+ ) {
47
+ if (!app || typeof app.make !== "function") {
48
+ throw new Error("registerRoutes requires application make().");
49
+ }
50
+
51
+ const router = app.make("jskit.http.router");
52
+ const normalizedRouteSurface = normalizeSurfaceId(routeSurface);
53
+ const routeBase = resolveScopedApiBasePath({
54
+ routeBase: "/w/:workspaceSlug",
55
+ relativePath: routeRelativePath,
56
+ strictParams: false
57
+ });
58
+ const visibility = checkRouteVisibility(routeOwnershipFilter);
59
+
60
+ router.register(
61
+ "GET",
62
+ `${routeBase}/current`,
63
+ {
64
+ auth: "required",
65
+ surface: normalizedRouteSurface,
66
+ visibility,
67
+ params: routeParamsValidator,
68
+ query: currentQueryInputValidator,
69
+ responses: createWorkflowResponses(currentStateOutputValidator),
70
+ meta: {
71
+ tags: ["google-rewarded"],
72
+ summary: "Read the current Google rewarded gate state."
73
+ }
74
+ },
75
+ async function googleRewardedCurrentRoute(request, reply) {
76
+ const response = await request.executeAction({
77
+ actionId: ACTION_CURRENT,
78
+ input: {
79
+ ...buildWorkspaceInputFromRouteParams(request.input.params),
80
+ ...(request.input.query || {})
81
+ }
82
+ });
83
+ reply.code(200).send(response);
84
+ }
85
+ );
86
+
87
+ router.register(
88
+ "POST",
89
+ `${routeBase}/start`,
90
+ {
91
+ auth: "required",
92
+ surface: normalizedRouteSurface,
93
+ visibility,
94
+ params: routeParamsValidator,
95
+ body: startCommandInputValidator,
96
+ responses: createWorkflowResponses(startGateOutputValidator),
97
+ meta: {
98
+ tags: ["google-rewarded"],
99
+ summary: "Start a Google rewarded watch session."
100
+ }
101
+ },
102
+ async function googleRewardedStartRoute(request, reply) {
103
+ const response = await request.executeAction({
104
+ actionId: ACTION_START,
105
+ input: {
106
+ ...buildWorkspaceInputFromRouteParams(request.input.params),
107
+ ...(request.input.body || {})
108
+ }
109
+ });
110
+ reply.code(200).send(response);
111
+ }
112
+ );
113
+
114
+ router.register(
115
+ "POST",
116
+ `${routeBase}/grant`,
117
+ {
118
+ auth: "required",
119
+ surface: normalizedRouteSurface,
120
+ visibility,
121
+ params: routeParamsValidator,
122
+ body: grantCommandInputValidator,
123
+ responses: createWorkflowResponses(grantRewardOutputValidator),
124
+ meta: {
125
+ tags: ["google-rewarded"],
126
+ summary: "Grant a Google rewarded unlock after a rewarded ad completes."
127
+ }
128
+ },
129
+ async function googleRewardedGrantRoute(request, reply) {
130
+ const response = await request.executeAction({
131
+ actionId: ACTION_GRANT,
132
+ input: {
133
+ ...buildWorkspaceInputFromRouteParams(request.input.params),
134
+ ...(request.input.body || {})
135
+ }
136
+ });
137
+ reply.code(200).send(response);
138
+ }
139
+ );
140
+
141
+ router.register(
142
+ "POST",
143
+ `${routeBase}/close`,
144
+ {
145
+ auth: "required",
146
+ surface: normalizedRouteSurface,
147
+ visibility,
148
+ params: routeParamsValidator,
149
+ body: closeCommandInputValidator,
150
+ responses: createWorkflowResponses(closeSessionOutputValidator),
151
+ meta: {
152
+ tags: ["google-rewarded"],
153
+ summary: "Close a Google rewarded watch session without granting a reward."
154
+ }
155
+ },
156
+ async function googleRewardedCloseRoute(request, reply) {
157
+ const response = await request.executeAction({
158
+ actionId: ACTION_CLOSE,
159
+ input: {
160
+ ...buildWorkspaceInputFromRouteParams(request.input.params),
161
+ ...(request.input.body || {})
162
+ }
163
+ });
164
+ reply.code(200).send(response);
165
+ }
166
+ );
167
+ }
168
+
169
+ export { registerRoutes };
@@ -0,0 +1,98 @@
1
+ import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
2
+ import { resolveCrudSurfacePolicyFromAppConfig } from "@jskit-ai/crud-core/server/crudModuleConfig";
3
+ import { createCrudJsonApiServiceEvents } from "@jskit-ai/crud-core/server/serviceEvents";
4
+ import {
5
+ INTERNAL_JSON_REST_API,
6
+ addResourceIfMissing,
7
+ createJsonRestResourceScopeOptions
8
+ } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
9
+ import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
10
+ import { toDatabaseDateTimeUtc } from "@jskit-ai/database-runtime/shared";
11
+ import { createRepository } from "./repository.js";
12
+ import { createService } from "./service.js";
13
+ import { createActions } from "./actions.js";
14
+ import { registerRoutes } from "./registerRoutes.js";
15
+ import { resource } from "../../shared/googleRewardedRuleResource.js";
16
+ const CRUD_MODULE_CONFIG = Object.freeze({
17
+ namespace: "google_rewarded_rules",
18
+ surface: "admin",
19
+ ownershipFilter: "workspace",
20
+ relativePath: "/google-rewarded-rules"
21
+ });
22
+ const baseServiceEvents = createCrudJsonApiServiceEvents(CRUD_MODULE_CONFIG.namespace);
23
+ const serviceEvents = {
24
+ ...baseServiceEvents
25
+ };
26
+
27
+ function resolveCrudPolicyFromApp(app) {
28
+ return resolveCrudSurfacePolicyFromAppConfig(CRUD_MODULE_CONFIG, resolveAppConfig(app), {
29
+ context: "GoogleRewardedRulesProvider"
30
+ });
31
+ }
32
+
33
+ class GoogleRewardedRulesProvider {
34
+ static id = "crud.google_rewarded_rules";
35
+
36
+ static dependsOn = ["runtime.actions", "runtime.database", "auth.policy.fastify", "local.main", "json-rest-api.core"];
37
+
38
+ register(app) {
39
+ const crudPolicy = resolveCrudPolicyFromApp(app);
40
+
41
+ app.singleton("repository.google_rewarded_rules", (scope) => {
42
+ const api = scope.make(INTERNAL_JSON_REST_API);
43
+ const knex = scope.make("jskit.database.knex");
44
+ return createRepository({
45
+ api,
46
+ knex
47
+ });
48
+ });
49
+
50
+ app.service(
51
+ "crud.google_rewarded_rules",
52
+ (scope) => {
53
+ return createService({
54
+ googleRewardedRulesRepository: scope.make("repository.google_rewarded_rules")
55
+ });
56
+ },
57
+ {
58
+ events: serviceEvents
59
+ }
60
+ );
61
+
62
+ app.actions(
63
+ withActionDefaults(
64
+ createActions({
65
+ surface: crudPolicy.surfaceId
66
+ }),
67
+ {
68
+ domain: "crud",
69
+ dependencies: {
70
+ googleRewardedRulesService: "crud.google_rewarded_rules"
71
+ }
72
+ }
73
+ )
74
+ );
75
+ }
76
+
77
+ async boot(app) {
78
+ const crudPolicy = resolveCrudPolicyFromApp(app);
79
+ const api = app.make(INTERNAL_JSON_REST_API);
80
+ await addResourceIfMissing(
81
+ api,
82
+ "googleRewardedRules",
83
+ createJsonRestResourceScopeOptions(resource, {
84
+ writeSerializers: {
85
+ "datetime-utc": toDatabaseDateTimeUtc
86
+ }
87
+ })
88
+ );
89
+ registerRoutes(app, {
90
+ routeOwnershipFilter: crudPolicy.ownershipFilter,
91
+ routeSurface: crudPolicy.surfaceId,
92
+ routeSurfaceRequiresWorkspace: crudPolicy.surfaceDefinition.requiresWorkspace === true,
93
+ routeRelativePath: crudPolicy.relativePath
94
+ });
95
+ }
96
+ }
97
+
98
+ export { GoogleRewardedRulesProvider };