@bluecopa/core 0.1.55 → 0.1.56
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
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RxCollection } from 'rxdb';
|
|
2
|
+
import { RxReplicationState } from 'rxdb/plugins/replication';
|
|
3
|
+
import { IWebsocketProvider } from '../config';
|
|
4
|
+
import { Row, WhereOp, CollectionRef, QueryBuilder, InputTableColumn } from './types';
|
|
5
|
+
import { DocRefImpl } from './docRef';
|
|
6
|
+
export declare class CollectionRefImpl<TRow extends Row = Row> implements CollectionRef<TRow> {
|
|
7
|
+
private collection;
|
|
8
|
+
private solutionId;
|
|
9
|
+
private tableName;
|
|
10
|
+
private primaryKeyField;
|
|
11
|
+
private pkColumn;
|
|
12
|
+
private replicationState;
|
|
13
|
+
private getWebsocketProvider;
|
|
14
|
+
constructor(collection: RxCollection, solutionId: string, tableName: string, primaryKeyField: string, pkColumn: InputTableColumn | undefined, replicationState: RxReplicationState<any, any>, getWebsocketProvider: () => IWebsocketProvider | undefined);
|
|
15
|
+
where(field: keyof TRow & string, op: WhereOp, value: unknown): QueryBuilder<TRow>;
|
|
16
|
+
orderBy(field: keyof TRow & string, direction?: 'asc' | 'desc'): QueryBuilder<TRow>;
|
|
17
|
+
limit(count: number): QueryBuilder<TRow>;
|
|
18
|
+
skip(count: number): QueryBuilder<TRow>;
|
|
19
|
+
doc(id: string): DocRefImpl<TRow>;
|
|
20
|
+
add(data: Partial<TRow>): Promise<string>;
|
|
21
|
+
subscribe(callback: (rows: TRow[]) => void): () => void;
|
|
22
|
+
get(): Promise<TRow[]>;
|
|
23
|
+
count(callback: (count: number) => void): () => void;
|
|
24
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RxCollection } from 'rxdb';
|
|
2
|
+
import { IWebsocketProvider } from '../config';
|
|
3
|
+
import { Row, DocRef } from './types';
|
|
4
|
+
export declare class DocRefImpl<TRow extends Row = Row> implements DocRef<TRow> {
|
|
5
|
+
private collection;
|
|
6
|
+
private id;
|
|
7
|
+
private solutionId;
|
|
8
|
+
private tableName;
|
|
9
|
+
private primaryKeyField;
|
|
10
|
+
private websocketProvider;
|
|
11
|
+
constructor(collection: RxCollection, id: string, solutionId: string, tableName: string, primaryKeyField: string, websocketProvider: () => IWebsocketProvider | undefined);
|
|
12
|
+
get(): Promise<TRow | null>;
|
|
13
|
+
update(data: Partial<TRow>): Promise<void>;
|
|
14
|
+
delete(): Promise<void>;
|
|
15
|
+
onSnapshot(callback: (doc: TRow | null) => void): () => void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { copaInputTableDb } from './inputTableDb';
|
|
2
|
+
export { InputTableColumnType } from './types';
|
|
3
|
+
export { InputTableError } from './utils';
|
|
4
|
+
export type { Row, WhereOp, QueryBuilder, DocRef, CollectionRef, InputTableDB, InputTableDefinition, InputTableColumn } from './types';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { RxCollection } from 'rxdb';
|
|
2
|
+
import { Row, WhereOp, QueryBuilder } from './types';
|
|
3
|
+
type WhereClause = {
|
|
4
|
+
field: string;
|
|
5
|
+
op: WhereOp;
|
|
6
|
+
value: unknown;
|
|
7
|
+
};
|
|
8
|
+
type OrderClause = {
|
|
9
|
+
field: string;
|
|
10
|
+
direction: 'asc' | 'desc';
|
|
11
|
+
};
|
|
12
|
+
export declare class QueryBuilderImpl<TRow extends Row = Row> implements QueryBuilder<TRow> {
|
|
13
|
+
private collection;
|
|
14
|
+
private wheres;
|
|
15
|
+
private orders;
|
|
16
|
+
private limitCount;
|
|
17
|
+
private skipCount;
|
|
18
|
+
constructor(collection: RxCollection, initialWhere?: WhereClause, initialOrder?: OrderClause, initialLimit?: number, initialSkip?: number);
|
|
19
|
+
where(field: keyof TRow & string, op: WhereOp, value: unknown): QueryBuilder<TRow>;
|
|
20
|
+
orderBy(field: keyof TRow & string, direction?: 'asc' | 'desc'): QueryBuilder<TRow>;
|
|
21
|
+
limit(count: number): QueryBuilder<TRow>;
|
|
22
|
+
skip(count: number): QueryBuilder<TRow>;
|
|
23
|
+
subscribe(callback: (rows: TRow[]) => void): () => void;
|
|
24
|
+
get(): Promise<TRow[]>;
|
|
25
|
+
private buildQuery;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RxReplicationState } from 'rxdb/plugins/replication';
|
|
2
|
+
import { IWebsocketProvider } from '../config';
|
|
3
|
+
export type RealtimeBindingHandle = {
|
|
4
|
+
cleanup: () => void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Bind IWebsocketProvider change notifications to RxDB replication.
|
|
8
|
+
*
|
|
9
|
+
* Backend publishes events on channel: public-input-table-v2-{workspaceId}-{solutionId}-{tableName}
|
|
10
|
+
* Events: INSERT, UPDATE, DELETE (one event per operation type)
|
|
11
|
+
* Payload: { event_name: string, event_type: "INSERT"|"UPDATE"|"DELETE", schema: string }
|
|
12
|
+
*
|
|
13
|
+
* Since the payload contains no row data, we trigger reSync() on any change
|
|
14
|
+
* to re-pull from the server.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createRealtimeBinding(provider: IWebsocketProvider, replicationState: RxReplicationState<any, any>, workspaceId: string, solutionId: string, tableName: string): RealtimeBindingHandle;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { RxCollection } from 'rxdb';
|
|
2
|
+
import { IWebsocketProvider } from '../../config';
|
|
3
|
+
import { InputTableDefinition } from '../types';
|
|
4
|
+
import { ReplicationHandle } from './replication';
|
|
5
|
+
import { RealtimeBindingHandle } from '../realtimeBinding';
|
|
6
|
+
export type ManagedCollection = {
|
|
7
|
+
collection: RxCollection;
|
|
8
|
+
replication: ReplicationHandle;
|
|
9
|
+
realtimeBinding: RealtimeBindingHandle | null;
|
|
10
|
+
tableName: string;
|
|
11
|
+
solutionId: string;
|
|
12
|
+
workspaceId: string;
|
|
13
|
+
primaryKeyField: string;
|
|
14
|
+
lastAccessed: number;
|
|
15
|
+
/** Number of active subscriptions — prevents LRU eviction while > 0 */
|
|
16
|
+
refCount: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Increment refCount for a managed collection (call when subscribing).
|
|
20
|
+
*/
|
|
21
|
+
export declare function addRef(managed: ManagedCollection): void;
|
|
22
|
+
/**
|
|
23
|
+
* Decrement refCount for a managed collection (call when unsubscribing).
|
|
24
|
+
*/
|
|
25
|
+
export declare function releaseRef(managed: ManagedCollection): void;
|
|
26
|
+
/**
|
|
27
|
+
* Get or create an RxDB collection for a table.
|
|
28
|
+
* Deduplicates concurrent creation requests. Handles LRU eviction with refcount protection.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getOrCreateCollection(solutionId: string, tableDef: InputTableDefinition, workspaceId: string, websocketProvider?: IWebsocketProvider): Promise<ManagedCollection>;
|
|
31
|
+
/**
|
|
32
|
+
* Close a specific collection and stop its replication.
|
|
33
|
+
*/
|
|
34
|
+
export declare function closeCollection(solutionId: string, tableName: string, workspaceId: string): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Close all collections (or all for a specific solutionId).
|
|
37
|
+
*/
|
|
38
|
+
export declare function closeAllCollections(solutionId?: string): Promise<void>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const CHECKPOINT_FIELD = "updated_at";
|
|
2
|
+
/** Maximum open RxDB collections (open-source limit is 16, leave headroom) */
|
|
3
|
+
export declare const MAX_COLLECTIONS = 14;
|
|
4
|
+
/** Replication retry interval (ms) */
|
|
5
|
+
export declare const REPLICATION_RETRY_TIME = 5000;
|
|
6
|
+
/** Replication pull batch size */
|
|
7
|
+
export declare const PULL_BATCH_SIZE = 100;
|
|
8
|
+
/** Replication push batch size */
|
|
9
|
+
export declare const PUSH_BATCH_SIZE = 10;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { RxReplicationState } from 'rxdb/plugins/replication';
|
|
2
|
+
import { RxCollection } from 'rxdb';
|
|
3
|
+
import { Subject } from 'rxjs';
|
|
4
|
+
type Checkpoint = {
|
|
5
|
+
id: string;
|
|
6
|
+
updatedAt: string;
|
|
7
|
+
} | null;
|
|
8
|
+
export type ReplicationHandle = {
|
|
9
|
+
state: RxReplicationState<any, Checkpoint>;
|
|
10
|
+
pullStream$: Subject<any>;
|
|
11
|
+
cancel: () => Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Set up bidirectional replication between an RxDB collection and PostgREST via BFF.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createReplication(collection: RxCollection, solutionId: string, tableName: string, primaryKeyField: string): ReplicationHandle;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RxJsonSchema } from 'rxdb';
|
|
2
|
+
import { InputTableDefinition } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Build an RxDB JSON Schema from an InputTableDefinition.
|
|
5
|
+
* Adds the checkpoint system column automatically.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildRxdbSchema(tableDef: InputTableDefinition): RxJsonSchema<any>;
|
|
8
|
+
/**
|
|
9
|
+
* Convert a table name to a valid RxDB collection name.
|
|
10
|
+
* RxDB requires: ^[a-z][a-z0-9]*$ (no underscores, no uppercase)
|
|
11
|
+
*/
|
|
12
|
+
export declare function toCollectionName(solutionId: string, tableName: string): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { IWebsocketProvider } from '../config';
|
|
2
|
+
export declare enum InputTableColumnType {
|
|
3
|
+
UUID = "uuid",
|
|
4
|
+
TEXT = "text",
|
|
5
|
+
INTEGER = "integer",
|
|
6
|
+
BIGINT = "bigint",
|
|
7
|
+
NUMERIC = "numeric",
|
|
8
|
+
BOOLEAN = "boolean",
|
|
9
|
+
TIMESTAMPTZ = "timestamptz",
|
|
10
|
+
DATE = "date",
|
|
11
|
+
TIME = "time",
|
|
12
|
+
JSONB = "jsonb"
|
|
13
|
+
}
|
|
14
|
+
export type InputTableColumn = {
|
|
15
|
+
name: string;
|
|
16
|
+
type: InputTableColumnType;
|
|
17
|
+
nullable?: boolean;
|
|
18
|
+
primary_key?: boolean;
|
|
19
|
+
auto_increment?: boolean;
|
|
20
|
+
default?: string | number | boolean | null;
|
|
21
|
+
searchable?: boolean;
|
|
22
|
+
};
|
|
23
|
+
export type InputTableDefinition = {
|
|
24
|
+
name: string;
|
|
25
|
+
columns: InputTableColumn[];
|
|
26
|
+
};
|
|
27
|
+
/** A plain row object returned by the SDK. */
|
|
28
|
+
export type Row = Record<string, unknown>;
|
|
29
|
+
/** Where clause operators */
|
|
30
|
+
export type WhereOp = '==' | '!=' | '<' | '<=' | '>' | '>=' | 'in' | 'not-in';
|
|
31
|
+
export interface QueryBuilder<TRow extends Row = Row> {
|
|
32
|
+
where(field: keyof TRow & string, op: WhereOp, value: unknown): QueryBuilder<TRow>;
|
|
33
|
+
orderBy(field: keyof TRow & string, direction?: 'asc' | 'desc'): QueryBuilder<TRow>;
|
|
34
|
+
limit(count: number): QueryBuilder<TRow>;
|
|
35
|
+
skip(count: number): QueryBuilder<TRow>;
|
|
36
|
+
subscribe(callback: (rows: TRow[]) => void): () => void;
|
|
37
|
+
get(): Promise<TRow[]>;
|
|
38
|
+
}
|
|
39
|
+
export interface DocRef<TRow extends Row = Row> {
|
|
40
|
+
get(): Promise<TRow | null>;
|
|
41
|
+
update(data: Partial<TRow>): Promise<void>;
|
|
42
|
+
delete(): Promise<void>;
|
|
43
|
+
onSnapshot(callback: (doc: TRow | null) => void): () => void;
|
|
44
|
+
}
|
|
45
|
+
export interface CollectionRef<TRow extends Row = Row> {
|
|
46
|
+
where(field: keyof TRow & string, op: WhereOp, value: unknown): QueryBuilder<TRow>;
|
|
47
|
+
orderBy(field: keyof TRow & string, direction?: 'asc' | 'desc'): QueryBuilder<TRow>;
|
|
48
|
+
limit(count: number): QueryBuilder<TRow>;
|
|
49
|
+
skip(count: number): QueryBuilder<TRow>;
|
|
50
|
+
doc(id: string): DocRef<TRow>;
|
|
51
|
+
add(data: Partial<TRow>): Promise<string>;
|
|
52
|
+
subscribe(callback: (rows: TRow[]) => void): () => void;
|
|
53
|
+
get(): Promise<TRow[]>;
|
|
54
|
+
count(callback: (count: number) => void): () => void;
|
|
55
|
+
}
|
|
56
|
+
export interface InputTableDB {
|
|
57
|
+
collection<TRow extends Row = Row>(name: string): CollectionRef<TRow>;
|
|
58
|
+
setWebsocketProvider(provider: IWebsocketProvider): void;
|
|
59
|
+
/** Force-clear the schema cache. Call after schema changes. */
|
|
60
|
+
clearSchemaCache(): void;
|
|
61
|
+
destroy(): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
export type TableListItem = {
|
|
64
|
+
name: string;
|
|
65
|
+
id: string;
|
|
66
|
+
appliedSchema: InputTableDefinition | null;
|
|
67
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for the input-table-db SDK.
|
|
3
|
+
*/
|
|
4
|
+
export declare class InputTableError extends Error {
|
|
5
|
+
status: number;
|
|
6
|
+
constructor(message: string, status: number);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Validate that a string is a safe identifier (alphanumeric, underscores, hyphens).
|
|
10
|
+
* Prevents path traversal and injection via solutionId, tableName, docId.
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateIdentifier(value: string, label: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Validate a checkpoint timestamp value matches ISO format.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateCheckpointTimestamp(value: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Validate a checkpoint ID value is a safe identifier.
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateCheckpointId(value: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Strip null values from a row and coerce PK to string.
|
|
23
|
+
* Used by both replication and realtime binding.
|
|
24
|
+
*/
|
|
25
|
+
export declare function stripNullsAndCoercePk(row: Record<string, unknown>, primaryKeyField: string): Record<string, unknown>;
|
|
26
|
+
/**
|
|
27
|
+
* Deferred subscription helper — used by all Lazy* wrappers to avoid boilerplate.
|
|
28
|
+
* Resolves the async subscription source, connects the callback, and returns
|
|
29
|
+
* an unsubscribe function that cleans up even if the resolve hasn't completed yet.
|
|
30
|
+
*/
|
|
31
|
+
export declare function deferSubscription<T>(resolve: () => Promise<(callback: (value: T) => void) => () => void>, callback: (value: T) => void, fallback: T): () => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bluecopa/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.56",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.es.js",
|
|
6
6
|
"module": "dist/index.es.js",
|
|
@@ -18,12 +18,17 @@
|
|
|
18
18
|
"build": "vite build",
|
|
19
19
|
"build:umd": "vite build --config vite.umd.config.ts",
|
|
20
20
|
"dev": "vite build --watch",
|
|
21
|
-
"prepublishOnly": "npm run build"
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"test:coverage": "vitest run --coverage"
|
|
22
25
|
},
|
|
23
26
|
"devDependencies": {
|
|
27
|
+
"@vitest/coverage-v8": "^4.0.5",
|
|
24
28
|
"typescript": "5.9.2",
|
|
25
29
|
"vite": "5.4.21",
|
|
26
|
-
"vite-plugin-dts": "4.5.4"
|
|
30
|
+
"vite-plugin-dts": "4.5.4",
|
|
31
|
+
"vitest": "4.0.5"
|
|
27
32
|
},
|
|
28
33
|
"author": "Bluecopa",
|
|
29
34
|
"license": "MIT",
|
|
@@ -37,6 +42,8 @@
|
|
|
37
42
|
"axios": "1.13.5",
|
|
38
43
|
"centrifuge": "5.2.2",
|
|
39
44
|
"lodash": "4.18.1",
|
|
40
|
-
"pusher-js": "^8.3.0"
|
|
45
|
+
"pusher-js": "^8.3.0",
|
|
46
|
+
"rxdb": "^16.21.1",
|
|
47
|
+
"rxjs": "^7.5.4"
|
|
41
48
|
}
|
|
42
49
|
}
|