@aglyn/tenant-data-admin 1.0.0-beta.229 → 1.0.0-beta.231
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/package.json +7 -7
- package/src/lib/server/duplicate-resource.js +4 -1
- package/src/lib/server/duplicate-resource.js.map +1 -1
- package/src/lib/server/erase.js +14 -0
- package/src/lib/server/erase.js.map +1 -1
- package/src/lib/server/host-released-names.d.ts +22 -0
- package/src/lib/server/host-released-names.js +31 -0
- package/src/lib/server/host-released-names.js.map +1 -0
- package/src/lib/server/host-visitor-records.d.ts +1 -1
- package/src/lib/server/host-visitor-records.js +1 -1
- package/src/lib/server/host-visitor-records.js.map +1 -1
- package/src/lib/server/mobile-push-switch.d.ts +46 -0
- package/src/lib/server/mobile-push-switch.js +45 -0
- package/src/lib/server/mobile-push-switch.js.map +1 -0
- package/src/lib/server/native-push.d.ts +17 -0
- package/src/lib/server/native-push.js +35 -0
- package/src/lib/server/native-push.js.map +1 -0
- package/src/lib/server/notifications.js +23 -0
- package/src/lib/server/notifications.js.map +1 -1
- package/src/lib/server/personal-data-export.js +108 -0
- package/src/lib/server/personal-data-export.js.map +1 -1
- package/src/lib/server/plugin-event-outbox.d.ts +138 -0
- package/src/lib/server/plugin-event-outbox.js +229 -0
- package/src/lib/server/plugin-event-outbox.js.map +1 -0
- package/src/lib/server/push-apns.d.ts +73 -0
- package/src/lib/server/push-apns.js +225 -0
- package/src/lib/server/push-apns.js.map +1 -0
- package/src/lib/server/push-delivery.d.ts +78 -0
- package/src/lib/server/push-delivery.js +176 -0
- package/src/lib/server/push-delivery.js.map +1 -0
- package/src/lib/server/push-fcm.d.ts +29 -0
- package/src/lib/server/push-fcm.js +83 -0
- package/src/lib/server/push-fcm.js.map +1 -0
- package/src/lib/server/serve-media-cdn.d.ts +6 -0
- package/src/lib/server/serve-media-cdn.js +20 -3
- package/src/lib/server/serve-media-cdn.js.map +1 -1
- package/src/lib/server/site-marketing-sync.d.ts +146 -0
- package/src/lib/server/site-marketing-sync.js +315 -0
- package/src/lib/server/site-marketing-sync.js.map +1 -0
- package/src/lib/server/upsert-contact.js +6 -5
- package/src/lib/server/upsert-contact.js.map +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/push-fcm.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Firebase Cloud Messaging for the Android app (AGL-3651), through\n * firebase-admin's `getMessaging().sendEach` with the project credentials\n * the server already holds, so it needs no secret of its own. The\n * notification is shown by the system; the tap data rides in `data`, and\n * Android delivers it at high priority.\n *\n * A token FCM reports unregistered or invalid is gone, and the row is pruned.\n *\n * Server-only, imported by `push-delivery.ts` only when a device is on FCM.\n */\n\nimport { getApp } from 'firebase-admin/app'\nimport { getMessaging, type BatchResponse, type TokenMessage } from 'firebase-admin/messaging'\nimport './firebase-admin'\nimport type { PushMessage, PushOutcome, PushTarget, PushTransport } from './push-delivery'\n\n/** `sendEach` takes at most 500 messages. */\nexport const FCM_BATCH = 500\n\nconst GONE = new Set([\n 'messaging/registration-token-not-registered',\n 'messaging/invalid-registration-token',\n 'messaging/invalid-argument',\n])\n\n/** The slice of firebase-admin's Messaging this transport uses; injected in tests. */\nexport interface FcmMessaging {\n sendEach(messages: TokenMessage[]): Promise<BatchResponse>\n}\n\nexport function fcmMessage(token: string, message: PushMessage): TokenMessage {\n return {\n token,\n notification: { title: message.title, ...(message.body ? { body: message.body } : {}) },\n // FCM data values are strings; MobilePushData's are.\n data: { ...message.data },\n android: { priority: 'high', notification: { sound: 'default' } },\n }\n}\n\nexport function createFcmTransport(messaging: FcmMessaging): PushTransport {\n return {\n async send(targets: readonly PushTarget[], message: PushMessage): Promise<PushOutcome[]> {\n const outcomes: PushOutcome[] = []\n for (let start = 0; start < targets.length; start += FCM_BATCH) {\n const batch = targets.slice(start, start + FCM_BATCH)\n try {\n const { responses } = await messaging.sendEach(batch.map((target) => fcmMessage(target.token, message)))\n for (const [index] of batch.entries()) {\n const response = responses[index]\n if (response?.success) outcomes.push('sent')\n else outcomes.push(GONE.has(response?.error?.code ?? '') ? 'prune' : 'failed')\n }\n } catch {\n outcomes.push(...batch.map((): PushOutcome => 'failed'))\n }\n }\n return outcomes\n },\n }\n}\n\nlet configured: PushTransport | null = null\n\n/** The deployment's FCM transport, on the firebase-admin default app. */\nexport function fcmTransport(): PushTransport {\n configured ??= createFcmTransport(getMessaging(getApp()))\n return configured\n}\n"],"names":["getApp","getMessaging","FCM_BATCH","GONE","Set","fcmMessage","token","message","notification","title","body","data","android","priority","sound","createFcmTransport","messaging","send","targets","outcomes","start","length","batch","slice","responses","sendEach","map","target","index","entries","response","success","push","has","error","code","configured","fcmTransport"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;CAUC,GAED,SAASA,MAAM,QAAQ,qBAAoB;AAC3C,SAASC,YAAY,QAA+C,2BAA0B;AAC9F,OAAO,sBAAkB;AAGzB,2CAA2C,GAC3C,OAAO,MAAMC,YAAY,IAAG;AAE5B,MAAMC,OAAO,IAAIC,IAAI;IACnB;IACA;IACA;CACD;AAOD,OAAO,SAASC,WAAWC,KAAa,EAAEC,OAAoB;IAC5D,OAAO;QACLD;QACAE,cAAc;YAAEC,OAAOF,QAAQE,KAAK;WAAMF,QAAQG,IAAI,GAAG;YAAEA,MAAMH,QAAQG,IAAI;QAAC,IAAI,CAAC;QACnF,qDAAqD;QACrDC,MAAM,aAAKJ,QAAQI,IAAI;QACvBC,SAAS;YAAEC,UAAU;YAAQL,cAAc;gBAAEM,OAAO;YAAU;QAAE;IAClE;AACF;AAEA,OAAO,SAASC,mBAAmBC,SAAuB;IACxD,OAAO;QACL,MAAMC,MAAKC,OAA8B,EAAEX,OAAoB;YAC7D,MAAMY,WAA0B,EAAE;YAClC,IAAK,IAAIC,QAAQ,GAAGA,QAAQF,QAAQG,MAAM,EAAED,SAASlB,UAAW;gBAC9D,MAAMoB,QAAQJ,QAAQK,KAAK,CAACH,OAAOA,QAAQlB;gBAC3C,IAAI;oBACF,MAAM,EAAEsB,SAAS,EAAE,GAAG,MAAMR,UAAUS,QAAQ,CAACH,MAAMI,GAAG,CAAC,CAACC,SAAWtB,WAAWsB,OAAOrB,KAAK,EAAEC;oBAC9F,KAAK,MAAM,CAACqB,MAAM,IAAIN,MAAMO,OAAO,GAAI;;4BAGTC;wBAF5B,MAAMA,WAAWN,SAAS,CAACI,MAAM;wBACjC,IAAIE,4BAAAA,SAAUC,OAAO,EAAEZ,SAASa,IAAI,CAAC;6BAChCb,SAASa,IAAI,CAAC7B,KAAK8B,GAAG,SAACH,6BAAAA,kBAAAA,SAAUI,KAAK,qBAAfJ,gBAAiBK,IAAI,mBAAI,MAAM,UAAU;oBACvE;gBACF,EAAE,eAAM;oBACNhB,SAASa,IAAI,IAAIV,MAAMI,GAAG,CAAC,IAAmB;gBAChD;YACF;YACA,OAAOP;QACT;IACF;AACF;AAEA,IAAIiB,aAAmC;AAEvC,uEAAuE,GACvE,OAAO,SAASC;IACdD,qBAAAA,aAAAA,aAAerB,mBAAmBd,aAAaD;IAC/C,OAAOoC;AACT"}
|
|
@@ -180,6 +180,12 @@ export declare function mediaCdnVersionIsCurrent(options: {
|
|
|
180
180
|
/** The document's {@link MEDIA_VARIANT_ENCODER_VERSION_FIELD}. */
|
|
181
181
|
documentEncoderVersion: unknown;
|
|
182
182
|
}): boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Whether an asset of this type is only ever served as its own bytes — no
|
|
185
|
+
* `?w=` variant, display copy, poster or rendition (AGL-3656). A web font is
|
|
186
|
+
* the case: a versioned URL that names its hash names everything served.
|
|
187
|
+
*/
|
|
188
|
+
export declare function mediaCdnHasNoDerivedForms(contentType: unknown): boolean;
|
|
183
189
|
/** The immutable content-hashed URL's policy for edge-cacheable (image) types. */
|
|
184
190
|
export declare const MEDIA_CDN_IMMUTABLE_CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
185
191
|
/**
|
|
@@ -221,6 +221,13 @@ const SEGMENT = MEDIA_CDN_SEGMENT;
|
|
|
221
221
|
const generation = typeof recorded === 'number' && Number.isInteger(recorded) && recorded > 0 ? recorded : 1;
|
|
222
222
|
return token.encoderVersion === MEDIA_VARIANT_ENCODER_VERSION && generation === token.encoderVersion;
|
|
223
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Whether an asset of this type is only ever served as its own bytes — no
|
|
226
|
+
* `?w=` variant, display copy, poster or rendition (AGL-3656). A web font is
|
|
227
|
+
* the case: a versioned URL that names its hash names everything served.
|
|
228
|
+
*/ export function mediaCdnHasNoDerivedForms(contentType) {
|
|
229
|
+
return String(contentType != null ? contentType : '').split(';')[0].trim().toLowerCase() === 'font/woff2';
|
|
230
|
+
}
|
|
224
231
|
/** The immutable content-hashed URL's policy for edge-cacheable (image) types. */ export const MEDIA_CDN_IMMUTABLE_CACHE_CONTROL = 'public, max-age=31536000, immutable';
|
|
225
232
|
/**
|
|
226
233
|
* The immutable URL's policy for edge-bypassing types (AGL-1515): the
|
|
@@ -246,7 +253,12 @@ const SEGMENT = MEDIA_CDN_SEGMENT;
|
|
|
246
253
|
*
|
|
247
254
|
* Unknown or absent types return false: correctness over cache.
|
|
248
255
|
*/ export function mediaCdnEdgeCacheable(contentType) {
|
|
249
|
-
|
|
256
|
+
const type = String(contentType != null ? contentType : '').split(';')[0].trim().toLowerCase();
|
|
257
|
+
// A web font (AGL-3656) is fetched the way an image is — one plain GET
|
|
258
|
+
// from an `@font-face` rule, never a range — and it is part of the page
|
|
259
|
+
// weight the band already measures, so it takes the image policy: the
|
|
260
|
+
// edge holds it, and a versioned URL a page names is kept for a year.
|
|
261
|
+
return type.startsWith('image/') || type === 'font/woff2';
|
|
250
262
|
}
|
|
251
263
|
/**
|
|
252
264
|
* The CDN's OWN Content-Security-Policy (AGL-1474).
|
|
@@ -337,7 +349,9 @@ const SEGMENT = MEDIA_CDN_SEGMENT;
|
|
|
337
349
|
* decision back without adding to it.
|
|
338
350
|
*/ function servesInline(contentType) {
|
|
339
351
|
const type = String(contentType != null ? contentType : '').split(';')[0].trim().toLowerCase();
|
|
340
|
-
return type.startsWith('image/') || type.startsWith('video/') || type.startsWith('audio/') || type === 'application/pdf'
|
|
352
|
+
return type.startsWith('image/') || type.startsWith('video/') || type.startsWith('audio/') || type === 'application/pdf' || // A web font (AGL-3656): an `@font-face` rule loads it, and a font is
|
|
353
|
+
// inert outside a stylesheet, so a top-level open has nothing to run.
|
|
354
|
+
type === 'font/woff2';
|
|
341
355
|
}
|
|
342
356
|
/** The `Content-Security-Policy` for a response serving `contentType`. */ export function mediaCdnContentSecurityPolicy(contentType) {
|
|
343
357
|
const type = String(contentType != null ? contentType : '').split(';')[0].trim().toLowerCase();
|
|
@@ -1311,7 +1325,10 @@ export function parseMediaCdnRange(header, size) {
|
|
|
1311
1325
|
widthRequested: Boolean(width),
|
|
1312
1326
|
variantServed: exactVariant,
|
|
1313
1327
|
otherRepresentation: usePoster || Boolean(rendition),
|
|
1314
|
-
|
|
1328
|
+
// A web font has no derived form for an encoder to have made, so no
|
|
1329
|
+
// generation can be behind (AGL-3656): only its bytes are named, and
|
|
1330
|
+
// a library on a plan without generated variants records none.
|
|
1331
|
+
documentEncoderVersion: mediaCdnHasNoDerivedForms(snapshot.get('contentType')) ? MEDIA_VARIANT_ENCODER_VERSION : snapshot.get(MEDIA_VARIANT_ENCODER_VERSION_FIELD)
|
|
1315
1332
|
});
|
|
1316
1333
|
const stableCacheControlFor = (type)=>mediaCdnEdgeCacheable(type) ? versionCurrent ? MEDIA_CDN_VERSIONED_CACHE_CONTROL : MEDIA_CDN_STABLE_CACHE_CONTROL : MEDIA_CDN_STABLE_EDGE_BYPASS_CACHE_CONTROL;
|
|
1317
1334
|
// A poster is `image/webp` and therefore edge-cacheable through the rule
|