@forestadmin/datasource-replica 1.0.0 → 1.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.
Files changed (44) hide show
  1. package/dist/cache-interface/collection.d.ts +54 -0
  2. package/dist/cache-interface/collection.js +89 -0
  3. package/dist/cache-interface/datasource.d.ts +14 -0
  4. package/dist/cache-interface/datasource.js +33 -0
  5. package/dist/decorators/schema/collection.d.ts +8 -0
  6. package/dist/decorators/schema/collection.js +35 -0
  7. package/dist/decorators/schema/data-source.d.ts +9 -0
  8. package/dist/decorators/schema/data-source.js +18 -0
  9. package/dist/decorators/sync/collection.d.ts +15 -0
  10. package/dist/decorators/sync/collection.js +107 -0
  11. package/dist/decorators/sync/data-source.d.ts +9 -0
  12. package/dist/decorators/sync/data-source.js +15 -0
  13. package/dist/decorators/write/collection.d.ts +11 -0
  14. package/dist/decorators/write/collection.js +54 -0
  15. package/dist/decorators/write/data-source.d.ts +8 -0
  16. package/dist/decorators/write/data-source.js +23 -0
  17. package/dist/flattener.d.ts +3 -0
  18. package/dist/flattener.js +59 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/options/flattener/options.d.ts +5 -0
  21. package/dist/options/flattener/options.js +103 -0
  22. package/dist/options/flattener/schema.d.ts +8 -0
  23. package/dist/options/flattener/schema.js +39 -0
  24. package/dist/options/index.d.ts +5 -0
  25. package/dist/options/index.js +37 -0
  26. package/dist/options/schema/analyzer.d.ts +11 -0
  27. package/dist/options/schema/analyzer.js +53 -0
  28. package/dist/options/schema/index.d.ts +6 -0
  29. package/dist/options/schema/index.js +43 -0
  30. package/dist/options/utils.d.ts +3 -0
  31. package/dist/options/utils.js +8 -0
  32. package/dist/sequelize.d.ts +6 -0
  33. package/dist/sequelize.js +122 -0
  34. package/dist/synchronization/analysis-passthrough.d.ts +20 -0
  35. package/dist/synchronization/analysis-passthrough.js +79 -0
  36. package/dist/synchronization/cache-target.d.ts +15 -0
  37. package/dist/synchronization/cache-target.js +85 -0
  38. package/dist/synchronization/customer-source.d.ts +31 -0
  39. package/dist/synchronization/customer-source.js +188 -0
  40. package/dist/types.d.ts +176 -0
  41. package/dist/types.js +11 -0
  42. package/dist/utils.d.ts +7 -0
  43. package/dist/utils.js +35 -0
  44. package/package.json +3 -3
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const sequelize_1 = require("sequelize");
7
+ const flattener_1 = __importDefault(require("../flattener"));
8
+ const utils_1 = require("../utils");
9
+ class CacheTarget {
10
+ constructor(connection, options, logger) {
11
+ this.connection = connection;
12
+ this.options = options;
13
+ this.logger = logger;
14
+ }
15
+ async applyDump(changes, firstPage) {
16
+ if (firstPage) {
17
+ // Truncate tables
18
+ for (const collection of this.options.flattenSchema) {
19
+ await this.connection.model(collection.name).truncate();
20
+ }
21
+ }
22
+ const recordsByCollection = {};
23
+ const entries = changes.entries.flatMap(entry => this.flattenRecord(entry));
24
+ for (const entry of entries) {
25
+ if (entry !== undefined) {
26
+ if (!recordsByCollection[entry.collection])
27
+ recordsByCollection[entry.collection] = [];
28
+ recordsByCollection[entry.collection].push(entry.record);
29
+ }
30
+ }
31
+ await this.connection.transaction(async (transaction) => {
32
+ for (const [collection, records] of Object.entries(recordsByCollection)) {
33
+ await this.connection.model(collection).bulkCreate(records, { transaction });
34
+ }
35
+ });
36
+ }
37
+ async applyDelta(changes) {
38
+ await this.connection.transaction(async (transaction) => {
39
+ for (const entry of changes.deletedEntries) {
40
+ if (this.checkCollection(entry.collection)) {
41
+ await this.destroySubModels(entry, transaction);
42
+ await this.connection
43
+ .model(entry.collection)
44
+ .destroy({ where: entry.record, transaction });
45
+ }
46
+ }
47
+ // Upsert records in both root and virtual collections
48
+ for (const entry of changes.newOrUpdatedEntries) {
49
+ if (this.checkCollection(entry.collection)) {
50
+ await this.destroySubModels(entry, transaction);
51
+ for (const subEntry of this.flattenRecord(entry))
52
+ await this.connection
53
+ .model(subEntry.collection)
54
+ .upsert(subEntry.record, { transaction });
55
+ }
56
+ }
57
+ });
58
+ }
59
+ flattenRecord(recordWithCollection) {
60
+ if (this.checkCollection(recordWithCollection.collection))
61
+ return (0, flattener_1.default)(recordWithCollection, this.options.schema.find(s => s.name === recordWithCollection.collection).fields, this.options?.flattenOptions?.[recordWithCollection.collection]?.asFields ?? [], this.options?.flattenOptions?.[recordWithCollection.collection]?.asModels ?? []);
62
+ }
63
+ async destroySubModels(entry, transaction) {
64
+ const { asModels } = this.options.flattenOptions[entry.collection];
65
+ const { fields } = this.options.schema.find(c => c.name === entry.collection);
66
+ const promises = asModels.map(asModel => {
67
+ const recordId = (0, utils_1.getRecordId)(fields, entry.record);
68
+ return this.connection.model((0, utils_1.escape) `${entry.collection}.${asModel}`).destroy({
69
+ where: { _fid: { [sequelize_1.Op.startsWith]: `${recordId}.${asModel}` } },
70
+ transaction,
71
+ });
72
+ });
73
+ await Promise.all(promises);
74
+ }
75
+ checkCollection(collection) {
76
+ const collectionName = this.options.schema.find(s => s.name === collection);
77
+ if (!collectionName) {
78
+ this.logger('Error', `Collection '${collection}' not found in schema. Skipping.`);
79
+ return false;
80
+ }
81
+ return true;
82
+ }
83
+ }
84
+ exports.default = CacheTarget;
85
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FjaGUtdGFyZ2V0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N5bmNocm9uaXphdGlvbi9jYWNoZS10YXJnZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFXQSx5Q0FBNEM7QUFFNUMsNkRBQXlDO0FBQ3pDLG9DQUErQztBQUUvQyxNQUFxQixXQUFXO0lBSzlCLFlBQVksVUFBcUIsRUFBRSxPQUF3QixFQUFFLE1BQWM7UUFDekUsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7UUFDN0IsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUVELEtBQUssQ0FBQyxTQUFTLENBQUMsT0FBeUIsRUFBRSxTQUFrQjtRQUMzRCxJQUFJLFNBQVMsRUFBRTtZQUNiLGtCQUFrQjtZQUNsQixLQUFLLE1BQU0sVUFBVSxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsYUFBYSxFQUFFO2dCQUNuRCxNQUFNLElBQUksQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQzthQUN6RDtTQUNGO1FBRUQsTUFBTSxtQkFBbUIsR0FBRyxFQUFrQyxDQUFDO1FBQy9ELE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBRTVFLEtBQUssTUFBTSxLQUFLLElBQUksT0FBTyxFQUFFO1lBQzNCLElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRTtnQkFDdkIsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUM7b0JBQUUsbUJBQW1CLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQztnQkFDdkYsbUJBQW1C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
@@ -0,0 +1,31 @@
1
+ import type CacheDataSourceInterface from '../cache-interface/datasource';
2
+ import type { PullDeltaReason, PullDumpReason, ReplicaDataSourceOptions, SynchronizationSource, SynchronizationTarget } from '../types';
3
+ import type { Logger } from '@forestadmin/datasource-toolkit';
4
+ import type { Sequelize } from 'sequelize';
5
+ export default class CustomerSource implements SynchronizationSource {
6
+ requestCache: CacheDataSourceInterface;
7
+ private readonly logger;
8
+ private options;
9
+ private connection;
10
+ private isRunning;
11
+ private queuedPullDumpReasons;
12
+ private queuedPullDeltaReasons;
13
+ private queuedPushDeltaRequest;
14
+ private schedules;
15
+ private target;
16
+ constructor(connection: Sequelize, options: ReplicaDataSourceOptions, logger: Logger);
17
+ start(target: SynchronizationTarget): Promise<void>;
18
+ stop(): Promise<void>;
19
+ private queuePushDelta;
20
+ queuePullDump(reason: PullDumpReason): Promise<void>;
21
+ queuePullDelta(reason: PullDeltaReason): Promise<void>;
22
+ private tick;
23
+ private runPushDelta;
24
+ private runPullDump;
25
+ private runPullDelta;
26
+ private getStartupState;
27
+ private setStartupState;
28
+ private getDeltaState;
29
+ private setDeltaState;
30
+ }
31
+ //# sourceMappingURL=customer-source.d.ts.map
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const datasource_toolkit_1 = require("@forestadmin/datasource-toolkit");
4
+ const croner_1 = require("croner");
5
+ class CustomerSource {
6
+ constructor(connection, options, logger) {
7
+ this.requestCache = null;
8
+ this.isRunning = false;
9
+ this.queuedPullDumpReasons = null;
10
+ this.queuedPullDeltaReasons = null;
11
+ this.queuedPushDeltaRequest = null;
12
+ this.schedules = [];
13
+ this.connection = connection;
14
+ this.options = options;
15
+ this.logger = logger;
16
+ const hasPullDeltaFlag = options.pullDeltaOnRestart ||
17
+ options.pullDeltaOnSchedule ||
18
+ options.pullDeltaOnAfterWrite ||
19
+ options.pullDeltaOnBeforeAccess;
20
+ if (options.pullDeltaHandler && !hasPullDeltaFlag)
21
+ throw new Error('Using pullDeltaHandler without pullDelta[*] flags');
22
+ if (hasPullDeltaFlag && !options.pullDeltaHandler)
23
+ throw new Error('Using pullDelta[*] flags without pullDeltaHandler');
24
+ }
25
+ async start(target) {
26
+ const { options } = this;
27
+ if (this.target)
28
+ throw new Error('Already started');
29
+ this.target = target;
30
+ if (options.pullDumpHandler &&
31
+ (options.pullDumpOnRestart || (await this.getStartupState()) !== 'done'))
32
+ await this.queuePullDump({ name: 'startup' });
33
+ if (options.pullDeltaHandler && options.pullDeltaOnRestart)
34
+ await this.queuePullDelta({ name: 'startup' });
35
+ if (options.pushDeltaHandler) {
36
+ options.pushDeltaHandler({ cache: this.requestCache, getPreviousDeltaState: () => this.getDeltaState() }, async (changes) => this.queuePushDelta(changes));
37
+ }
38
+ if (options.pullDumpHandler && options.pullDumpOnSchedule) {
39
+ const schedule = options.pullDumpOnSchedule;
40
+ for (const pattern of Array.isArray(schedule) ? schedule : [schedule]) {
41
+ this.schedules.push((0, croner_1.Cron)(pattern, () => this.queuePullDump({ name: 'schedule' })));
42
+ }
43
+ }
44
+ if (options.pullDeltaHandler && options.pullDeltaOnSchedule) {
45
+ const schedule = options.pullDeltaOnSchedule;
46
+ for (const pattern of Array.isArray(schedule) ? schedule : [schedule]) {
47
+ this.schedules.push((0, croner_1.Cron)(pattern, () => this.queuePullDelta({ name: 'schedule' })));
48
+ }
49
+ }
50
+ }
51
+ async stop() {
52
+ this.schedules.forEach(cron => cron.stop());
53
+ this.schedules.length = 0;
54
+ }
55
+ queuePushDelta(delta) {
56
+ if (!this.queuedPushDeltaRequest)
57
+ this.queuedPushDeltaRequest = { newOrUpdatedEntries: [], deletedEntries: [] };
58
+ this.queuedPushDeltaRequest.deletedEntries.push(...delta.deletedEntries);
59
+ this.queuedPushDeltaRequest.newOrUpdatedEntries.push(...delta.newOrUpdatedEntries);
60
+ if (delta.nextDeltaState)
61
+ this.queuedPushDeltaRequest.nextDeltaState = delta.nextDeltaState;
62
+ this.tick();
63
+ }
64
+ queuePullDump(reason) {
65
+ if (!this.queuedPullDumpReasons)
66
+ this.queuedPullDumpReasons = { deferred: new datasource_toolkit_1.Deferred(), reasons: [] };
67
+ const { deferred } = this.queuedPullDumpReasons;
68
+ this.queuedPullDumpReasons.reasons.push({ ...reason, at: new Date() });
69
+ this.tick();
70
+ return deferred.promise;
71
+ }
72
+ queuePullDelta(reason) {
73
+ if (!this.queuedPullDeltaReasons)
74
+ this.queuedPullDeltaReasons = { deferred: new datasource_toolkit_1.Deferred(), reasons: [] };
75
+ const { deferred } = this.queuedPullDeltaReasons;
76
+ this.queuedPullDeltaReasons.reasons.push({ ...reason, at: new Date() });
77
+ // We wait for the access delay before running the delta
78
+ // This allows to batch delta requests at the cost of adding a floor delay to all requests.
79
+ if (this.options.pullDeltaOnBeforeAccessDelay)
80
+ setTimeout(() => this.tick(), this.options.pullDeltaOnBeforeAccessDelay);
81
+ else
82
+ this.tick();
83
+ return deferred.promise;
84
+ }
85
+ tick() {
86
+ if (!this.isRunning) {
87
+ if (this.queuedPushDeltaRequest) {
88
+ this.runPushDelta(this.queuedPushDeltaRequest);
89
+ this.queuedPushDeltaRequest = null;
90
+ }
91
+ else if (this.queuedPullDumpReasons) {
92
+ this.runPullDump(this.queuedPullDumpReasons);
93
+ this.queuedPullDumpReasons = null;
94
+ }
95
+ else if (this.queuedPullDeltaReasons) {
96
+ this.runPullDelta(this.queuedPullDeltaReasons);
97
+ this.queuedPullDeltaReasons = null;
98
+ }
99
+ }
100
+ }
101
+ async runPushDelta(changes) {
102
+ this.isRunning = true;
103
+ await this.target.applyDelta(changes);
104
+ this.isRunning = false;
105
+ this.tick();
106
+ }
107
+ /* eslint-disable no-await-in-loop */
108
+ async runPullDump(queue) {
109
+ this.isRunning = true;
110
+ let state = null;
111
+ let more = true;
112
+ let firstPage = true;
113
+ await this.setStartupState('in_progress');
114
+ // Fill table with dump
115
+ while (more) {
116
+ try {
117
+ const changes = await this.options.pullDumpHandler({
118
+ reasons: queue.reasons,
119
+ cache: this.requestCache,
120
+ previousDumpState: state,
121
+ });
122
+ await this.target.applyDump(changes, firstPage);
123
+ firstPage = false;
124
+ more = changes.more;
125
+ if (changes.more === true)
126
+ state = changes.nextDumpState;
127
+ if (changes.more === false && changes.nextDeltaState)
128
+ await this.setDeltaState(changes.nextDeltaState);
129
+ }
130
+ catch (error) {
131
+ more = false;
132
+ this.logger('Warn', `Dump failed for the namespace "${this.options.cacheNamespace}": ${error.message}`);
133
+ }
134
+ }
135
+ await this.setStartupState('done');
136
+ this.isRunning = false;
137
+ queue.deferred.resolve();
138
+ this.tick();
139
+ }
140
+ async runPullDelta(queue) {
141
+ this.isRunning = true;
142
+ let more = true;
143
+ while (more) {
144
+ // Update records in database
145
+ const previousState = await this.getDeltaState();
146
+ const collections = queue.reasons.flatMap(r => 'affectedCollections' in r ? r.affectedCollections : []);
147
+ try {
148
+ const changes = await this.options.pullDeltaHandler({
149
+ reasons: queue.reasons,
150
+ affectedCollections: [...new Set(collections)],
151
+ cache: this.requestCache,
152
+ previousDeltaState: previousState,
153
+ });
154
+ await this.target.applyDelta(changes);
155
+ await this.setDeltaState(changes.nextDeltaState);
156
+ more = changes.more;
157
+ }
158
+ catch (error) {
159
+ more = false;
160
+ this.logger('Warn', `Delta failed for the namespace "${this.options.cacheNamespace}": ${error.message}`);
161
+ }
162
+ }
163
+ this.isRunning = false;
164
+ queue.deferred.resolve();
165
+ this.tick();
166
+ }
167
+ /* eslint-enable no-await-in-loop */
168
+ async getStartupState() {
169
+ const metadataModel = this.connection.model(`${this.options.cacheNamespace}_metadata`);
170
+ const stateRecord = await metadataModel.findByPk('statup_state');
171
+ return stateRecord ? JSON.parse(stateRecord.dataValues.content) : 'pending';
172
+ }
173
+ async setStartupState(state) {
174
+ const metadataModel = this.connection.model(`${this.options.cacheNamespace}_metadata`);
175
+ await metadataModel.upsert({ id: 'statup_state', content: JSON.stringify(state) });
176
+ }
177
+ async getDeltaState() {
178
+ const metadataModel = this.connection.model(`${this.options.cacheNamespace}_metadata`);
179
+ const stateRecord = await metadataModel.findByPk('delta_state');
180
+ return stateRecord ? JSON.parse(stateRecord.dataValues.content) : null;
181
+ }
182
+ async setDeltaState(state) {
183
+ const metadataModel = this.connection.model(`${this.options.cacheNamespace}_metadata`);
184
+ await metadataModel.upsert({ id: 'delta_state', content: JSON.stringify(state) });
185
+ }
186
+ }
187
+ exports.default = CustomerSource;
188
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItc291cmNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N5bmNocm9uaXphdGlvbi9jdXN0b21lci1zb3VyY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFZQSx3RUFBMkQ7QUFDM0QsbUNBQThCO0FBYzlCLE1BQXFCLGNBQWM7SUFnQmpDLFlBQVksVUFBcUIsRUFBRSxPQUFpQyxFQUFFLE1BQWM7UUFmcEYsaUJBQVksR0FBNkIsSUFBSSxDQUFDO1FBTXRDLGNBQVMsR0FBRyxLQUFLLENBQUM7UUFFbEIsMEJBQXFCLEdBQTBCLElBQUksQ0FBQztRQUNwRCwyQkFBc0IsR0FBMkIsSUFBSSxDQUFDO1FBQ3RELDJCQUFzQixHQUFzQixJQUFJLENBQUM7UUFFakQsY0FBUyxHQUFXLEVBQUUsQ0FBQztRQUk3QixJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQztRQUM3QixJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztRQUVyQixNQUFNLGdCQUFnQixHQUNwQixPQUFPLENBQUMsa0JBQWtCO1lBQzFCLE9BQU8sQ0FBQyxtQkFBbUI7WUFDM0IsT0FBTyxDQUFDLHFCQUFxQjtZQUM3QixPQUFPLENBQUMsdUJBQXVCLENBQUM7UUFFbEMsSUFBSSxPQUFPLENBQUMsZ0JBQWdCLElBQUksQ0FBQyxnQkFBZ0I7WUFDL0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxtREFBbUQsQ0FBQyxDQUFDO1FBRXZFLElBQUksZ0JBQWdCLElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCO1lBQy9DLE1BQU0sSUFBSSxLQUFLLENBQUMsbURBQW1ELENBQUMsQ0FBQztJQUN6RSxDQUFDO0lBRUQsS0FBSyxDQUFDLEtBQUssQ0FBQyxNQUE2QjtRQUN2QyxNQUFNLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDO1FBRXpCLElBQUksSUFBSSxDQUFDLE1BQU07WUFB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
@@ -0,0 +1,176 @@
1
+ import type CacheDataSourceInterface from './cache-interface/datasource';
2
+ import type { TFilter, TPaginatedFilter } from '@forestadmin/datasource-customizer';
3
+ import type { ConnectionOptions } from '@forestadmin/datasource-sql';
4
+ import type { Aggregation, Caller, ColumnSchema, Logger, PrimitiveTypes, Projection, RecordData } from '@forestadmin/datasource-toolkit';
5
+ export type ValueOrPromiseOrFactory<T> = T | Promise<T> | (() => T) | (() => Promise<T>);
6
+ export type RecordDataWithCollection = {
7
+ collection: string;
8
+ record: RecordData;
9
+ };
10
+ export declare function isLeafField(field: Field): field is LeafField;
11
+ export type ObjectField = {
12
+ [key: string]: Field;
13
+ };
14
+ export type ArrayField = [Field];
15
+ export type LeafField = {
16
+ type: PrimitiveTypes | 'Integer';
17
+ defaultValue?: unknown;
18
+ enumValues?: string[];
19
+ isPrimaryKey?: boolean;
20
+ isReadOnly?: boolean;
21
+ unique?: boolean;
22
+ validation?: ColumnSchema['validation'];
23
+ reference?: {
24
+ relationName: string;
25
+ targetCollection: string;
26
+ targetField: string;
27
+ relationInverse?: string;
28
+ };
29
+ };
30
+ export type Field = ArrayField | ObjectField | LeafField;
31
+ export type CollectionReplicaSchema = {
32
+ name: string;
33
+ fields: Record<string, Field>;
34
+ };
35
+ export type PullDumpReason = {
36
+ name: 'startup' | 'schedule';
37
+ };
38
+ export type PullDeltaReason = {
39
+ collection?: string;
40
+ affectedCollections?: string[];
41
+ } & ({
42
+ name: 'startup';
43
+ } | {
44
+ name: 'schedule';
45
+ } | {
46
+ name: 'before-list';
47
+ caller: Caller;
48
+ filter: TPaginatedFilter;
49
+ projection: Projection;
50
+ } | {
51
+ name: 'before-aggregate';
52
+ caller: Caller;
53
+ filter: TFilter;
54
+ aggregation: Aggregation;
55
+ } | {
56
+ name: 'after-create';
57
+ caller: Caller;
58
+ records: RecordData[];
59
+ } | {
60
+ name: 'after-update';
61
+ caller: Caller;
62
+ filter: TFilter;
63
+ patch: RecordData;
64
+ } | {
65
+ name: 'after-delete';
66
+ caller: Caller;
67
+ filter: TFilter;
68
+ });
69
+ export type PullDeltaRequest = {
70
+ previousDeltaState: unknown;
71
+ cache: CacheDataSourceInterface;
72
+ affectedCollections: string[];
73
+ reasons: Array<PullDeltaReason & {
74
+ at: Date;
75
+ }>;
76
+ };
77
+ export type PullDumpRequest = {
78
+ previousDumpState: unknown;
79
+ cache: CacheDataSourceInterface;
80
+ reasons: Array<PullDumpReason & {
81
+ at: Date;
82
+ }>;
83
+ };
84
+ export type PushDeltaRequest = {
85
+ getPreviousDeltaState(): unknown;
86
+ cache: CacheDataSourceInterface;
87
+ };
88
+ export type PullDumpResponse = {
89
+ more: true;
90
+ entries: RecordDataWithCollection[];
91
+ nextDumpState: unknown;
92
+ } | {
93
+ more: false;
94
+ entries: RecordDataWithCollection[];
95
+ nextDeltaState?: unknown;
96
+ };
97
+ export type PullDeltaResponse = {
98
+ more: boolean;
99
+ nextDeltaState: unknown;
100
+ newOrUpdatedEntries: RecordDataWithCollection[];
101
+ deletedEntries: RecordDataWithCollection[];
102
+ };
103
+ export type PushDeltaResponse = Omit<PullDeltaResponse, 'more' | 'nextDeltaState'> & {
104
+ nextDeltaState?: unknown;
105
+ };
106
+ export type FlattenOptions = {
107
+ [modelName: string]: {
108
+ asModels?: string[];
109
+ asFields?: Array<string | {
110
+ field: string;
111
+ level: number;
112
+ }>;
113
+ };
114
+ };
115
+ export type ReplicaDataSourceOptions = {
116
+ /** URL of the cache database (default to in-memory sqlite) */
117
+ cacheInto?: ConnectionOptions;
118
+ /**
119
+ * Prefix that should be used when creating the tables on the cache
120
+ * Defaults to 'forest_cache_'.
121
+ *
122
+ * Using a prefix allows to share the same database between multiple projects, environments, or
123
+ * datasources.
124
+ */
125
+ cacheNamespace?: string;
126
+ /** Schema options */
127
+ schema?: ValueOrPromiseOrFactory<CollectionReplicaSchema[]>;
128
+ flattenMode?: 'auto' | 'manual';
129
+ flattenOptions?: ValueOrPromiseOrFactory<FlattenOptions>;
130
+ /** Writing options */
131
+ createRecordHandler?: (collectionName: string, record: RecordData) => Promise<RecordData>;
132
+ updateRecordHandler?: (collectionName: string, record: RecordData) => Promise<void>;
133
+ deleteRecordHandler?: (collectionName: string, record: RecordData) => Promise<void>;
134
+ /** Push options */
135
+ pushDeltaHandler?: (request: PushDeltaRequest, onChanges: (changes: PushDeltaResponse) => Promise<void>) => void | Promise<void>;
136
+ /** Pull dump options */
137
+ pullDumpHandler?: (request: PullDumpRequest) => Promise<PullDumpResponse>;
138
+ pullDumpOnRestart?: boolean;
139
+ pullDumpOnSchedule?: string | string[];
140
+ /** Pull delta options */
141
+ pullDeltaHandler?: (request: PullDeltaRequest) => Promise<PullDeltaResponse>;
142
+ pullDeltaOnRestart?: boolean;
143
+ pullDeltaOnSchedule?: string | string[];
144
+ pullDeltaOnBeforeAccess?: boolean;
145
+ pullDeltaOnAfterWrite?: boolean;
146
+ /**
147
+ * Delay that should be waited before each cache access to give the opportunity for
148
+ * multiple requests to be batched together.
149
+ *
150
+ * Note that this delay will add latency to each request, so set it to a low value (< 100ms)
151
+ */
152
+ pullDeltaOnBeforeAccessDelay?: number;
153
+ };
154
+ export type ResolvedOptions = Pick<ReplicaDataSourceOptions, 'cacheInto' | 'cacheNamespace' | 'createRecordHandler' | 'updateRecordHandler' | 'deleteRecordHandler' | 'pullDeltaOnBeforeAccess' | 'pullDeltaOnAfterWrite'> & {
155
+ schema?: CollectionReplicaSchema[];
156
+ flattenSchema?: CollectionReplicaSchema[];
157
+ flattenOptions?: {
158
+ [modelName: string]: {
159
+ asModels?: string[];
160
+ asFields?: string[];
161
+ };
162
+ };
163
+ logger: Logger;
164
+ source: SynchronizationSource;
165
+ };
166
+ export interface SynchronizationTarget {
167
+ applyDump(changes: PullDumpResponse, firstPage: boolean): Promise<void>;
168
+ applyDelta(changes: PushDeltaResponse): Promise<void>;
169
+ }
170
+ export interface SynchronizationSource {
171
+ requestCache: CacheDataSourceInterface;
172
+ start(target: SynchronizationTarget): Promise<void>;
173
+ queuePullDump(reason: PullDumpReason): Promise<void>;
174
+ queuePullDelta(reason: PullDeltaReason): Promise<void>;
175
+ }
176
+ //# sourceMappingURL=types.d.ts.map
package/dist/types.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isLeafField = void 0;
4
+ /// //////
5
+ // Schema
6
+ /// //////
7
+ function isLeafField(field) {
8
+ return typeof field === 'object' && 'type' in field;
9
+ }
10
+ exports.isLeafField = isLeafField;
11
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBZ0JBLFVBQVU7QUFDVixTQUFTO0FBQ1QsVUFBVTtBQUVWLFNBQWdCLFdBQVcsQ0FBQyxLQUFZO0lBQ3RDLE9BQU8sT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLE1BQU0sSUFBSSxLQUFLLENBQUM7QUFDdEQsQ0FBQztBQUZELGtDQUVDIn0=
@@ -0,0 +1,7 @@
1
+ import type { CollectionReplicaSchema } from './types';
2
+ import type { RecordData } from '@forestadmin/datasource-toolkit';
3
+ export declare function escape(strings: TemplateStringsArray, ...exps: unknown[]): string;
4
+ export declare function deepclone(any: unknown): any;
5
+ export declare function extractValue(record: RecordData, path: string, remove: boolean): any;
6
+ export declare function getRecordId(fields: CollectionReplicaSchema['fields'], record: RecordData): unknown;
7
+ //# sourceMappingURL=utils.d.ts.map
package/dist/utils.js ADDED
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRecordId = exports.extractValue = exports.deepclone = exports.escape = void 0;
4
+ function escape(strings, ...exps) {
5
+ return strings.reduce((acc, str, i) => acc + str + (exps[i] ?? ''), '').replace(/\./g, '_');
6
+ }
7
+ exports.escape = escape;
8
+ function deepclone(any) {
9
+ return JSON.parse(JSON.stringify(any));
10
+ }
11
+ exports.deepclone = deepclone;
12
+ function extractValue(record, path, remove) {
13
+ if (!path)
14
+ return record;
15
+ const [prefix, suffix] = path.split(/\.(.*)/);
16
+ const value = extractValue(record?.[prefix], suffix, remove);
17
+ if (remove && record && (!suffix || !Object.keys(record?.[prefix] ?? {}).length))
18
+ delete record[prefix];
19
+ return value;
20
+ }
21
+ exports.extractValue = extractValue;
22
+ function getRecordId(fields, record) {
23
+ // eslint-disable-next-line no-underscore-dangle
24
+ if (record._fid)
25
+ return record._fid;
26
+ const pks = Object.entries(fields)
27
+ .filter(([, v]) => 'type' in v && v.isPrimaryKey)
28
+ .map(([k]) => record[k]);
29
+ if (pks.length === 0)
30
+ throw new Error('No primary key found');
31
+ else if (pks.length === 1)
32
+ return pks[0];
33
+ }
34
+ exports.getRecordId = getRecordId;
35
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBR0EsU0FBZ0IsTUFBTSxDQUFDLE9BQTZCLEVBQUUsR0FBRyxJQUFlO0lBQ3RFLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxHQUFHLEdBQUcsR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUM7QUFDOUYsQ0FBQztBQUZELHdCQUVDO0FBRUQsU0FBZ0IsU0FBUyxDQUFDLEdBQVk7SUFDcEMsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUN6QyxDQUFDO0FBRkQsOEJBRUM7QUFFRCxTQUFnQixZQUFZLENBQUMsTUFBa0IsRUFBRSxJQUFZLEVBQUUsTUFBZTtJQUM1RSxJQUFJLENBQUMsSUFBSTtRQUFFLE9BQU8sTUFBTSxDQUFDO0lBRXpCLE1BQU0sQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUM5QyxNQUFNLEtBQUssR0FBRyxZQUFZLENBQUMsTUFBTSxFQUFFLENBQUMsTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQzdELElBQUksTUFBTSxJQUFJLE1BQU0sSUFBSSxDQUFDLENBQUMsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFDOUUsT0FBTyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7SUFFeEIsT0FBTyxLQUFLLENBQUM7QUFDZixDQUFDO0FBVEQsb0NBU0M7QUFFRCxTQUFnQixXQUFXLENBQ3pCLE1BQXlDLEVBQ3pDLE1BQWtCO0lBRWxCLGdEQUFnRDtJQUNoRCxJQUFJLE1BQU0sQ0FBQyxJQUFJO1FBQUUsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDO0lBRXBDLE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDO1NBQy9CLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsWUFBWSxDQUFDO1NBQ2hELEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRTNCLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1NBQ3pELElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQUUsT0FBTyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDM0MsQ0FBQztBQWJELGtDQWFDIn0=
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forestadmin/datasource-replica",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "main": "dist/index.js",
5
5
  "license": "GPL-3.0",
6
6
  "publishConfig": {
@@ -12,8 +12,8 @@
12
12
  "directory": "packages/datasource-replica"
13
13
  },
14
14
  "files": [
15
- "dist/src/**/*.js",
16
- "dist/src/**/*.d.ts"
15
+ "dist/**/*.js",
16
+ "dist/**/*.d.ts"
17
17
  ],
18
18
  "scripts": {
19
19
  "build": "tsc",