@bluecopa/core 0.1.55 → 0.1.57
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 +136 -0
- package/dist/config.d.ts +4 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.es.js +856 -4
- package/dist/index.es.js.map +1 -1
- package/dist/input-table-db/collectionRef.d.ts +24 -0
- package/dist/input-table-db/docRef.d.ts +16 -0
- package/dist/input-table-db/index.d.ts +4 -0
- package/dist/input-table-db/inputTableDb.d.ts +2 -0
- package/dist/input-table-db/queryBuilder.d.ts +27 -0
- package/dist/input-table-db/realtimeBinding.d.ts +16 -0
- package/dist/input-table-db/rxdb/collectionManager.d.ts +38 -0
- package/dist/input-table-db/rxdb/constants.d.ts +9 -0
- package/dist/input-table-db/rxdb/database.d.ts +3 -0
- package/dist/input-table-db/rxdb/replication.d.ts +17 -0
- package/dist/input-table-db/rxdb/schemaBuilder.d.ts +12 -0
- package/dist/input-table-db/types.d.ts +67 -0
- package/dist/input-table-db/utils.d.ts +31 -0
- package/package.json +11 -4
package/dist/index.es.js
CHANGED
|
@@ -3,13 +3,19 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
import axios from "axios";
|
|
5
5
|
import _ from "lodash";
|
|
6
|
+
import { createRxDatabase } from "rxdb";
|
|
7
|
+
import { getRxStorageDexie } from "rxdb/plugins/storage-dexie";
|
|
8
|
+
import { replicateRxCollection } from "rxdb/plugins/replication";
|
|
9
|
+
import { Subject } from "rxjs";
|
|
6
10
|
class ConfigSingleton {
|
|
7
11
|
constructor() {
|
|
8
12
|
this.config = {
|
|
9
13
|
apiBaseUrl: "",
|
|
10
14
|
accessToken: "",
|
|
11
15
|
workspaceId: "",
|
|
12
|
-
userId: ""
|
|
16
|
+
userId: "",
|
|
17
|
+
solutionId: void 0,
|
|
18
|
+
websocketProvider: void 0
|
|
13
19
|
};
|
|
14
20
|
}
|
|
15
21
|
static getInstance() {
|
|
@@ -29,7 +35,9 @@ class ConfigSingleton {
|
|
|
29
35
|
apiBaseUrl: "",
|
|
30
36
|
accessToken: "",
|
|
31
37
|
workspaceId: "",
|
|
32
|
-
userId: ""
|
|
38
|
+
userId: "",
|
|
39
|
+
solutionId: void 0,
|
|
40
|
+
websocketProvider: void 0
|
|
33
41
|
};
|
|
34
42
|
}
|
|
35
43
|
}
|
|
@@ -60,6 +68,9 @@ const createApiClient = () => {
|
|
|
60
68
|
if (copaConfig.workspaceId && config.headers) {
|
|
61
69
|
config.headers["X-COPA-WORKSPACE-ID"] = copaConfig.workspaceId;
|
|
62
70
|
}
|
|
71
|
+
if (copaConfig.solutionId && config.headers) {
|
|
72
|
+
config.headers["x-bluecopa-solution-id"] = copaConfig.solutionId;
|
|
73
|
+
}
|
|
63
74
|
console.log("API Request Config:", {
|
|
64
75
|
baseURL: config.baseURL,
|
|
65
76
|
hasToken: !!copaConfig.accessToken,
|
|
@@ -8312,8 +8323,8 @@ var pusher = { exports: {} };
|
|
|
8312
8323
|
},
|
|
8313
8324
|
randomInt(max) {
|
|
8314
8325
|
const random = function() {
|
|
8315
|
-
const
|
|
8316
|
-
const random2 =
|
|
8326
|
+
const crypto2 = window.crypto || window["msCrypto"];
|
|
8327
|
+
const random2 = crypto2.getRandomValues(new Uint32Array(1))[0];
|
|
8317
8328
|
return random2 / Math.pow(2, 32);
|
|
8318
8329
|
};
|
|
8319
8330
|
return Math.floor(random() * max);
|
|
@@ -11843,6 +11854,844 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
|
|
|
11843
11854
|
workflow: index$m,
|
|
11844
11855
|
worksheet: index$j
|
|
11845
11856
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
11857
|
+
var InputTableColumnType = /* @__PURE__ */ ((InputTableColumnType2) => {
|
|
11858
|
+
InputTableColumnType2["UUID"] = "uuid";
|
|
11859
|
+
InputTableColumnType2["TEXT"] = "text";
|
|
11860
|
+
InputTableColumnType2["INTEGER"] = "integer";
|
|
11861
|
+
InputTableColumnType2["BIGINT"] = "bigint";
|
|
11862
|
+
InputTableColumnType2["NUMERIC"] = "numeric";
|
|
11863
|
+
InputTableColumnType2["BOOLEAN"] = "boolean";
|
|
11864
|
+
InputTableColumnType2["TIMESTAMPTZ"] = "timestamptz";
|
|
11865
|
+
InputTableColumnType2["DATE"] = "date";
|
|
11866
|
+
InputTableColumnType2["TIME"] = "time";
|
|
11867
|
+
InputTableColumnType2["JSONB"] = "jsonb";
|
|
11868
|
+
return InputTableColumnType2;
|
|
11869
|
+
})(InputTableColumnType || {});
|
|
11870
|
+
const OP_MAP = {
|
|
11871
|
+
"==": "$eq",
|
|
11872
|
+
"!=": "$ne",
|
|
11873
|
+
"<": "$lt",
|
|
11874
|
+
"<=": "$lte",
|
|
11875
|
+
">": "$gt",
|
|
11876
|
+
">=": "$gte",
|
|
11877
|
+
in: "$in",
|
|
11878
|
+
"not-in": "$nin"
|
|
11879
|
+
};
|
|
11880
|
+
class QueryBuilderImpl {
|
|
11881
|
+
constructor(collection, initialWhere, initialOrder, initialLimit, initialSkip) {
|
|
11882
|
+
this.collection = collection;
|
|
11883
|
+
this.wheres = [];
|
|
11884
|
+
this.orders = [];
|
|
11885
|
+
if (initialWhere) this.wheres.push(initialWhere);
|
|
11886
|
+
if (initialOrder) this.orders.push(initialOrder);
|
|
11887
|
+
if (initialLimit !== void 0) this.limitCount = initialLimit;
|
|
11888
|
+
if (initialSkip !== void 0) this.skipCount = initialSkip;
|
|
11889
|
+
}
|
|
11890
|
+
where(field, op, value) {
|
|
11891
|
+
this.wheres.push({ field, op, value });
|
|
11892
|
+
return this;
|
|
11893
|
+
}
|
|
11894
|
+
orderBy(field, direction = "asc") {
|
|
11895
|
+
this.orders.push({ field, direction });
|
|
11896
|
+
return this;
|
|
11897
|
+
}
|
|
11898
|
+
limit(count) {
|
|
11899
|
+
this.limitCount = count;
|
|
11900
|
+
return this;
|
|
11901
|
+
}
|
|
11902
|
+
skip(count) {
|
|
11903
|
+
this.skipCount = count;
|
|
11904
|
+
return this;
|
|
11905
|
+
}
|
|
11906
|
+
subscribe(callback) {
|
|
11907
|
+
const query = this.buildQuery();
|
|
11908
|
+
const rxSub = query.$.subscribe((docs) => {
|
|
11909
|
+
callback(docs.map((d) => d.toJSON()));
|
|
11910
|
+
});
|
|
11911
|
+
return () => rxSub.unsubscribe();
|
|
11912
|
+
}
|
|
11913
|
+
async get() {
|
|
11914
|
+
const query = this.buildQuery();
|
|
11915
|
+
const docs = await query.exec();
|
|
11916
|
+
return docs.map((d) => d.toJSON());
|
|
11917
|
+
}
|
|
11918
|
+
buildQuery() {
|
|
11919
|
+
const selector = {};
|
|
11920
|
+
for (const { field, op, value } of this.wheres) {
|
|
11921
|
+
const rxOp = OP_MAP[op];
|
|
11922
|
+
if (!selector[field]) selector[field] = {};
|
|
11923
|
+
selector[field][rxOp] = value;
|
|
11924
|
+
}
|
|
11925
|
+
const queryParams = { selector };
|
|
11926
|
+
if (this.orders.length > 0) {
|
|
11927
|
+
queryParams.sort = this.orders.map(({ field, direction }) => ({ [field]: direction }));
|
|
11928
|
+
}
|
|
11929
|
+
if (this.limitCount !== void 0) {
|
|
11930
|
+
queryParams.limit = this.limitCount;
|
|
11931
|
+
}
|
|
11932
|
+
if (this.skipCount !== void 0) {
|
|
11933
|
+
queryParams.skip = this.skipCount;
|
|
11934
|
+
}
|
|
11935
|
+
return this.collection.find(queryParams);
|
|
11936
|
+
}
|
|
11937
|
+
}
|
|
11938
|
+
const CHECKPOINT_FIELD = "updated_at";
|
|
11939
|
+
const MAX_COLLECTIONS = 14;
|
|
11940
|
+
const REPLICATION_RETRY_TIME = 5e3;
|
|
11941
|
+
const PULL_BATCH_SIZE = 100;
|
|
11942
|
+
const PUSH_BATCH_SIZE = 10;
|
|
11943
|
+
const IDENTIFIER_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
11944
|
+
const ISO_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
|
|
11945
|
+
class InputTableError extends Error {
|
|
11946
|
+
constructor(message, status) {
|
|
11947
|
+
super(message);
|
|
11948
|
+
this.status = status;
|
|
11949
|
+
this.name = "InputTableError";
|
|
11950
|
+
}
|
|
11951
|
+
}
|
|
11952
|
+
function validateIdentifier(value, label) {
|
|
11953
|
+
const trimmed = value.trim();
|
|
11954
|
+
if (!trimmed || !IDENTIFIER_PATTERN.test(trimmed)) {
|
|
11955
|
+
throw new InputTableError(`Invalid ${label}: "${value}"`, 400);
|
|
11956
|
+
}
|
|
11957
|
+
return trimmed;
|
|
11958
|
+
}
|
|
11959
|
+
function validateCheckpointTimestamp(value) {
|
|
11960
|
+
if (!ISO_TIMESTAMP_PATTERN.test(value)) {
|
|
11961
|
+
throw new InputTableError(`Invalid checkpoint timestamp: "${value}"`, 400);
|
|
11962
|
+
}
|
|
11963
|
+
return value;
|
|
11964
|
+
}
|
|
11965
|
+
function validateCheckpointId(value) {
|
|
11966
|
+
return validateIdentifier(value, "checkpoint id");
|
|
11967
|
+
}
|
|
11968
|
+
function stripNullsAndCoercePk(row, primaryKeyField) {
|
|
11969
|
+
const doc = {};
|
|
11970
|
+
for (const [key, value] of Object.entries(row)) {
|
|
11971
|
+
if (value === null) continue;
|
|
11972
|
+
doc[key] = value;
|
|
11973
|
+
}
|
|
11974
|
+
doc[primaryKeyField] = String(row[primaryKeyField]);
|
|
11975
|
+
return doc;
|
|
11976
|
+
}
|
|
11977
|
+
function deferSubscription(resolve, callback, fallback) {
|
|
11978
|
+
let innerUnsub = null;
|
|
11979
|
+
let cancelled = false;
|
|
11980
|
+
resolve().then((subscribeFn) => {
|
|
11981
|
+
if (!cancelled) {
|
|
11982
|
+
innerUnsub = subscribeFn(callback);
|
|
11983
|
+
}
|
|
11984
|
+
}).catch((err) => {
|
|
11985
|
+
console.error("[copaInputTableDb]", err);
|
|
11986
|
+
if (!cancelled) callback(fallback);
|
|
11987
|
+
});
|
|
11988
|
+
return () => {
|
|
11989
|
+
cancelled = true;
|
|
11990
|
+
innerUnsub == null ? void 0 : innerUnsub();
|
|
11991
|
+
};
|
|
11992
|
+
}
|
|
11993
|
+
class DocRefImpl {
|
|
11994
|
+
constructor(collection, id, solutionId, tableName, primaryKeyField, websocketProvider) {
|
|
11995
|
+
this.collection = collection;
|
|
11996
|
+
this.id = id;
|
|
11997
|
+
this.solutionId = solutionId;
|
|
11998
|
+
this.tableName = tableName;
|
|
11999
|
+
this.primaryKeyField = primaryKeyField;
|
|
12000
|
+
this.websocketProvider = websocketProvider;
|
|
12001
|
+
}
|
|
12002
|
+
async get() {
|
|
12003
|
+
const doc = await this.collection.findOne(this.id).exec();
|
|
12004
|
+
return doc ? doc.toJSON() : null;
|
|
12005
|
+
}
|
|
12006
|
+
async update(data) {
|
|
12007
|
+
const doc = await this.collection.findOne(this.id).exec();
|
|
12008
|
+
if (!doc) {
|
|
12009
|
+
throw new InputTableError(`Document "${this.id}" not found`, 404);
|
|
12010
|
+
}
|
|
12011
|
+
const patch = { ...data };
|
|
12012
|
+
delete patch[this.primaryKeyField];
|
|
12013
|
+
delete patch[CHECKPOINT_FIELD];
|
|
12014
|
+
await doc.patch(patch);
|
|
12015
|
+
}
|
|
12016
|
+
async delete() {
|
|
12017
|
+
await apiClient.request({
|
|
12018
|
+
url: `input-table-v2/${this.solutionId}/rows/${this.tableName}`,
|
|
12019
|
+
method: "DELETE",
|
|
12020
|
+
params: { [this.primaryKeyField]: `eq.${this.id}` }
|
|
12021
|
+
});
|
|
12022
|
+
const doc = await this.collection.findOne(this.id).exec();
|
|
12023
|
+
if (doc) await doc.remove();
|
|
12024
|
+
}
|
|
12025
|
+
onSnapshot(callback) {
|
|
12026
|
+
const rxSub = this.collection.findOne(this.id).$.subscribe((doc) => {
|
|
12027
|
+
callback(doc ? doc.toJSON() : null);
|
|
12028
|
+
});
|
|
12029
|
+
return () => rxSub.unsubscribe();
|
|
12030
|
+
}
|
|
12031
|
+
}
|
|
12032
|
+
class CollectionRefImpl {
|
|
12033
|
+
constructor(collection, solutionId, tableName, primaryKeyField, pkColumn, replicationState, getWebsocketProvider) {
|
|
12034
|
+
this.collection = collection;
|
|
12035
|
+
this.solutionId = solutionId;
|
|
12036
|
+
this.tableName = tableName;
|
|
12037
|
+
this.primaryKeyField = primaryKeyField;
|
|
12038
|
+
this.pkColumn = pkColumn;
|
|
12039
|
+
this.replicationState = replicationState;
|
|
12040
|
+
this.getWebsocketProvider = getWebsocketProvider;
|
|
12041
|
+
}
|
|
12042
|
+
where(field, op, value) {
|
|
12043
|
+
return new QueryBuilderImpl(this.collection, { field, op, value });
|
|
12044
|
+
}
|
|
12045
|
+
orderBy(field, direction = "asc") {
|
|
12046
|
+
return new QueryBuilderImpl(this.collection, void 0, { field, direction });
|
|
12047
|
+
}
|
|
12048
|
+
limit(count) {
|
|
12049
|
+
return new QueryBuilderImpl(this.collection, void 0, void 0, count);
|
|
12050
|
+
}
|
|
12051
|
+
skip(count) {
|
|
12052
|
+
return new QueryBuilderImpl(this.collection, void 0, void 0, void 0, count);
|
|
12053
|
+
}
|
|
12054
|
+
doc(id) {
|
|
12055
|
+
return new DocRefImpl(
|
|
12056
|
+
this.collection,
|
|
12057
|
+
id,
|
|
12058
|
+
this.solutionId,
|
|
12059
|
+
this.tableName,
|
|
12060
|
+
this.primaryKeyField,
|
|
12061
|
+
this.getWebsocketProvider
|
|
12062
|
+
);
|
|
12063
|
+
}
|
|
12064
|
+
async add(data) {
|
|
12065
|
+
var _a, _b;
|
|
12066
|
+
const pk = this.primaryKeyField;
|
|
12067
|
+
const insertData = { ...data };
|
|
12068
|
+
const needsServerPk = ((_a = this.pkColumn) == null ? void 0 : _a.auto_increment) && this.pkColumn.type !== InputTableColumnType.UUID && !insertData[pk];
|
|
12069
|
+
if (needsServerPk) {
|
|
12070
|
+
delete insertData[pk];
|
|
12071
|
+
delete insertData[CHECKPOINT_FIELD];
|
|
12072
|
+
const response = await apiClient.post(
|
|
12073
|
+
`/input-table-v2/${this.solutionId}/rows/${this.tableName}`,
|
|
12074
|
+
insertData,
|
|
12075
|
+
{ headers: { Prefer: "return=representation" } }
|
|
12076
|
+
);
|
|
12077
|
+
this.replicationState.reSync();
|
|
12078
|
+
const serverRow = Array.isArray(response.data) ? response.data[0] : response.data;
|
|
12079
|
+
return String((serverRow == null ? void 0 : serverRow[pk]) ?? "");
|
|
12080
|
+
}
|
|
12081
|
+
if (((_b = this.pkColumn) == null ? void 0 : _b.type) === InputTableColumnType.UUID && this.pkColumn.auto_increment && !insertData[pk]) {
|
|
12082
|
+
insertData[pk] = crypto.randomUUID();
|
|
12083
|
+
}
|
|
12084
|
+
if (!insertData[pk]) {
|
|
12085
|
+
insertData[pk] = generatePushID();
|
|
12086
|
+
}
|
|
12087
|
+
insertData[pk] = String(insertData[pk]).trim();
|
|
12088
|
+
delete insertData[CHECKPOINT_FIELD];
|
|
12089
|
+
await this.collection.insert(insertData);
|
|
12090
|
+
return String(insertData[pk]);
|
|
12091
|
+
}
|
|
12092
|
+
subscribe(callback) {
|
|
12093
|
+
const rxSub = this.collection.find().$.subscribe((docs) => {
|
|
12094
|
+
callback(docs.map((d) => d.toJSON()));
|
|
12095
|
+
});
|
|
12096
|
+
return () => rxSub.unsubscribe();
|
|
12097
|
+
}
|
|
12098
|
+
async get() {
|
|
12099
|
+
const docs = await this.collection.find().exec();
|
|
12100
|
+
return docs.map((d) => d.toJSON());
|
|
12101
|
+
}
|
|
12102
|
+
count(callback) {
|
|
12103
|
+
const rxSub = this.collection.count().$.subscribe((count) => {
|
|
12104
|
+
callback(count);
|
|
12105
|
+
});
|
|
12106
|
+
return () => rxSub.unsubscribe();
|
|
12107
|
+
}
|
|
12108
|
+
}
|
|
12109
|
+
let dbPromise = null;
|
|
12110
|
+
function getInputTableDatabase() {
|
|
12111
|
+
if (typeof window === "undefined") {
|
|
12112
|
+
return Promise.reject(
|
|
12113
|
+
new Error(
|
|
12114
|
+
"[copaInputTableDb] RxDB requires a browser environment (IndexedDB). Cannot run on server."
|
|
12115
|
+
)
|
|
12116
|
+
);
|
|
12117
|
+
}
|
|
12118
|
+
if (!dbPromise) {
|
|
12119
|
+
dbPromise = createDatabase();
|
|
12120
|
+
}
|
|
12121
|
+
return dbPromise;
|
|
12122
|
+
}
|
|
12123
|
+
async function createDatabase() {
|
|
12124
|
+
const db = await createRxDatabase({
|
|
12125
|
+
name: "bluecopa_input_tables_v2",
|
|
12126
|
+
storage: getRxStorageDexie(),
|
|
12127
|
+
multiInstance: true,
|
|
12128
|
+
eventReduce: true
|
|
12129
|
+
});
|
|
12130
|
+
return db;
|
|
12131
|
+
}
|
|
12132
|
+
async function closeInputTableDatabase() {
|
|
12133
|
+
if (dbPromise) {
|
|
12134
|
+
const db = await dbPromise;
|
|
12135
|
+
await db.close();
|
|
12136
|
+
dbPromise = null;
|
|
12137
|
+
}
|
|
12138
|
+
}
|
|
12139
|
+
const PG_TO_JSON_SCHEMA = {
|
|
12140
|
+
[InputTableColumnType.UUID]: { type: "string" },
|
|
12141
|
+
[InputTableColumnType.TEXT]: { type: "string" },
|
|
12142
|
+
[InputTableColumnType.INTEGER]: { type: "integer" },
|
|
12143
|
+
[InputTableColumnType.BIGINT]: { type: "string" },
|
|
12144
|
+
[InputTableColumnType.NUMERIC]: { type: "number" },
|
|
12145
|
+
[InputTableColumnType.BOOLEAN]: { type: "boolean" },
|
|
12146
|
+
[InputTableColumnType.TIMESTAMPTZ]: { type: "string" },
|
|
12147
|
+
[InputTableColumnType.DATE]: { type: "string" },
|
|
12148
|
+
[InputTableColumnType.TIME]: { type: "string" },
|
|
12149
|
+
[InputTableColumnType.JSONB]: { type: "string" }
|
|
12150
|
+
};
|
|
12151
|
+
function buildRxdbSchema(tableDef) {
|
|
12152
|
+
const pkColumn = tableDef.columns.find((c) => c.primary_key);
|
|
12153
|
+
const primaryKey = (pkColumn == null ? void 0 : pkColumn.name) ?? "id";
|
|
12154
|
+
const properties = {};
|
|
12155
|
+
const required = [primaryKey];
|
|
12156
|
+
for (const col of tableDef.columns) {
|
|
12157
|
+
const jsonType = PG_TO_JSON_SCHEMA[col.type] ?? { type: "string" };
|
|
12158
|
+
properties[col.name] = { ...jsonType };
|
|
12159
|
+
if (col.primary_key) {
|
|
12160
|
+
properties[col.name] = { type: "string", maxLength: 200 };
|
|
12161
|
+
}
|
|
12162
|
+
if (!col.nullable && !col.primary_key) {
|
|
12163
|
+
required.push(col.name);
|
|
12164
|
+
}
|
|
12165
|
+
}
|
|
12166
|
+
properties[CHECKPOINT_FIELD] = { type: "string", maxLength: 50 };
|
|
12167
|
+
return {
|
|
12168
|
+
title: `input_table_${tableDef.name}`,
|
|
12169
|
+
version: 0,
|
|
12170
|
+
primaryKey,
|
|
12171
|
+
type: "object",
|
|
12172
|
+
properties,
|
|
12173
|
+
required,
|
|
12174
|
+
indexes: [CHECKPOINT_FIELD]
|
|
12175
|
+
};
|
|
12176
|
+
}
|
|
12177
|
+
function toCollectionName(solutionId, tableName) {
|
|
12178
|
+
const sanitized = tableName.replace(/[^a-z0-9]/g, "");
|
|
12179
|
+
const solPrefix = solutionId.replace(/[^a-z0-9]/g, "").slice(-12);
|
|
12180
|
+
return `s${solPrefix}t${sanitized}`;
|
|
12181
|
+
}
|
|
12182
|
+
function createReplication(collection, solutionId, tableName, primaryKeyField) {
|
|
12183
|
+
const pullStream$ = new Subject();
|
|
12184
|
+
const state = replicateRxCollection({
|
|
12185
|
+
collection,
|
|
12186
|
+
replicationIdentifier: `input-table-v2-${solutionId}-${tableName}`,
|
|
12187
|
+
live: true,
|
|
12188
|
+
retryTime: REPLICATION_RETRY_TIME,
|
|
12189
|
+
autoStart: true,
|
|
12190
|
+
waitForLeadership: false,
|
|
12191
|
+
pull: {
|
|
12192
|
+
async handler(checkpoint, batchSize) {
|
|
12193
|
+
const params = {
|
|
12194
|
+
order: `${CHECKPOINT_FIELD}.asc,${primaryKeyField}.asc`,
|
|
12195
|
+
limit: String(batchSize),
|
|
12196
|
+
select: "*"
|
|
12197
|
+
};
|
|
12198
|
+
if ((checkpoint == null ? void 0 : checkpoint.id) && (checkpoint == null ? void 0 : checkpoint.updatedAt)) {
|
|
12199
|
+
const updatedAt = validateCheckpointTimestamp(checkpoint.updatedAt);
|
|
12200
|
+
const id = validateCheckpointId(checkpoint.id);
|
|
12201
|
+
params.or = `(${CHECKPOINT_FIELD}.gt.${updatedAt},and(${CHECKPOINT_FIELD}.eq.${updatedAt},${primaryKeyField}.gt.${id}))`;
|
|
12202
|
+
}
|
|
12203
|
+
const response = await apiClient.get(`/input-table-v2/${solutionId}/rows/${tableName}`, {
|
|
12204
|
+
params,
|
|
12205
|
+
headers: { Prefer: "count=exact" }
|
|
12206
|
+
});
|
|
12207
|
+
const rows = response.data ?? [];
|
|
12208
|
+
const documents = rows.map((row) => stripNullsAndCoercePk(row, primaryKeyField));
|
|
12209
|
+
const lastDoc = documents[documents.length - 1];
|
|
12210
|
+
const newCheckpoint = documents.length === 0 ? checkpoint : {
|
|
12211
|
+
id: String(lastDoc[primaryKeyField]),
|
|
12212
|
+
updatedAt: String(lastDoc[CHECKPOINT_FIELD])
|
|
12213
|
+
};
|
|
12214
|
+
return { documents, checkpoint: newCheckpoint };
|
|
12215
|
+
},
|
|
12216
|
+
batchSize: PULL_BATCH_SIZE,
|
|
12217
|
+
stream$: pullStream$.asObservable()
|
|
12218
|
+
},
|
|
12219
|
+
push: {
|
|
12220
|
+
async handler(changeRows) {
|
|
12221
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
12222
|
+
const conflicts = [];
|
|
12223
|
+
for (const row of changeRows) {
|
|
12224
|
+
const doc = row.newDocumentState;
|
|
12225
|
+
try {
|
|
12226
|
+
let serverResponse;
|
|
12227
|
+
if (row.assumedMasterState == null) {
|
|
12228
|
+
const insertData = { ...doc };
|
|
12229
|
+
delete insertData[CHECKPOINT_FIELD];
|
|
12230
|
+
delete insertData._deleted;
|
|
12231
|
+
serverResponse = await apiClient.post(`/input-table-v2/${solutionId}/rows/${tableName}`, insertData, {
|
|
12232
|
+
headers: { Prefer: "return=representation" }
|
|
12233
|
+
});
|
|
12234
|
+
} else {
|
|
12235
|
+
const updateData = { ...doc };
|
|
12236
|
+
delete updateData[primaryKeyField];
|
|
12237
|
+
delete updateData[CHECKPOINT_FIELD];
|
|
12238
|
+
delete updateData._deleted;
|
|
12239
|
+
serverResponse = await apiClient.patch(`/input-table-v2/${solutionId}/rows/${tableName}`, updateData, {
|
|
12240
|
+
params: { [primaryKeyField]: `eq.${doc[primaryKeyField]}` },
|
|
12241
|
+
headers: { Prefer: "return=representation" }
|
|
12242
|
+
});
|
|
12243
|
+
}
|
|
12244
|
+
const serverRow = Array.isArray(serverResponse == null ? void 0 : serverResponse.data) ? serverResponse.data[0] : serverResponse == null ? void 0 : serverResponse.data;
|
|
12245
|
+
if (serverRow) {
|
|
12246
|
+
const cleaned = stripNullsAndCoercePk(serverRow, primaryKeyField);
|
|
12247
|
+
const localDoc = await collection.findOne(String(cleaned[primaryKeyField])).exec();
|
|
12248
|
+
if (localDoc) {
|
|
12249
|
+
const patch = { ...cleaned };
|
|
12250
|
+
delete patch[primaryKeyField];
|
|
12251
|
+
delete patch._deleted;
|
|
12252
|
+
await localDoc.patch(patch);
|
|
12253
|
+
}
|
|
12254
|
+
}
|
|
12255
|
+
} catch (err) {
|
|
12256
|
+
const status = (_a = err == null ? void 0 : err.response) == null ? void 0 : _a.status;
|
|
12257
|
+
const pgCode = ((_c = (_b = err == null ? void 0 : err.response) == null ? void 0 : _b.data) == null ? void 0 : _c.code) ?? ((_f = (_e = (_d = err == null ? void 0 : err.response) == null ? void 0 : _d.data) == null ? void 0 : _e.data) == null ? void 0 : _f.code);
|
|
12258
|
+
console.error(`[replication:push] Error (status=${status}, pgCode=${pgCode}):`, doc[primaryKeyField], (_g = err == null ? void 0 : err.response) == null ? void 0 : _g.data);
|
|
12259
|
+
const isInsert = row.assumedMasterState == null;
|
|
12260
|
+
if (status === 409 || pgCode === "23505") {
|
|
12261
|
+
try {
|
|
12262
|
+
const response = await apiClient.get(
|
|
12263
|
+
`/input-table-v2/${solutionId}/rows/${tableName}`,
|
|
12264
|
+
{
|
|
12265
|
+
params: { [primaryKeyField]: `eq.${doc[primaryKeyField]}` }
|
|
12266
|
+
}
|
|
12267
|
+
);
|
|
12268
|
+
const serverRows = response.data ?? [];
|
|
12269
|
+
if (serverRows.length > 0) {
|
|
12270
|
+
conflicts.push(stripNullsAndCoercePk(serverRows[0], primaryKeyField));
|
|
12271
|
+
} else if (isInsert) {
|
|
12272
|
+
console.error(`[replication:push] Unique violation on non-PK column, removing doc:`, doc[primaryKeyField]);
|
|
12273
|
+
const localDoc = await collection.findOne(String(doc[primaryKeyField])).exec();
|
|
12274
|
+
if (localDoc) await localDoc.remove();
|
|
12275
|
+
}
|
|
12276
|
+
} catch {
|
|
12277
|
+
}
|
|
12278
|
+
} else if (pgCode === "23503" || status === 400 || status === 404) {
|
|
12279
|
+
if (isInsert) {
|
|
12280
|
+
console.error(`[replication:push] Removing invalid doc (${pgCode ?? status}):`, doc[primaryKeyField]);
|
|
12281
|
+
try {
|
|
12282
|
+
const localDoc = await collection.findOne(String(doc[primaryKeyField])).exec();
|
|
12283
|
+
if (localDoc) await localDoc.remove();
|
|
12284
|
+
} catch {
|
|
12285
|
+
}
|
|
12286
|
+
} else {
|
|
12287
|
+
console.error(`[replication:push] Reverting doc to server state (${pgCode ?? status}):`, doc[primaryKeyField]);
|
|
12288
|
+
try {
|
|
12289
|
+
const response = await apiClient.get(
|
|
12290
|
+
`/input-table-v2/${solutionId}/rows/${tableName}`,
|
|
12291
|
+
{
|
|
12292
|
+
params: { [primaryKeyField]: `eq.${doc[primaryKeyField]}` }
|
|
12293
|
+
}
|
|
12294
|
+
);
|
|
12295
|
+
const serverRows = response.data ?? [];
|
|
12296
|
+
if (serverRows.length > 0) {
|
|
12297
|
+
conflicts.push(stripNullsAndCoercePk(serverRows[0], primaryKeyField));
|
|
12298
|
+
}
|
|
12299
|
+
} catch {
|
|
12300
|
+
}
|
|
12301
|
+
}
|
|
12302
|
+
} else {
|
|
12303
|
+
throw err;
|
|
12304
|
+
}
|
|
12305
|
+
}
|
|
12306
|
+
}
|
|
12307
|
+
return conflicts;
|
|
12308
|
+
},
|
|
12309
|
+
batchSize: PUSH_BATCH_SIZE
|
|
12310
|
+
}
|
|
12311
|
+
});
|
|
12312
|
+
return {
|
|
12313
|
+
state,
|
|
12314
|
+
pullStream$,
|
|
12315
|
+
cancel: async () => {
|
|
12316
|
+
pullStream$.complete();
|
|
12317
|
+
await state.cancel();
|
|
12318
|
+
}
|
|
12319
|
+
};
|
|
12320
|
+
}
|
|
12321
|
+
function createRealtimeBinding(provider, replicationState, workspaceId, solutionId, tableName) {
|
|
12322
|
+
const channel = `public-input-table-v2-${workspaceId}-${solutionId}-${tableName}`;
|
|
12323
|
+
function handleChangeNotification() {
|
|
12324
|
+
replicationState.reSync();
|
|
12325
|
+
}
|
|
12326
|
+
provider.bind(channel, "INSERT", handleChangeNotification);
|
|
12327
|
+
provider.bind(channel, "UPDATE", handleChangeNotification);
|
|
12328
|
+
provider.bind(channel, "DELETE", handleChangeNotification);
|
|
12329
|
+
return {
|
|
12330
|
+
cleanup: () => {
|
|
12331
|
+
provider.unbind(channel, "INSERT");
|
|
12332
|
+
provider.unbind(channel, "UPDATE");
|
|
12333
|
+
provider.unbind(channel, "DELETE");
|
|
12334
|
+
}
|
|
12335
|
+
};
|
|
12336
|
+
}
|
|
12337
|
+
const activeCollections = /* @__PURE__ */ new Map();
|
|
12338
|
+
const pendingCollections = /* @__PURE__ */ new Map();
|
|
12339
|
+
function collectionKey(workspaceId, solutionId, tableName) {
|
|
12340
|
+
return `${workspaceId}:${toCollectionName(solutionId, tableName)}`;
|
|
12341
|
+
}
|
|
12342
|
+
async function getOrCreateCollection(solutionId, tableDef, workspaceId, websocketProvider) {
|
|
12343
|
+
const key = collectionKey(workspaceId, solutionId, tableDef.name);
|
|
12344
|
+
const existing = activeCollections.get(key);
|
|
12345
|
+
if (existing) {
|
|
12346
|
+
existing.lastAccessed = Date.now();
|
|
12347
|
+
return existing;
|
|
12348
|
+
}
|
|
12349
|
+
if (pendingCollections.has(key)) {
|
|
12350
|
+
return pendingCollections.get(key);
|
|
12351
|
+
}
|
|
12352
|
+
const pending = createCollectionInternal(
|
|
12353
|
+
key,
|
|
12354
|
+
solutionId,
|
|
12355
|
+
tableDef,
|
|
12356
|
+
workspaceId,
|
|
12357
|
+
websocketProvider
|
|
12358
|
+
);
|
|
12359
|
+
pendingCollections.set(key, pending);
|
|
12360
|
+
try {
|
|
12361
|
+
return await pending;
|
|
12362
|
+
} finally {
|
|
12363
|
+
pendingCollections.delete(key);
|
|
12364
|
+
}
|
|
12365
|
+
}
|
|
12366
|
+
async function createCollectionInternal(key, solutionId, tableDef, workspaceId, websocketProvider) {
|
|
12367
|
+
var _a, _b;
|
|
12368
|
+
if (activeCollections.size >= MAX_COLLECTIONS) {
|
|
12369
|
+
await evictLeastRecentlyUsed();
|
|
12370
|
+
}
|
|
12371
|
+
const db = await getInputTableDatabase();
|
|
12372
|
+
const schema = buildRxdbSchema(tableDef);
|
|
12373
|
+
const collName = toCollectionName(solutionId, tableDef.name);
|
|
12374
|
+
const pkField = ((_a = tableDef.columns.find((c) => c.primary_key)) == null ? void 0 : _a.name) ?? "id";
|
|
12375
|
+
let collection;
|
|
12376
|
+
const collectionConfig = {
|
|
12377
|
+
schema,
|
|
12378
|
+
conflictHandler: {
|
|
12379
|
+
isEqual: (a, b) => a[CHECKPOINT_FIELD] === b[CHECKPOINT_FIELD],
|
|
12380
|
+
resolve: (i) => i.realMasterState
|
|
12381
|
+
// server wins
|
|
12382
|
+
}
|
|
12383
|
+
};
|
|
12384
|
+
const existingCollection = db[collName];
|
|
12385
|
+
if (existingCollection) {
|
|
12386
|
+
collection = existingCollection;
|
|
12387
|
+
} else {
|
|
12388
|
+
try {
|
|
12389
|
+
const collections = await db.addCollections({ [collName]: collectionConfig });
|
|
12390
|
+
collection = collections[collName];
|
|
12391
|
+
} catch (err) {
|
|
12392
|
+
if ((err == null ? void 0 : err.code) === "DB6") {
|
|
12393
|
+
const previousSchema = (_b = err == null ? void 0 : err.parameters) == null ? void 0 : _b.previousSchema;
|
|
12394
|
+
if (previousSchema) {
|
|
12395
|
+
await db.removeCollectionDoc(collName, previousSchema);
|
|
12396
|
+
}
|
|
12397
|
+
const collections = await db.addCollections({ [collName]: collectionConfig });
|
|
12398
|
+
collection = collections[collName];
|
|
12399
|
+
} else {
|
|
12400
|
+
throw err;
|
|
12401
|
+
}
|
|
12402
|
+
}
|
|
12403
|
+
}
|
|
12404
|
+
const replication = createReplication(collection, solutionId, tableDef.name, pkField);
|
|
12405
|
+
let realtimeBinding = null;
|
|
12406
|
+
if (websocketProvider) {
|
|
12407
|
+
realtimeBinding = createRealtimeBinding(
|
|
12408
|
+
websocketProvider,
|
|
12409
|
+
replication.state,
|
|
12410
|
+
workspaceId,
|
|
12411
|
+
solutionId,
|
|
12412
|
+
tableDef.name
|
|
12413
|
+
);
|
|
12414
|
+
}
|
|
12415
|
+
const managed = {
|
|
12416
|
+
collection,
|
|
12417
|
+
replication,
|
|
12418
|
+
realtimeBinding,
|
|
12419
|
+
tableName: tableDef.name,
|
|
12420
|
+
solutionId,
|
|
12421
|
+
workspaceId,
|
|
12422
|
+
primaryKeyField: pkField,
|
|
12423
|
+
lastAccessed: Date.now(),
|
|
12424
|
+
refCount: 0
|
|
12425
|
+
};
|
|
12426
|
+
activeCollections.set(key, managed);
|
|
12427
|
+
return managed;
|
|
12428
|
+
}
|
|
12429
|
+
async function closeAllCollections(solutionId) {
|
|
12430
|
+
var _a;
|
|
12431
|
+
const toClose = [...activeCollections.entries()];
|
|
12432
|
+
for (const [key, managed] of toClose) {
|
|
12433
|
+
(_a = managed.realtimeBinding) == null ? void 0 : _a.cleanup();
|
|
12434
|
+
await managed.replication.cancel();
|
|
12435
|
+
await managed.collection.close();
|
|
12436
|
+
activeCollections.delete(key);
|
|
12437
|
+
}
|
|
12438
|
+
}
|
|
12439
|
+
async function evictLeastRecentlyUsed() {
|
|
12440
|
+
var _a;
|
|
12441
|
+
let oldest = null;
|
|
12442
|
+
for (const entry of activeCollections.entries()) {
|
|
12443
|
+
if (entry[1].refCount > 0) continue;
|
|
12444
|
+
if (!oldest || entry[1].lastAccessed < oldest[1].lastAccessed) {
|
|
12445
|
+
oldest = entry;
|
|
12446
|
+
}
|
|
12447
|
+
}
|
|
12448
|
+
if (oldest) {
|
|
12449
|
+
(_a = oldest[1].realtimeBinding) == null ? void 0 : _a.cleanup();
|
|
12450
|
+
await oldest[1].replication.cancel();
|
|
12451
|
+
await oldest[1].collection.close();
|
|
12452
|
+
activeCollections.delete(oldest[0]);
|
|
12453
|
+
} else {
|
|
12454
|
+
console.warn(
|
|
12455
|
+
`[copaInputTableDb] All ${MAX_COLLECTIONS} collections have active subscribers. Cannot evict.`
|
|
12456
|
+
);
|
|
12457
|
+
}
|
|
12458
|
+
}
|
|
12459
|
+
const SOLUTION_COOKIE_NAME = "x-bluecopa-solution-id";
|
|
12460
|
+
function getSolutionId() {
|
|
12461
|
+
const config = getConfig();
|
|
12462
|
+
if (config.solutionId) {
|
|
12463
|
+
return validateIdentifier(config.solutionId, "solutionId");
|
|
12464
|
+
}
|
|
12465
|
+
try {
|
|
12466
|
+
const cookie = document.cookie.split(";").map((c) => c.trim()).find((c) => c.startsWith(`${SOLUTION_COOKIE_NAME}=`));
|
|
12467
|
+
if (cookie) {
|
|
12468
|
+
const value = decodeURIComponent(cookie.split("=").slice(1).join("="));
|
|
12469
|
+
return validateIdentifier(value, "solutionId (cookie)");
|
|
12470
|
+
}
|
|
12471
|
+
} catch {
|
|
12472
|
+
}
|
|
12473
|
+
throw new InputTableError(
|
|
12474
|
+
"[copaInputTableDb] No solutionId configured. Set via copaSetConfig({ solutionId }) or cookie.",
|
|
12475
|
+
400
|
|
12476
|
+
);
|
|
12477
|
+
}
|
|
12478
|
+
const _InputTableDBImpl = class _InputTableDBImpl {
|
|
12479
|
+
constructor() {
|
|
12480
|
+
this.schemaCache = /* @__PURE__ */ new Map();
|
|
12481
|
+
this.schemaPending = /* @__PURE__ */ new Map();
|
|
12482
|
+
this.wsWarned = false;
|
|
12483
|
+
this.generation = 0;
|
|
12484
|
+
}
|
|
12485
|
+
setWebsocketProvider(provider) {
|
|
12486
|
+
this.wsProvider = provider;
|
|
12487
|
+
}
|
|
12488
|
+
getProvider() {
|
|
12489
|
+
return this.wsProvider ?? getConfig().websocketProvider;
|
|
12490
|
+
}
|
|
12491
|
+
collection(name) {
|
|
12492
|
+
const provider = this.getProvider();
|
|
12493
|
+
if (!provider && !this.wsWarned) {
|
|
12494
|
+
this.wsWarned = true;
|
|
12495
|
+
console.warn(
|
|
12496
|
+
"[copaInputTableDb] No WebSocket provider configured. Realtime sync is disabled. Set via copaSetConfig({ websocketProvider }) or copaInputTableDb.setWebsocketProvider(ws)."
|
|
12497
|
+
);
|
|
12498
|
+
}
|
|
12499
|
+
return new LazyCollectionRef(() => this.resolveCollection(name));
|
|
12500
|
+
}
|
|
12501
|
+
/** Force-clear the schema cache. Call after schema changes. */
|
|
12502
|
+
clearSchemaCache() {
|
|
12503
|
+
this.schemaCache.clear();
|
|
12504
|
+
this.schemaPending.clear();
|
|
12505
|
+
}
|
|
12506
|
+
async resolveCollection(tableName) {
|
|
12507
|
+
const gen = this.generation;
|
|
12508
|
+
const solutionId = getSolutionId();
|
|
12509
|
+
const validatedTableName = validateIdentifier(tableName, "tableName");
|
|
12510
|
+
const workspaceId = getConfig().workspaceId;
|
|
12511
|
+
const tables = await this.fetchTableList(solutionId);
|
|
12512
|
+
if (gen !== this.generation) throw new InputTableError("InputTableDB was destroyed", 410);
|
|
12513
|
+
const tableItem = tables.find((t) => t.name === validatedTableName);
|
|
12514
|
+
if (!(tableItem == null ? void 0 : tableItem.appliedSchema)) {
|
|
12515
|
+
throw new InputTableError(`Collection "${validatedTableName}" not found`, 404);
|
|
12516
|
+
}
|
|
12517
|
+
const managed = await getOrCreateCollection(
|
|
12518
|
+
solutionId,
|
|
12519
|
+
tableItem.appliedSchema,
|
|
12520
|
+
workspaceId,
|
|
12521
|
+
this.getProvider()
|
|
12522
|
+
);
|
|
12523
|
+
if (gen !== this.generation) throw new InputTableError("InputTableDB was destroyed", 410);
|
|
12524
|
+
const pkColumn = tableItem.appliedSchema.columns.find((c) => c.primary_key);
|
|
12525
|
+
return new CollectionRefImpl(
|
|
12526
|
+
managed.collection,
|
|
12527
|
+
solutionId,
|
|
12528
|
+
validatedTableName,
|
|
12529
|
+
managed.primaryKeyField,
|
|
12530
|
+
pkColumn,
|
|
12531
|
+
managed.replication.state,
|
|
12532
|
+
() => this.getProvider()
|
|
12533
|
+
);
|
|
12534
|
+
}
|
|
12535
|
+
fetchTableList(solutionId) {
|
|
12536
|
+
const cached = this.schemaCache.get(solutionId);
|
|
12537
|
+
if (cached && Date.now() - cached.fetchedAt < _InputTableDBImpl.SCHEMA_CACHE_TTL) {
|
|
12538
|
+
return Promise.resolve(cached.tables);
|
|
12539
|
+
}
|
|
12540
|
+
if (this.schemaPending.has(solutionId)) {
|
|
12541
|
+
return this.schemaPending.get(solutionId);
|
|
12542
|
+
}
|
|
12543
|
+
const pending = apiClient.get(`/input-table-v2/${solutionId}/tables`).then((res) => {
|
|
12544
|
+
const tables = res.data ?? [];
|
|
12545
|
+
this.schemaCache.set(solutionId, { tables, fetchedAt: Date.now() });
|
|
12546
|
+
this.schemaPending.delete(solutionId);
|
|
12547
|
+
return tables;
|
|
12548
|
+
}).catch((err) => {
|
|
12549
|
+
this.schemaPending.delete(solutionId);
|
|
12550
|
+
throw err;
|
|
12551
|
+
});
|
|
12552
|
+
this.schemaPending.set(solutionId, pending);
|
|
12553
|
+
return pending;
|
|
12554
|
+
}
|
|
12555
|
+
async destroy() {
|
|
12556
|
+
this.generation++;
|
|
12557
|
+
await closeAllCollections();
|
|
12558
|
+
await closeInputTableDatabase();
|
|
12559
|
+
this.schemaCache.clear();
|
|
12560
|
+
this.schemaPending.clear();
|
|
12561
|
+
this.wsWarned = false;
|
|
12562
|
+
}
|
|
12563
|
+
};
|
|
12564
|
+
_InputTableDBImpl.SCHEMA_CACHE_TTL = 5 * 60 * 1e3;
|
|
12565
|
+
let InputTableDBImpl = _InputTableDBImpl;
|
|
12566
|
+
class LazyCollectionRef {
|
|
12567
|
+
constructor(resolve) {
|
|
12568
|
+
this.resolve = resolve;
|
|
12569
|
+
this.resolvedRef = null;
|
|
12570
|
+
}
|
|
12571
|
+
getRef() {
|
|
12572
|
+
if (!this.resolvedRef) {
|
|
12573
|
+
this.resolvedRef = this.resolve();
|
|
12574
|
+
}
|
|
12575
|
+
return this.resolvedRef;
|
|
12576
|
+
}
|
|
12577
|
+
where(field, op, value) {
|
|
12578
|
+
return new LazyQueryBuilder(
|
|
12579
|
+
() => this.getRef().then((ref) => ref.where(field, op, value))
|
|
12580
|
+
);
|
|
12581
|
+
}
|
|
12582
|
+
orderBy(field, direction = "asc") {
|
|
12583
|
+
return new LazyQueryBuilder(
|
|
12584
|
+
() => this.getRef().then((ref) => ref.orderBy(field, direction))
|
|
12585
|
+
);
|
|
12586
|
+
}
|
|
12587
|
+
limit(count) {
|
|
12588
|
+
return new LazyQueryBuilder(() => this.getRef().then((ref) => ref.limit(count)));
|
|
12589
|
+
}
|
|
12590
|
+
skip(count) {
|
|
12591
|
+
return new LazyQueryBuilder(() => this.getRef().then((ref) => ref.skip(count)));
|
|
12592
|
+
}
|
|
12593
|
+
doc(id) {
|
|
12594
|
+
return new LazyDocRef(id, () => this.getRef().then((ref) => ref.doc(id)));
|
|
12595
|
+
}
|
|
12596
|
+
async add(data) {
|
|
12597
|
+
const ref = await this.getRef();
|
|
12598
|
+
return ref.add(data);
|
|
12599
|
+
}
|
|
12600
|
+
subscribe(callback) {
|
|
12601
|
+
return deferSubscription(
|
|
12602
|
+
() => this.getRef().then((ref) => (cb) => ref.subscribe(cb)),
|
|
12603
|
+
callback,
|
|
12604
|
+
[]
|
|
12605
|
+
);
|
|
12606
|
+
}
|
|
12607
|
+
async get() {
|
|
12608
|
+
try {
|
|
12609
|
+
const ref = await this.getRef();
|
|
12610
|
+
return ref.get();
|
|
12611
|
+
} catch (err) {
|
|
12612
|
+
console.error("[copaInputTableDb]", err);
|
|
12613
|
+
return [];
|
|
12614
|
+
}
|
|
12615
|
+
}
|
|
12616
|
+
count(callback) {
|
|
12617
|
+
return deferSubscription(
|
|
12618
|
+
() => this.getRef().then((ref) => (cb) => ref.count(cb)),
|
|
12619
|
+
callback,
|
|
12620
|
+
0
|
|
12621
|
+
);
|
|
12622
|
+
}
|
|
12623
|
+
}
|
|
12624
|
+
class LazyQueryBuilder {
|
|
12625
|
+
constructor(resolveBase) {
|
|
12626
|
+
this.resolveBase = resolveBase;
|
|
12627
|
+
this.pendingOps = [];
|
|
12628
|
+
}
|
|
12629
|
+
chain(op) {
|
|
12630
|
+
this.pendingOps.push(op);
|
|
12631
|
+
return this;
|
|
12632
|
+
}
|
|
12633
|
+
where(field, op, value) {
|
|
12634
|
+
return this.chain((qb) => qb.where(field, op, value));
|
|
12635
|
+
}
|
|
12636
|
+
orderBy(field, direction = "asc") {
|
|
12637
|
+
return this.chain((qb) => qb.orderBy(field, direction));
|
|
12638
|
+
}
|
|
12639
|
+
limit(count) {
|
|
12640
|
+
return this.chain((qb) => qb.limit(count));
|
|
12641
|
+
}
|
|
12642
|
+
skip(count) {
|
|
12643
|
+
return this.chain((qb) => qb.skip(count));
|
|
12644
|
+
}
|
|
12645
|
+
async resolveWithOps() {
|
|
12646
|
+
let qb = await this.resolveBase();
|
|
12647
|
+
for (const op of this.pendingOps) {
|
|
12648
|
+
qb = op(qb);
|
|
12649
|
+
}
|
|
12650
|
+
return qb;
|
|
12651
|
+
}
|
|
12652
|
+
subscribe(callback) {
|
|
12653
|
+
return deferSubscription(
|
|
12654
|
+
() => this.resolveWithOps().then((qb) => (cb) => qb.subscribe(cb)),
|
|
12655
|
+
callback,
|
|
12656
|
+
[]
|
|
12657
|
+
);
|
|
12658
|
+
}
|
|
12659
|
+
async get() {
|
|
12660
|
+
try {
|
|
12661
|
+
const qb = await this.resolveWithOps();
|
|
12662
|
+
return qb.get();
|
|
12663
|
+
} catch (err) {
|
|
12664
|
+
console.error("[copaInputTableDb]", err);
|
|
12665
|
+
return [];
|
|
12666
|
+
}
|
|
12667
|
+
}
|
|
12668
|
+
}
|
|
12669
|
+
class LazyDocRef {
|
|
12670
|
+
constructor(id, resolve) {
|
|
12671
|
+
this.id = id;
|
|
12672
|
+
this.resolve = resolve;
|
|
12673
|
+
}
|
|
12674
|
+
async get() {
|
|
12675
|
+
const ref = await this.resolve();
|
|
12676
|
+
return ref.get();
|
|
12677
|
+
}
|
|
12678
|
+
async update(data) {
|
|
12679
|
+
const ref = await this.resolve();
|
|
12680
|
+
return ref.update(data);
|
|
12681
|
+
}
|
|
12682
|
+
async delete() {
|
|
12683
|
+
const ref = await this.resolve();
|
|
12684
|
+
return ref.delete();
|
|
12685
|
+
}
|
|
12686
|
+
onSnapshot(callback) {
|
|
12687
|
+
return deferSubscription(
|
|
12688
|
+
() => this.resolve().then((ref) => (cb) => ref.onSnapshot(cb)),
|
|
12689
|
+
callback,
|
|
12690
|
+
null
|
|
12691
|
+
);
|
|
12692
|
+
}
|
|
12693
|
+
}
|
|
12694
|
+
const copaInputTableDb = new InputTableDBImpl();
|
|
11846
12695
|
const bluecopaTailwindConfig = {
|
|
11847
12696
|
darkMode: "class",
|
|
11848
12697
|
important: true,
|
|
@@ -12001,8 +12850,11 @@ const bluecopaTailwindConfig = {
|
|
|
12001
12850
|
}
|
|
12002
12851
|
};
|
|
12003
12852
|
export {
|
|
12853
|
+
InputTableColumnType,
|
|
12854
|
+
InputTableError,
|
|
12004
12855
|
index as copaApi,
|
|
12005
12856
|
getConfig as copaGetConfig,
|
|
12857
|
+
copaInputTableDb,
|
|
12006
12858
|
setConfig as copaSetConfig,
|
|
12007
12859
|
bluecopaTailwindConfig as copaTailwindConfig,
|
|
12008
12860
|
index$i as copaUtils
|