@growth-labs/push 0.2.0 → 0.4.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.
- package/README.md +6 -2
- package/dist/db/schema.d.ts +24 -10
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +28 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/queue.d.ts +18 -2
- package/dist/utils/queue.d.ts.map +1 -1
- package/dist/utils/queue.js +43 -9
- package/dist/utils/queue.js.map +1 -1
- package/dist/utils/scheduled.d.ts +1 -0
- package/dist/utils/scheduled.d.ts.map +1 -1
- package/dist/utils/scheduled.js +1 -1
- package/dist/utils/scheduled.js.map +1 -1
- package/package.json +13 -7
- package/src/db/schema.ts +14 -0
- package/src/index.ts +69 -0
- package/src/middleware/push.ts +10 -0
- package/src/options.ts +45 -0
- package/src/routes/subscribe.ts +82 -0
- package/src/routes/unsubscribe.ts +29 -0
- package/src/types.ts +129 -0
- package/src/utils/analytics.ts +96 -0
- package/src/utils/cleanup.ts +28 -0
- package/src/utils/composer.ts +141 -0
- package/src/utils/index.ts +38 -0
- package/src/utils/queue.ts +173 -0
- package/src/utils/scheduled.ts +152 -0
- package/src/utils/send.ts +140 -0
- package/src/utils/subscriptions.ts +124 -0
- package/src/utils/turnstile.ts +25 -0
- package/src/utils/vapid.ts +28 -0
- package/src/client/push-sw.d.ts.map +0 -1
- package/src/client/push-sw.js +0 -25
- package/src/client/push-sw.js.map +0 -1
package/README.md
CHANGED
|
@@ -46,7 +46,11 @@ import { sendPushNotification, enqueuePushBatchAll } from '@growth-labs/push/uti
|
|
|
46
46
|
await sendPushNotification(subscription, { title: 'Breaking', body: '...' }, sendContext)
|
|
47
47
|
|
|
48
48
|
// Batch send to all subscribers (via Queue)
|
|
49
|
-
await enqueuePushBatchAll(env.SITE_DB, payload, {
|
|
49
|
+
await enqueuePushBatchAll(env.SITE_DB, payload, {
|
|
50
|
+
queue: env.PUSH_QUEUE,
|
|
51
|
+
siteId: 'fronts', // required since 0.4.0
|
|
52
|
+
batchSize: 100,
|
|
53
|
+
})
|
|
50
54
|
```
|
|
51
55
|
|
|
52
56
|
## Queue Consumer
|
|
@@ -54,7 +58,7 @@ await enqueuePushBatchAll(env.SITE_DB, payload, { queue: env.PUSH_QUEUE, batchSi
|
|
|
54
58
|
Package provides handler, consumer wires it up:
|
|
55
59
|
|
|
56
60
|
```typescript
|
|
57
|
-
import { handlePushQueue } from '@growth-labs/push/
|
|
61
|
+
import { handlePushQueue } from '@growth-labs/push/consumer'
|
|
58
62
|
|
|
59
63
|
export default {
|
|
60
64
|
async queue(batch, env) {
|
package/dist/db/schema.d.ts
CHANGED
|
@@ -18,7 +18,9 @@ export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLite
|
|
|
18
18
|
baseColumn: never;
|
|
19
19
|
identity: undefined;
|
|
20
20
|
generated: undefined;
|
|
21
|
-
},
|
|
21
|
+
}, {}, {
|
|
22
|
+
length: number | undefined;
|
|
23
|
+
}>;
|
|
22
24
|
endpoint: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
23
25
|
name: "endpoint";
|
|
24
26
|
tableName: "gl_push_subscriptions";
|
|
@@ -35,7 +37,9 @@ export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLite
|
|
|
35
37
|
baseColumn: never;
|
|
36
38
|
identity: undefined;
|
|
37
39
|
generated: undefined;
|
|
38
|
-
},
|
|
40
|
+
}, {}, {
|
|
41
|
+
length: number | undefined;
|
|
42
|
+
}>;
|
|
39
43
|
keyP256dh: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
40
44
|
name: "key_p256dh";
|
|
41
45
|
tableName: "gl_push_subscriptions";
|
|
@@ -52,7 +56,9 @@ export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLite
|
|
|
52
56
|
baseColumn: never;
|
|
53
57
|
identity: undefined;
|
|
54
58
|
generated: undefined;
|
|
55
|
-
},
|
|
59
|
+
}, {}, {
|
|
60
|
+
length: number | undefined;
|
|
61
|
+
}>;
|
|
56
62
|
keyAuth: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
57
63
|
name: "key_auth";
|
|
58
64
|
tableName: "gl_push_subscriptions";
|
|
@@ -69,7 +75,9 @@ export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLite
|
|
|
69
75
|
baseColumn: never;
|
|
70
76
|
identity: undefined;
|
|
71
77
|
generated: undefined;
|
|
72
|
-
},
|
|
78
|
+
}, {}, {
|
|
79
|
+
length: number | undefined;
|
|
80
|
+
}>;
|
|
73
81
|
topics: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
74
82
|
name: "topics";
|
|
75
83
|
tableName: "gl_push_subscriptions";
|
|
@@ -86,7 +94,9 @@ export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLite
|
|
|
86
94
|
baseColumn: never;
|
|
87
95
|
identity: undefined;
|
|
88
96
|
generated: undefined;
|
|
89
|
-
},
|
|
97
|
+
}, {}, {
|
|
98
|
+
length: number | undefined;
|
|
99
|
+
}>;
|
|
90
100
|
createdAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
91
101
|
name: "created_at";
|
|
92
102
|
tableName: "gl_push_subscriptions";
|
|
@@ -103,7 +113,7 @@ export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLite
|
|
|
103
113
|
baseColumn: never;
|
|
104
114
|
identity: undefined;
|
|
105
115
|
generated: undefined;
|
|
106
|
-
},
|
|
116
|
+
}, {}, {}>;
|
|
107
117
|
lastDeliveryAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
108
118
|
name: "last_delivery_at";
|
|
109
119
|
tableName: "gl_push_subscriptions";
|
|
@@ -120,7 +130,7 @@ export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLite
|
|
|
120
130
|
baseColumn: never;
|
|
121
131
|
identity: undefined;
|
|
122
132
|
generated: undefined;
|
|
123
|
-
},
|
|
133
|
+
}, {}, {}>;
|
|
124
134
|
failureCount: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
125
135
|
name: "failure_count";
|
|
126
136
|
tableName: "gl_push_subscriptions";
|
|
@@ -137,7 +147,7 @@ export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLite
|
|
|
137
147
|
baseColumn: never;
|
|
138
148
|
identity: undefined;
|
|
139
149
|
generated: undefined;
|
|
140
|
-
},
|
|
150
|
+
}, {}, {}>;
|
|
141
151
|
userAgent: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
142
152
|
name: "user_agent";
|
|
143
153
|
tableName: "gl_push_subscriptions";
|
|
@@ -154,7 +164,9 @@ export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLite
|
|
|
154
164
|
baseColumn: never;
|
|
155
165
|
identity: undefined;
|
|
156
166
|
generated: undefined;
|
|
157
|
-
},
|
|
167
|
+
}, {}, {
|
|
168
|
+
length: number | undefined;
|
|
169
|
+
}>;
|
|
158
170
|
country: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
159
171
|
name: "country";
|
|
160
172
|
tableName: "gl_push_subscriptions";
|
|
@@ -171,7 +183,9 @@ export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLite
|
|
|
171
183
|
baseColumn: never;
|
|
172
184
|
identity: undefined;
|
|
173
185
|
generated: undefined;
|
|
174
|
-
},
|
|
186
|
+
}, {}, {
|
|
187
|
+
length: number | undefined;
|
|
188
|
+
}>;
|
|
175
189
|
};
|
|
176
190
|
dialect: "sqlite";
|
|
177
191
|
}>;
|
package/dist/db/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW5B,CAAA"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,cAAc,CAAA;AAQlE,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,gBAAgB,CA0DnE"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
1
2
|
import { pushOptionsSchema } from './options.js';
|
|
3
|
+
const ENTRYPOINT_EXTENSION = import.meta.url.endsWith('.ts') ? '.ts' : '.js';
|
|
4
|
+
function resolveEntrypoint(path) {
|
|
5
|
+
return fileURLToPath(new URL(`${path}${ENTRYPOINT_EXTENSION}`, import.meta.url));
|
|
6
|
+
}
|
|
2
7
|
export default function push(options) {
|
|
3
8
|
const resolved = pushOptionsSchema.parse(options);
|
|
4
9
|
const virtualConfig = {
|
|
@@ -36,17 +41,17 @@ export default function push(options) {
|
|
|
36
41
|
},
|
|
37
42
|
});
|
|
38
43
|
addMiddleware({
|
|
39
|
-
entrypoint: '
|
|
44
|
+
entrypoint: resolveEntrypoint('./middleware/push'),
|
|
40
45
|
order: 'pre',
|
|
41
46
|
});
|
|
42
47
|
injectRoute({
|
|
43
48
|
pattern: resolved.routes.subscribe,
|
|
44
|
-
entrypoint: '
|
|
49
|
+
entrypoint: resolveEntrypoint('./routes/subscribe'),
|
|
45
50
|
prerender: false,
|
|
46
51
|
});
|
|
47
52
|
injectRoute({
|
|
48
53
|
pattern: resolved.routes.unsubscribe,
|
|
49
|
-
entrypoint: '
|
|
54
|
+
entrypoint: resolveEntrypoint('./routes/unsubscribe'),
|
|
50
55
|
prerender: false,
|
|
51
56
|
});
|
|
52
57
|
},
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAoB,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAElE,MAAM,oBAAoB,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAA;AAE5E,SAAS,iBAAiB,CAAC,IAAY;IACtC,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,oBAAoB,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AACjF,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,OAAoB;IAChD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAEjD,MAAM,aAAa,GAAG;QACrB,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;KACzB,CAAA;IAED,MAAM,eAAe,GAAG,iCAAiC,CAAA;IACzD,MAAM,uBAAuB,GAAG,KAAK,eAAe,EAAE,CAAA;IAEtD,OAAO;QACN,IAAI,EAAE,mBAAmB;QACzB,KAAK,EAAE;YACN,oBAAoB,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,EAAE;gBACtE,YAAY,CAAC;oBACZ,IAAI,EAAE;wBACL,OAAO,EAAE;4BACR;gCACC,IAAI,EAAE,yBAAyB;gCAC/B,SAAS,CAAC,EAAE;oCACX,IAAI,EAAE,KAAK,eAAe;wCAAE,OAAO,uBAAuB,CAAA;gCAC3D,CAAC;gCACD,IAAI,CAAC,EAAE;oCACN,IAAI,EAAE,KAAK,uBAAuB,EAAE,CAAC;wCACpC,OAAO,kBAAkB,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,EAAE,CAAA;oCACzD,CAAC;gCACF,CAAC;6BACD;yBACD;qBACD;iBACD,CAAC,CAAA;gBAEF,aAAa,CAAC;oBACb,UAAU,EAAE,iBAAiB,CAAC,mBAAmB,CAAC;oBAClD,KAAK,EAAE,KAAK;iBACZ,CAAC,CAAA;gBAEF,WAAW,CAAC;oBACX,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS;oBAClC,UAAU,EAAE,iBAAiB,CAAC,oBAAoB,CAAC;oBACnD,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAA;gBAEF,WAAW,CAAC;oBACX,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW;oBACpC,UAAU,EAAE,iBAAiB,CAAC,sBAAsB,CAAC;oBACrD,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAA;YACH,CAAC;SACD;KACD,CAAA;AACF,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -35,12 +35,40 @@ export interface PushSubscriptionRecord {
|
|
|
35
35
|
* VAPID credentials are NOT included. The Queue consumer Worker reads them
|
|
36
36
|
* from its own env bindings (Cloudflare Secrets). This keeps the private key
|
|
37
37
|
* in exactly one place and avoids serializing it into every Queue message.
|
|
38
|
+
*
|
|
39
|
+
* `siteId` identifies which site enqueued the message. The realm consumer
|
|
40
|
+
* uses it with `siteConfigLookup` to apply per-site overrides (icon URL,
|
|
41
|
+
* VAPID identity, etc.). Sites in single-tenant mode can pass any string.
|
|
38
42
|
*/
|
|
39
43
|
export interface PushQueueMessage {
|
|
44
|
+
siteId: string;
|
|
40
45
|
subscriptions: PushSubscriptionRecord[];
|
|
41
46
|
payload: PushPayload;
|
|
42
47
|
options: PushSendOptions;
|
|
43
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Per-site config override applied at consumer time. Returned from
|
|
51
|
+
* SitePushConfigLookup. All fields are optional — only the ones present
|
|
52
|
+
* will override the consumer context for that message.
|
|
53
|
+
*/
|
|
54
|
+
export interface SitePushConfig {
|
|
55
|
+
defaults?: {
|
|
56
|
+
icon?: string;
|
|
57
|
+
ttl?: number;
|
|
58
|
+
urgency?: 'very-low' | 'low' | 'normal' | 'high';
|
|
59
|
+
};
|
|
60
|
+
vapid?: {
|
|
61
|
+
subject?: string;
|
|
62
|
+
publicKey?: string;
|
|
63
|
+
privateKey?: string;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Per-message lookup. The realm consumer calls it with the message's
|
|
68
|
+
* `siteId`. Returns the per-site config to apply, or `null` to skip the
|
|
69
|
+
* override and use the consumer's context as-is.
|
|
70
|
+
*/
|
|
71
|
+
export type SitePushConfigLookup = (siteId: string, env: Record<string, unknown>) => Promise<SitePushConfig | null>;
|
|
44
72
|
/** Result of a single push send attempt */
|
|
45
73
|
export interface PushSendResult {
|
|
46
74
|
subscriptionId: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE;QACR,KAAK,EAAE,MAAM,CAAA;QACb,GAAG,EAAE,MAAM,CAAA;KACX,CAAA;IACD,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,wCAAwC;AACxC,MAAM,WAAW,eAAe;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;CAChD;AAED,sEAAsE;AACtE,MAAM,WAAW,sBAAsB;IACtC,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { emitPushAnalyticsEvent, trackPushCleanup, trackPushEvent, trackPushSend, trackPushSubscription, } from './analytics.js';
|
|
2
2
|
export { cleanupStaleSubscriptions } from './cleanup.js';
|
|
3
3
|
export { composePushPayload, type PushTemplate, type TemplateVariables, templates, } from './composer.js';
|
|
4
|
-
export { enqueuePushBatch, enqueuePushBatchAll
|
|
4
|
+
export { enqueuePushBatch, enqueuePushBatchAll } from './queue.js';
|
|
5
5
|
export { handleScheduledPush, type ScheduledContentProvider, type ScheduledPushContext, type ScheduledPushResult, } from './scheduled.js';
|
|
6
6
|
export { type SendContext, sendPushBatch, sendPushNotification, } from './send.js';
|
|
7
7
|
export { countSubscriptions, deleteSubscriptionByEndpoint, deleteSubscriptionById, findSubscriptionByEndpoint, getAllSubscriptions, incrementFailureCount, markDelivered, saveSubscription, } from './subscriptions.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,qBAAqB,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EACN,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,SAAS,GACT,MAAM,eAAe,CAAA;AACtB,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,qBAAqB,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EACN,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,SAAS,GACT,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAClE,OAAO,EACN,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,GACxB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACN,KAAK,WAAW,EAChB,aAAa,EACb,oBAAoB,GACpB,MAAM,WAAW,CAAA;AAClB,OAAO,EACN,kBAAkB,EAClB,4BAA4B,EAC5B,sBAAsB,EACtB,0BAA0B,EAC1B,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACb,gBAAgB,GAChB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/utils/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { emitPushAnalyticsEvent, trackPushCleanup, trackPushEvent, trackPushSend, trackPushSubscription, } from './analytics.js';
|
|
2
2
|
export { cleanupStaleSubscriptions } from './cleanup.js';
|
|
3
3
|
export { composePushPayload, templates, } from './composer.js';
|
|
4
|
-
export { enqueuePushBatch, enqueuePushBatchAll
|
|
4
|
+
export { enqueuePushBatch, enqueuePushBatchAll } from './queue.js';
|
|
5
5
|
export { handleScheduledPush, } from './scheduled.js';
|
|
6
6
|
export { sendPushBatch, sendPushNotification, } from './send.js';
|
|
7
7
|
export { countSubscriptions, deleteSubscriptionByEndpoint, deleteSubscriptionById, findSubscriptionByEndpoint, getAllSubscriptions, incrementFailureCount, markDelivered, saveSubscription, } from './subscriptions.js';
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,qBAAqB,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EACN,kBAAkB,EAGlB,SAAS,GACT,MAAM,eAAe,CAAA;AACtB,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,qBAAqB,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EACN,kBAAkB,EAGlB,SAAS,GACT,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAClE,OAAO,EACN,mBAAmB,GAInB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAEN,aAAa,EACb,oBAAoB,GACpB,MAAM,WAAW,CAAA;AAClB,OAAO,EACN,kBAAkB,EAClB,4BAA4B,EAC5B,sBAAsB,EACtB,0BAA0B,EAC1B,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACb,gBAAgB,GAChB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/utils/queue.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import type { PushPayload, PushQueueMessage, PushSendOptions, PushSubscriptionRecord } from '../types.js';
|
|
1
|
+
import type { PushPayload, PushQueueMessage, PushSendOptions, PushSubscriptionRecord, SitePushConfigLookup } from '../types.js';
|
|
2
2
|
interface EnqueueContext {
|
|
3
3
|
queue: Queue;
|
|
4
|
+
siteId: string;
|
|
4
5
|
batchSize?: number;
|
|
5
6
|
}
|
|
6
7
|
/**
|
|
7
8
|
* Split a known set of subscriptions into batches and enqueue each batch.
|
|
9
|
+
* Each emitted message carries the caller's `siteId` so the realm consumer
|
|
10
|
+
* can apply per-site overrides via `siteConfigLookup`.
|
|
8
11
|
*/
|
|
9
12
|
export declare function enqueuePushBatch(subscriptions: PushSubscriptionRecord[], payload: PushPayload, context: EnqueueContext, options?: PushSendOptions): Promise<{
|
|
10
13
|
batches: number;
|
|
@@ -30,10 +33,23 @@ interface QueueConsumerContext {
|
|
|
30
33
|
ttl: number;
|
|
31
34
|
urgency: 'very-low' | 'low' | 'normal' | 'high';
|
|
32
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Optional realm-level multi-tenant lookup. When provided, the consumer
|
|
38
|
+
* calls it per message using `message.body.siteId` and merges the result
|
|
39
|
+
* over the base context for that batch.
|
|
40
|
+
*/
|
|
41
|
+
siteConfigLookup?: SitePushConfigLookup;
|
|
42
|
+
/**
|
|
43
|
+
* Worker env, forwarded to `siteConfigLookup` so the lookup can read
|
|
44
|
+
* KV / D1 / etc. Required when `siteConfigLookup` is set.
|
|
45
|
+
*/
|
|
46
|
+
env?: Record<string, unknown>;
|
|
33
47
|
}
|
|
34
48
|
/**
|
|
35
49
|
* Queue consumer handler. Process a batch of push notification sends.
|
|
36
|
-
* VAPID credentials come from
|
|
50
|
+
* VAPID credentials come from context — they are NOT in the Queue message.
|
|
51
|
+
* When `context.siteConfigLookup` is configured, per-message overrides are
|
|
52
|
+
* applied for VAPID identity and notification defaults.
|
|
37
53
|
*/
|
|
38
54
|
export declare function handlePushQueue(batch: MessageBatch<PushQueueMessage>, context: QueueConsumerContext): Promise<void>;
|
|
39
55
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/utils/queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,sBAAsB,
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/utils/queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EAEtB,oBAAoB,EACpB,MAAM,aAAa,CAAA;AASpB,UAAU,cAAc;IACvB,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACrC,aAAa,EAAE,sBAAsB,EAAE,EACvC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,eAAoB,GAC3B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,CAAC,CAoB1D;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACxC,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,eAAoB,GAC3B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB1D;AAED,UAAU,oBAAoB;IAC7B,EAAE,EAAE,UAAU,CAAA;IACd,KAAK,EAAE;QACN,OAAO,EAAE,MAAM,CAAA;QACf,SAAS,EAAE,MAAM,CAAA;QACjB,UAAU,EAAE,MAAM,CAAA;KAClB,CAAA;IACD,QAAQ,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,GAAG,EAAE,MAAM,CAAA;QACX,OAAO,EAAE,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;KAC/C,CAAA;IACD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,oBAAoB,CAAA;IACvC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B;AAqBD;;;;;GAKG;AACH,wBAAsB,eAAe,CACpC,KAAK,EAAE,YAAY,CAAC,gBAAgB,CAAC,EACrC,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
|
package/dist/utils/queue.js
CHANGED
|
@@ -2,13 +2,20 @@ import { sendPushBatch } from './send.js';
|
|
|
2
2
|
import { deleteSubscriptionById, getAllSubscriptions, incrementFailureCount, markDelivered, } from './subscriptions.js';
|
|
3
3
|
/**
|
|
4
4
|
* Split a known set of subscriptions into batches and enqueue each batch.
|
|
5
|
+
* Each emitted message carries the caller's `siteId` so the realm consumer
|
|
6
|
+
* can apply per-site overrides via `siteConfigLookup`.
|
|
5
7
|
*/
|
|
6
8
|
export async function enqueuePushBatch(subscriptions, payload, context, options = {}) {
|
|
7
9
|
const batchSize = context.batchSize ?? 100;
|
|
8
10
|
let batches = 0;
|
|
9
11
|
for (let i = 0; i < subscriptions.length; i += batchSize) {
|
|
10
12
|
const batch = subscriptions.slice(i, i + batchSize);
|
|
11
|
-
const message = {
|
|
13
|
+
const message = {
|
|
14
|
+
siteId: context.siteId,
|
|
15
|
+
subscriptions: batch,
|
|
16
|
+
payload,
|
|
17
|
+
options,
|
|
18
|
+
};
|
|
12
19
|
await context.queue.send(message);
|
|
13
20
|
batches++;
|
|
14
21
|
}
|
|
@@ -30,7 +37,12 @@ export async function enqueuePushBatchAll(db, payload, context, options = {}) {
|
|
|
30
37
|
const subs = await getAllSubscriptions(db, { limit: batchSize, offset });
|
|
31
38
|
if (subs.length === 0)
|
|
32
39
|
break;
|
|
33
|
-
const message = {
|
|
40
|
+
const message = {
|
|
41
|
+
siteId: context.siteId,
|
|
42
|
+
subscriptions: subs,
|
|
43
|
+
payload,
|
|
44
|
+
options,
|
|
45
|
+
};
|
|
34
46
|
await context.queue.send(message);
|
|
35
47
|
batches++;
|
|
36
48
|
total += subs.length;
|
|
@@ -38,18 +50,40 @@ export async function enqueuePushBatchAll(db, payload, context, options = {}) {
|
|
|
38
50
|
}
|
|
39
51
|
return { batches, totalSubscriptions: total };
|
|
40
52
|
}
|
|
53
|
+
function applyOverride(context, override) {
|
|
54
|
+
if (!override)
|
|
55
|
+
return { vapid: context.vapid, defaults: context.defaults };
|
|
56
|
+
return {
|
|
57
|
+
vapid: {
|
|
58
|
+
subject: override.vapid?.subject ?? context.vapid.subject,
|
|
59
|
+
publicKey: override.vapid?.publicKey ?? context.vapid.publicKey,
|
|
60
|
+
privateKey: override.vapid?.privateKey ?? context.vapid.privateKey,
|
|
61
|
+
},
|
|
62
|
+
defaults: {
|
|
63
|
+
icon: override.defaults?.icon ?? context.defaults.icon,
|
|
64
|
+
ttl: override.defaults?.ttl ?? context.defaults.ttl,
|
|
65
|
+
urgency: override.defaults?.urgency ?? context.defaults.urgency,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
41
69
|
/**
|
|
42
70
|
* Queue consumer handler. Process a batch of push notification sends.
|
|
43
|
-
* VAPID credentials come from
|
|
71
|
+
* VAPID credentials come from context — they are NOT in the Queue message.
|
|
72
|
+
* When `context.siteConfigLookup` is configured, per-message overrides are
|
|
73
|
+
* applied for VAPID identity and notification defaults.
|
|
44
74
|
*/
|
|
45
75
|
export async function handlePushQueue(batch, context) {
|
|
46
|
-
const sendContext = {
|
|
47
|
-
vapidSubject: context.vapid.subject,
|
|
48
|
-
vapidPublicKey: context.vapid.publicKey,
|
|
49
|
-
vapidPrivateKey: context.vapid.privateKey,
|
|
50
|
-
defaults: context.defaults,
|
|
51
|
-
};
|
|
52
76
|
for (const message of batch.messages) {
|
|
77
|
+
const override = context.siteConfigLookup
|
|
78
|
+
? await context.siteConfigLookup(message.body.siteId, context.env ?? {})
|
|
79
|
+
: null;
|
|
80
|
+
const effective = applyOverride(context, override);
|
|
81
|
+
const sendContext = {
|
|
82
|
+
vapidSubject: effective.vapid.subject,
|
|
83
|
+
vapidPublicKey: effective.vapid.publicKey,
|
|
84
|
+
vapidPrivateKey: effective.vapid.privateKey,
|
|
85
|
+
defaults: effective.defaults,
|
|
86
|
+
};
|
|
53
87
|
const { subscriptions, payload, options } = message.body;
|
|
54
88
|
const result = await sendPushBatch(subscriptions, payload, sendContext, options);
|
|
55
89
|
for (const sendResult of result.results) {
|
package/dist/utils/queue.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/utils/queue.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/utils/queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduled.d.ts","sourceRoot":"","sources":["../../src/utils/scheduled.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAK/D;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACpC,EAAE,EAAE,UAAU,CAAA;IACd,KAAK,EAAE,KAAK,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE;QACR,cAAc,EAAE,MAAM,CAAA;KACtB,CAAA;CACD;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE;QACb,OAAO,EAAE,MAAM,CAAA;QACf,kBAAkB,EAAE,MAAM,CAAA;KAC1B,GAAG,IAAI,CAAA;IACR,OAAO,EAAE;QACR,OAAO,EAAE,MAAM,CAAA;KACf,CAAA;CACD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,EAAE;IAChD,EAAE,EAAE,UAAU,CAAA;CACd,KAAK,OAAO,CAAC;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,GAAG,IAAI,CAAC,CAAA;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,oBAAoB,EAC7B,eAAe,EAAE,wBAAwB,GACvC,OAAO,CAAC,mBAAmB,CAAC,CAwC9B"}
|
|
1
|
+
{"version":3,"file":"scheduled.d.ts","sourceRoot":"","sources":["../../src/utils/scheduled.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAK/D;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACpC,EAAE,EAAE,UAAU,CAAA;IACd,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE;QACR,cAAc,EAAE,MAAM,CAAA;KACtB,CAAA;CACD;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE;QACb,OAAO,EAAE,MAAM,CAAA;QACf,kBAAkB,EAAE,MAAM,CAAA;KAC1B,GAAG,IAAI,CAAA;IACR,OAAO,EAAE;QACR,OAAO,EAAE,MAAM,CAAA;KACf,CAAA;CACD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,EAAE;IAChD,EAAE,EAAE,UAAU,CAAA;CACd,KAAK,OAAO,CAAC;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,GAAG,IAAI,CAAC,CAAA;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,oBAAoB,EAC7B,eAAe,EAAE,wBAAwB,GACvC,OAAO,CAAC,mBAAmB,CAAC,CAwC9B"}
|
package/dist/utils/scheduled.js
CHANGED
|
@@ -48,7 +48,7 @@ export async function handleScheduledPush(context, contentProvider) {
|
|
|
48
48
|
const content = await contentProvider({ db: context.db });
|
|
49
49
|
// Step 2: Enqueue notifications if content is available
|
|
50
50
|
if (content) {
|
|
51
|
-
const enqueueResult = await enqueuePushBatchAll(context.db, content.payload, { queue: context.queue, batchSize: context.batchSize }, content.options ?? {});
|
|
51
|
+
const enqueueResult = await enqueuePushBatchAll(context.db, content.payload, { queue: context.queue, siteId: context.siteId, batchSize: context.batchSize }, content.options ?? {});
|
|
52
52
|
notification = {
|
|
53
53
|
batches: enqueueResult.batches,
|
|
54
54
|
totalSubscriptions: enqueueResult.totalSubscriptions,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduled.js","sourceRoot":"","sources":["../../src/utils/scheduled.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"scheduled.js","sourceRoot":"","sources":["../../src/utils/scheduled.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAgEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,OAA6B,EAC7B,eAAyC;IAEzC,IAAI,YAAY,GAAwC,IAAI,CAAA;IAE5D,2CAA2C;IAC3C,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;IAEzD,wDAAwD;IACxD,IAAI,OAAO,EAAE,CAAC;QACb,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAC9C,OAAO,CAAC,EAAE,EACV,OAAO,CAAC,OAAO,EACf,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,EAC9E,OAAO,CAAC,OAAO,IAAI,EAAE,CACrB,CAAA;QAED,YAAY,GAAG;YACd,OAAO,EAAE,aAAa,CAAC,OAAO;YAC9B,kBAAkB,EAAE,aAAa,CAAC,kBAAkB;SACpD,CAAA;QAED,0EAA0E;QAC1E,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE;YACpC,IAAI,EAAE,aAAa,CAAC,kBAAkB;YACtC,MAAM,EAAE,CAAC;YACT,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,EAAE;SACX,CAAC,CAAA;IACH,CAAC;IAED,8CAA8C;IAC9C,MAAM,aAAa,GAAG,MAAM,yBAAyB,CAAC,OAAO,CAAC,EAAE,EAAE;QACjE,cAAc,EAAE,OAAO,CAAC,OAAO,CAAC,cAAc;KAC9C,CAAC,CAAA;IAEF,OAAO;QACN,YAAY;QACZ,OAAO,EAAE;YACR,OAAO,EAAE,aAAa,CAAC,OAAO;SAC9B;KACD,CAAA;AACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growth-labs/push",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
"types": "./dist/db/schema.d.ts",
|
|
33
33
|
"import": "./dist/db/schema.js"
|
|
34
34
|
},
|
|
35
|
+
"./consumer": {
|
|
36
|
+
"types": "./dist/utils/queue.d.ts",
|
|
37
|
+
"import": "./dist/utils/queue.js"
|
|
38
|
+
},
|
|
35
39
|
"./middleware": {
|
|
36
40
|
"types": "./dist/middleware/push.d.ts",
|
|
37
41
|
"import": "./dist/middleware/push.js",
|
|
@@ -48,9 +52,11 @@
|
|
|
48
52
|
},
|
|
49
53
|
"files": [
|
|
50
54
|
"dist",
|
|
51
|
-
"src
|
|
52
|
-
"src
|
|
53
|
-
"src
|
|
55
|
+
"src/**/*.ts",
|
|
56
|
+
"src/**/*.astro",
|
|
57
|
+
"src/**/*.css",
|
|
58
|
+
"src/**/*.md",
|
|
59
|
+
"src/**/*.sql",
|
|
54
60
|
"README.md"
|
|
55
61
|
],
|
|
56
62
|
"publishConfig": {
|
|
@@ -58,16 +64,16 @@
|
|
|
58
64
|
"registry": "https://registry.npmjs.org/"
|
|
59
65
|
},
|
|
60
66
|
"peerDependencies": {
|
|
61
|
-
"astro": "^6.
|
|
67
|
+
"astro": "^6.1.10"
|
|
62
68
|
},
|
|
63
69
|
"dependencies": {
|
|
64
70
|
"@block65/webcrypto-web-push": "^1.0.0",
|
|
65
|
-
"drizzle-orm": "^0.
|
|
71
|
+
"drizzle-orm": "^0.45.2",
|
|
66
72
|
"zod": "^3.23.0"
|
|
67
73
|
},
|
|
68
74
|
"devDependencies": {
|
|
69
75
|
"@cloudflare/workers-types": "^4.0.0",
|
|
70
|
-
"astro": "^6.
|
|
76
|
+
"astro": "^6.1.10",
|
|
71
77
|
"typescript": "^5.7.0",
|
|
72
78
|
"vitest": "^3.0.0"
|
|
73
79
|
},
|
package/src/db/schema.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
|
|
2
|
+
|
|
3
|
+
export const pushSubscriptions = sqliteTable('gl_push_subscriptions', {
|
|
4
|
+
id: text('id').primaryKey(),
|
|
5
|
+
endpoint: text('endpoint').notNull().unique(),
|
|
6
|
+
keyP256dh: text('key_p256dh').notNull(),
|
|
7
|
+
keyAuth: text('key_auth').notNull(),
|
|
8
|
+
topics: text('topics'),
|
|
9
|
+
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
|
|
10
|
+
lastDeliveryAt: integer('last_delivery_at', { mode: 'timestamp' }),
|
|
11
|
+
failureCount: integer('failure_count').notNull().default(0),
|
|
12
|
+
userAgent: text('user_agent'),
|
|
13
|
+
country: text('country'),
|
|
14
|
+
})
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url'
|
|
2
|
+
import type { AstroIntegration } from 'astro'
|
|
3
|
+
import { type PushOptions, pushOptionsSchema } from './options.js'
|
|
4
|
+
|
|
5
|
+
const ENTRYPOINT_EXTENSION = import.meta.url.endsWith('.ts') ? '.ts' : '.js'
|
|
6
|
+
|
|
7
|
+
function resolveEntrypoint(path: string): string {
|
|
8
|
+
return fileURLToPath(new URL(`${path}${ENTRYPOINT_EXTENSION}`, import.meta.url))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function push(options: PushOptions): AstroIntegration {
|
|
12
|
+
const resolved = pushOptionsSchema.parse(options)
|
|
13
|
+
|
|
14
|
+
const virtualConfig = {
|
|
15
|
+
vapidPublicKey: resolved.vapidPublicKey,
|
|
16
|
+
d1Binding: resolved.d1Binding,
|
|
17
|
+
queueBinding: resolved.queueBinding,
|
|
18
|
+
defaults: resolved.defaults,
|
|
19
|
+
queue: resolved.queue,
|
|
20
|
+
turnstile: resolved.turnstile,
|
|
21
|
+
routes: resolved.routes,
|
|
22
|
+
cleanup: resolved.cleanup,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const virtualModuleId = 'virtual:growth-labs/push/config'
|
|
26
|
+
const resolvedVirtualModuleId = `\0${virtualModuleId}`
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
name: '@growth-labs/push',
|
|
30
|
+
hooks: {
|
|
31
|
+
'astro:config:setup': ({ addMiddleware, injectRoute, updateConfig }) => {
|
|
32
|
+
updateConfig({
|
|
33
|
+
vite: {
|
|
34
|
+
plugins: [
|
|
35
|
+
{
|
|
36
|
+
name: 'growth-labs-push-config',
|
|
37
|
+
resolveId(id) {
|
|
38
|
+
if (id === virtualModuleId) return resolvedVirtualModuleId
|
|
39
|
+
},
|
|
40
|
+
load(id) {
|
|
41
|
+
if (id === resolvedVirtualModuleId) {
|
|
42
|
+
return `export default ${JSON.stringify(virtualConfig)}`
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
addMiddleware({
|
|
51
|
+
entrypoint: resolveEntrypoint('./middleware/push'),
|
|
52
|
+
order: 'pre',
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
injectRoute({
|
|
56
|
+
pattern: resolved.routes.subscribe,
|
|
57
|
+
entrypoint: resolveEntrypoint('./routes/subscribe'),
|
|
58
|
+
prerender: false,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
injectRoute({
|
|
62
|
+
pattern: resolved.routes.unsubscribe,
|
|
63
|
+
entrypoint: resolveEntrypoint('./routes/unsubscribe'),
|
|
64
|
+
prerender: false,
|
|
65
|
+
})
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import config from 'virtual:growth-labs/push/config'
|
|
2
|
+
import { defineMiddleware } from 'astro/middleware'
|
|
3
|
+
|
|
4
|
+
export const onRequest = defineMiddleware(async (context, next) => {
|
|
5
|
+
;(context.locals as Record<string, unknown>).push = {
|
|
6
|
+
vapidPublicKey: config.vapidPublicKey,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return next()
|
|
10
|
+
})
|