cic-kit-firebase-functions 0.0.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 +76 -0
- package/dist/admin.d.ts +4 -0
- package/dist/admin.js +9 -0
- package/dist/admin.js.map +1 -0
- package/dist/cicInit.d.ts +15 -0
- package/dist/cicInit.js +11 -0
- package/dist/cicInit.js.map +1 -0
- package/dist/config/env.d.ts +13 -0
- package/dist/config/env.js +23 -0
- package/dist/config/env.js.map +1 -0
- package/dist/features/senderPush/sendUserPush.d.ts +17 -0
- package/dist/features/senderPush/sendUserPush.js +77 -0
- package/dist/features/senderPush/sendUserPush.js.map +1 -0
- package/dist/features/syncPublicUser/syncPublicUser.d.ts +8 -0
- package/dist/features/syncPublicUser/syncPublicUser.js +59 -0
- package/dist/features/syncPublicUser/syncPublicUser.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# cic-kit-firebase-functions
|
|
2
|
+
|
|
3
|
+
Set di Firebase Functions riutilizzabili per progetti CIC.
|
|
4
|
+
|
|
5
|
+
**Installazione**
|
|
6
|
+
```bash
|
|
7
|
+
npm i cic-kit-firebase-functions
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
**Peer Dependencies**
|
|
11
|
+
```bash
|
|
12
|
+
npm i firebase-admin firebase-functions
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Uso Consigliato**
|
|
16
|
+
```ts
|
|
17
|
+
import { cicInit } from 'cic-kit-firebase-functions';
|
|
18
|
+
|
|
19
|
+
const { sendUserPush, syncPublicUser } = cicInit({
|
|
20
|
+
region: 'europe-west1',
|
|
21
|
+
https: { cors: true },
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export { sendUserPush, syncPublicUser };
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Uso Avanzato (Factory)**
|
|
28
|
+
```ts
|
|
29
|
+
import { createSendUserPush, createSyncPublicUser } from 'cic-kit-firebase-functions';
|
|
30
|
+
|
|
31
|
+
export const sendUserPush = createSendUserPush({ region: 'europe-west1', cors: true });
|
|
32
|
+
export const syncPublicUser = createSyncPublicUser('europe-west1');
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**API**
|
|
36
|
+
`cicInit(options)`
|
|
37
|
+
- `options.region`: `SupportedRegion` (default `europe-west1`)
|
|
38
|
+
- `options.https.cors`: `boolean | string | RegExp | Array<string | RegExp>` (default `true`)
|
|
39
|
+
|
|
40
|
+
`sendUserPush` (Callable)
|
|
41
|
+
- Input:
|
|
42
|
+
```ts
|
|
43
|
+
{
|
|
44
|
+
toUid: string;
|
|
45
|
+
title: string;
|
|
46
|
+
options?: WebpushNotification;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
- Behavior: legge `users/{toUid}`, usa `fcmTokens`, invia WebPush con `title` + `options`, ripulisce token invalidi.
|
|
50
|
+
- Return:
|
|
51
|
+
```ts
|
|
52
|
+
{ sent: number; total: number; cleaned: number }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`syncPublicUser` (Firestore trigger)
|
|
56
|
+
- Trigger: `users/{userId}`
|
|
57
|
+
- Copia i campi indicati in `publicKey` dentro `users_public/{userId}`
|
|
58
|
+
- Se `birthHideYear === true` maschera l'anno della data di nascita
|
|
59
|
+
|
|
60
|
+
**Dati Attesi in Firestore**
|
|
61
|
+
`users/{userId}`
|
|
62
|
+
- `fcmTokens`: `string[]`
|
|
63
|
+
- `publicKey`: `string[]` (lista campi da esporre)
|
|
64
|
+
- `birthHideYear`: `boolean` (opzionale)
|
|
65
|
+
- `birthDate`: `string` (formato `dd/mm/yyyy`, opzionale)
|
|
66
|
+
- `name`, `surname`, `createdAt`, `updatedAt` (opzionali)
|
|
67
|
+
|
|
68
|
+
**Build**
|
|
69
|
+
```bash
|
|
70
|
+
npm run build
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Note**
|
|
74
|
+
- Questo pacchetto esporta funzioni già pronte. Se usi `cicInit`, chiamala prima di esportare le function.
|
|
75
|
+
- L'opzione `options.data.url` in `sendUserPush` viene usata come link webpush (fallback `/`).
|
|
76
|
+
- Il pacchetto e' ESM: usare `import`, non `require`.
|
package/dist/admin.d.ts
ADDED
package/dist/admin.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// functions/src/libs/admin.ts
|
|
2
|
+
import { getApps, initializeApp } from 'firebase-admin/app';
|
|
3
|
+
import { getFirestore } from 'firebase-admin/firestore';
|
|
4
|
+
import { getMessaging } from 'firebase-admin/messaging';
|
|
5
|
+
const _app = getApps();
|
|
6
|
+
export const app = (_app.length && _app?.[0]) ? _app[0] : initializeApp();
|
|
7
|
+
export const db = getFirestore(app);
|
|
8
|
+
export const messaging = getMessaging(app);
|
|
9
|
+
//# sourceMappingURL=admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,OAAO,EAAE,OAAO,EAAE,aAAa,EAAY,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,MAAM,IAAI,GAAU,OAAO,EAAE,CAAC;AAC9B,MAAM,CAAC,MAAM,GAAG,GAAQ,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;AAC/E,MAAM,CAAC,MAAM,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,SAAS,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CicInitOptions } from './config/env.js';
|
|
2
|
+
export declare function cicInit(options?: CicInitOptions): {
|
|
3
|
+
readonly sendUserPush: import("firebase-functions/v2/https").CallableFunction<{
|
|
4
|
+
toUid: string;
|
|
5
|
+
title: string;
|
|
6
|
+
options?: import("firebase-admin/messaging").WebpushNotification;
|
|
7
|
+
}, Promise<{
|
|
8
|
+
sent: number;
|
|
9
|
+
total: number;
|
|
10
|
+
cleaned: number;
|
|
11
|
+
}>>;
|
|
12
|
+
readonly syncPublicUser: import("firebase-functions/v2").CloudFunction<import("firebase-functions/v2/firestore").FirestoreEvent<import("firebase-functions").Change<import("firebase-functions/v2/firestore").DocumentSnapshot> | undefined, {
|
|
13
|
+
userId: string;
|
|
14
|
+
}>>;
|
|
15
|
+
};
|
package/dist/cicInit.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { setConfig } from './config/env.js';
|
|
2
|
+
import { createSendUserPush } from './features/senderPush/sendUserPush.js';
|
|
3
|
+
import { createSyncPublicUser } from './features/syncPublicUser/syncPublicUser.js';
|
|
4
|
+
export function cicInit(options = {}) {
|
|
5
|
+
setConfig(options);
|
|
6
|
+
return {
|
|
7
|
+
sendUserPush: createSendUserPush(),
|
|
8
|
+
syncPublicUser: createSyncPublicUser(),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=cicInit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cicInit.js","sourceRoot":"","sources":["../src/cicInit.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,6CAA6C,CAAC;AAEnF,MAAM,UAAU,OAAO,CAAC,UAA0B,EAAE;IAClD,SAAS,CAAC,OAAO,CAAC,CAAC;IAEnB,OAAO;QACL,YAAY,EAAE,kBAAkB,EAAE;QAClC,cAAc,EAAE,oBAAoB,EAAE;KAC9B,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SupportedRegion } from 'firebase-functions/v2/options';
|
|
2
|
+
export type CicInitOptions = {
|
|
3
|
+
region?: SupportedRegion;
|
|
4
|
+
https?: {
|
|
5
|
+
cors?: boolean;
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
export declare function setConfig(options?: CicInitOptions): void;
|
|
9
|
+
export declare function getRegion(): SupportedRegion;
|
|
10
|
+
export declare function getHttpsDefaults(): {
|
|
11
|
+
readonly region: SupportedRegion;
|
|
12
|
+
readonly cors: boolean;
|
|
13
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const defaultConfig = {
|
|
2
|
+
region: 'europe-west1',
|
|
3
|
+
https: {
|
|
4
|
+
cors: true,
|
|
5
|
+
},
|
|
6
|
+
};
|
|
7
|
+
let config = {
|
|
8
|
+
region: defaultConfig.region,
|
|
9
|
+
https: { ...defaultConfig.https },
|
|
10
|
+
};
|
|
11
|
+
export function setConfig(options = {}) {
|
|
12
|
+
if (options.region)
|
|
13
|
+
config.region = options.region;
|
|
14
|
+
if (options.https?.cors !== undefined)
|
|
15
|
+
config.https.cors = options.https.cors;
|
|
16
|
+
}
|
|
17
|
+
export function getRegion() {
|
|
18
|
+
return config.region;
|
|
19
|
+
}
|
|
20
|
+
export function getHttpsDefaults() {
|
|
21
|
+
return { region: config.region, cors: config.https.cors };
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"AAiBA,MAAM,aAAa,GAAc;IAC/B,MAAM,EAAE,cAAc;IACtB,KAAK,EAAE;QACL,IAAI,EAAE,IAAI;KACX;CACF,CAAC;AAEF,IAAI,MAAM,GAAc;IACtB,MAAM,EAAE,aAAa,CAAC,MAAM;IAC5B,KAAK,EAAE,EAAE,GAAG,aAAa,CAAC,KAAK,EAAE;CAClC,CAAC;AAEF,MAAM,UAAU,SAAS,CAAC,UAA0B,EAAE;IACpD,IAAI,OAAO,CAAC,MAAM;QAAE,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IACnD,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,KAAK,SAAS;QAAE,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,EAAW,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { WebpushNotification } from 'firebase-admin/messaging';
|
|
2
|
+
export declare const MAX_FCM_CHUNK = 500;
|
|
3
|
+
export declare function chunk<T>(arr: T[], n?: number): T[][];
|
|
4
|
+
type SendUserPushInput = {
|
|
5
|
+
toUid: string;
|
|
6
|
+
title: string;
|
|
7
|
+
options?: WebpushNotification;
|
|
8
|
+
};
|
|
9
|
+
export declare function createSendUserPush(httpsDefaults?: {
|
|
10
|
+
readonly region: import("firebase-functions/v2").SupportedRegion;
|
|
11
|
+
readonly cors: boolean;
|
|
12
|
+
}): import("firebase-functions/v2/https").CallableFunction<SendUserPushInput, Promise<{
|
|
13
|
+
sent: number;
|
|
14
|
+
total: number;
|
|
15
|
+
cleaned: number;
|
|
16
|
+
}>>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// functions/src/features/push/sendUserPush.ts
|
|
2
|
+
import { onCall, HttpsError } from 'firebase-functions/v2/https';
|
|
3
|
+
import { logger } from 'firebase-functions';
|
|
4
|
+
import { db, messaging } from '../../admin.js';
|
|
5
|
+
import { getHttpsDefaults } from '../../config/env.js';
|
|
6
|
+
export const MAX_FCM_CHUNK = 500;
|
|
7
|
+
export function chunk(arr, n = MAX_FCM_CHUNK) {
|
|
8
|
+
return arr.reduce((acc, _, i) => (i % n ? acc : [...acc, arr.slice(i, i + n)]), []);
|
|
9
|
+
}
|
|
10
|
+
async function handleSendUserPush(request) {
|
|
11
|
+
if (!request.auth) {
|
|
12
|
+
throw new HttpsError('unauthenticated', 'Login richiesto.');
|
|
13
|
+
}
|
|
14
|
+
const { toUid, title, options = {} } = (request.data ?? {});
|
|
15
|
+
if (!toUid || !title) {
|
|
16
|
+
throw new HttpsError('invalid-argument', 'toUid e title sono obbligatori');
|
|
17
|
+
}
|
|
18
|
+
const userDoc = await db.collection('users').doc(toUid).get();
|
|
19
|
+
if (!userDoc.exists) {
|
|
20
|
+
console.warn('sendUserPush: user non trovato', { toUid });
|
|
21
|
+
logger.warn('sendUserPush: user non trovato', { toUid });
|
|
22
|
+
return { sent: 0, total: 0, cleaned: 0 };
|
|
23
|
+
}
|
|
24
|
+
const fcmTokens = Array.isArray(userDoc.get('fcmTokens'))
|
|
25
|
+
? userDoc.get('fcmTokens').filter(Boolean)
|
|
26
|
+
: [];
|
|
27
|
+
if (!fcmTokens.length) {
|
|
28
|
+
return { sent: 0, total: 0, cleaned: 0 };
|
|
29
|
+
}
|
|
30
|
+
const { silent, ...ops } = options;
|
|
31
|
+
const webpush = {
|
|
32
|
+
notification: {
|
|
33
|
+
title,
|
|
34
|
+
...(silent === null ? {} : { silent }),
|
|
35
|
+
...ops
|
|
36
|
+
},
|
|
37
|
+
fcmOptions: {
|
|
38
|
+
link: options?.data?.url ?? '/'
|
|
39
|
+
},
|
|
40
|
+
headers: {
|
|
41
|
+
TTL: '3600',
|
|
42
|
+
Urgency: 'normal',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
let sent = 0;
|
|
46
|
+
let cleaned = 0;
|
|
47
|
+
let stillValid = new Set(fcmTokens);
|
|
48
|
+
for (const part of chunk(fcmTokens)) {
|
|
49
|
+
const res = await messaging.sendEachForMulticast({
|
|
50
|
+
tokens: part,
|
|
51
|
+
notification: { title, body: options?.body ?? 'undefined' },
|
|
52
|
+
webpush,
|
|
53
|
+
});
|
|
54
|
+
sent += res.responses.filter(r => r.success).length;
|
|
55
|
+
res.responses.forEach((r, i) => {
|
|
56
|
+
if (!r.success) {
|
|
57
|
+
const code = r.error?.code || '';
|
|
58
|
+
const token = part[i];
|
|
59
|
+
if ((code.includes('registration-token-not-registered') ||
|
|
60
|
+
code.includes('invalid-argument')) && token) {
|
|
61
|
+
if (stillValid.delete(token))
|
|
62
|
+
cleaned++;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (cleaned > 0) {
|
|
68
|
+
await db.collection('users').doc(toUid).update({
|
|
69
|
+
fcmTokens: Array.from(stillValid),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return { sent, total: fcmTokens.length, cleaned };
|
|
73
|
+
}
|
|
74
|
+
export function createSendUserPush(httpsDefaults = getHttpsDefaults()) {
|
|
75
|
+
return onCall(httpsDefaults, handleSendUserPush);
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=sendUserPush.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sendUserPush.js","sourceRoot":"","sources":["../../../src/features/senderPush/sendUserPush.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type DocumentSnapshot, type FirestoreEvent } from 'firebase-functions/v2/firestore';
|
|
2
|
+
import type { Change } from 'firebase-functions/v2';
|
|
3
|
+
export declare function createSyncPublicUser(region?: import("firebase-functions/v2").SupportedRegion): import("firebase-functions/v2").CloudFunction<FirestoreEvent<Change<DocumentSnapshot> | undefined, {
|
|
4
|
+
userId: string;
|
|
5
|
+
}>>;
|
|
6
|
+
export declare const syncPublicUser: import("firebase-functions/v2").CloudFunction<FirestoreEvent<Change<DocumentSnapshot> | undefined, {
|
|
7
|
+
userId: string;
|
|
8
|
+
}>>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { onDocumentWritten } from 'firebase-functions/v2/firestore';
|
|
2
|
+
import { Timestamp } from 'firebase-admin/firestore';
|
|
3
|
+
import { db } from '../../admin.js';
|
|
4
|
+
import { getRegion } from '../../config/env.js';
|
|
5
|
+
function pickPublicFields(user, publicKeys) {
|
|
6
|
+
const out = {};
|
|
7
|
+
for (const key of publicKeys) {
|
|
8
|
+
if (typeof key !== 'string')
|
|
9
|
+
continue;
|
|
10
|
+
if (key in user)
|
|
11
|
+
out[key] = user[key];
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
function maskBirthDate(date) {
|
|
16
|
+
const match = /^(\d{2}\/\d{2})\/\d{4}$/.exec(date);
|
|
17
|
+
return (match && match[1]) ? match[1] : date;
|
|
18
|
+
}
|
|
19
|
+
// users -> users_public sync
|
|
20
|
+
async function handleSyncPublicUser(event) {
|
|
21
|
+
const id = event.params.userId;
|
|
22
|
+
const beforeExists = event.data?.before.exists ?? false;
|
|
23
|
+
const afterExists = event.data?.after.exists ?? false;
|
|
24
|
+
const publicRef = db.doc(`users_public/${id}`);
|
|
25
|
+
// Se lo user viene eliminato, elimina anche la versione pubblica
|
|
26
|
+
if (beforeExists && !afterExists) {
|
|
27
|
+
await publicRef.delete();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Se non esiste after, non fare nulla (caso strano ma safe)
|
|
31
|
+
if (!afterExists)
|
|
32
|
+
return;
|
|
33
|
+
const user = event.data.after.data();
|
|
34
|
+
// publicKey: lista campi da esporre
|
|
35
|
+
const publicKeys = Array.isArray(user.publicKey) ? user.publicKey : [];
|
|
36
|
+
const picked = pickPublicFields(user, publicKeys);
|
|
37
|
+
if (user.birthHideYear === true && typeof picked.birthDate === 'string') {
|
|
38
|
+
picked.birthDate = maskBirthDate(picked.birthDate);
|
|
39
|
+
}
|
|
40
|
+
// opzionale: metadati utili
|
|
41
|
+
const updatedAt = user.updatedAt ?? Timestamp.now();
|
|
42
|
+
const createdAt = user.createdAt ?? updatedAt;
|
|
43
|
+
const name = typeof user.name === 'string' ? user.name : '';
|
|
44
|
+
const surname = typeof user.surname === 'string' ? user.surname : '';
|
|
45
|
+
// Set "merge: false" cosi se togli una key da publicKey, sparisce anche dal doc pubblico
|
|
46
|
+
await publicRef.set({
|
|
47
|
+
...picked,
|
|
48
|
+
id,
|
|
49
|
+
name,
|
|
50
|
+
surname,
|
|
51
|
+
createdAt,
|
|
52
|
+
updatedAt,
|
|
53
|
+
}, { merge: false });
|
|
54
|
+
}
|
|
55
|
+
export function createSyncPublicUser(region = getRegion()) {
|
|
56
|
+
return onDocumentWritten({ region, document: 'users/{userId}' }, handleSyncPublicUser);
|
|
57
|
+
}
|
|
58
|
+
export const syncPublicUser = createSyncPublicUser();
|
|
59
|
+
//# sourceMappingURL=syncPublicUser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncPublicUser.js","sourceRoot":"","sources":["../../../src/features/syncPublicUser/syncPublicUser.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { cicInit } from './cicInit.js';
|
|
2
|
+
export { getHttpsDefaults } from './config/env.js';
|
|
3
|
+
export { app, db, messaging } from './admin.js';
|
|
4
|
+
// export { createSendUserPush } from './features/senderPush/sendUserPush.js';
|
|
5
|
+
// export { createSyncPublicUser } from './features/syncPublicUser/syncPublicUser.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEhD,8EAA8E;AAC9E,sFAAsF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cic-kit-firebase-functions",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Firebase Functions riutilizzabili per progetti CIC.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"package.json"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"pack": "npm run build && npm pack"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"firebase-admin": "^12.0.0",
|
|
26
|
+
"firebase-functions": "^5.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"typescript": "^5.9.0"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"firebase",
|
|
33
|
+
"firebase-functions",
|
|
34
|
+
"firebase-admin",
|
|
35
|
+
"cloud-functions",
|
|
36
|
+
"cic"
|
|
37
|
+
],
|
|
38
|
+
"author": "",
|
|
39
|
+
"license": "ISC",
|
|
40
|
+
"type": "module",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"typescript": "^5.9.3"
|
|
43
|
+
}
|
|
44
|
+
}
|