@concavejs/core 0.0.1-alpha.7 → 0.0.1-alpha.8
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/dist/auth/auth-context.d.ts +4 -15
- package/dist/auth/auth-context.js +6 -15
- package/dist/docstore/index.d.ts +1 -0
- package/dist/docstore/index.js +1 -0
- package/dist/docstore/interface.d.ts +8 -0
- package/dist/docstore/search-interfaces.d.ts +29 -0
- package/dist/docstore/search-interfaces.js +8 -0
- package/dist/http/http-handler.js +3 -3
- package/dist/id-codec/document-id.js +4 -3
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/kernel/context-storage.d.ts +1 -0
- package/dist/kernel/context-storage.js +29 -11
- package/dist/kernel/docstore-gateway.d.ts +31 -6
- package/dist/kernel/docstore-gateway.js +105 -31
- package/dist/kernel/index.d.ts +4 -3
- package/dist/kernel/index.js +4 -3
- package/dist/kernel/missing-schema-error.d.ts +5 -0
- package/dist/kernel/missing-schema-error.js +25 -0
- package/dist/kernel/native-timers.d.ts +7 -0
- package/dist/kernel/native-timers.js +14 -0
- package/dist/kernel/schema-service.d.ts +0 -5
- package/dist/kernel/schema-service.js +1 -25
- package/dist/kernel/udf-kernel.d.ts +11 -1
- package/dist/kernel/udf-kernel.js +10 -10
- package/dist/query/query-runtime.js +6 -3
- package/dist/queryengine/cursor.js +3 -2
- package/dist/queryengine/index.d.ts +2 -2
- package/dist/queryengine/index.js +1 -1
- package/dist/ryow/uncommitted-writes.js +4 -5
- package/dist/scheduler/cron-executor.js +2 -1
- package/dist/scheduler/scheduled-function-executor.js +3 -6
- package/dist/subscriptions/subscription-manager.d.ts +22 -0
- package/dist/subscriptions/subscription-manager.js +131 -44
- package/dist/sync/protocol-handler.d.ts +5 -0
- package/dist/sync/protocol-handler.js +31 -2
- package/dist/sync/session-backpressure.js +4 -3
- package/dist/sync/session-heartbeat.js +3 -2
- package/dist/system/internal.js +6 -3
- package/dist/system/system-functions.js +1 -1
- package/dist/transactor/occ-transaction.js +9 -8
- package/dist/transactor/occ-validation.js +3 -3
- package/dist/udf/analysis/validator.js +1 -1
- package/dist/udf/executor/inline.d.ts +1 -1
- package/dist/udf/executor/inline.js +6 -2
- package/dist/udf/module-loader/call-context.d.ts +5 -6
- package/dist/udf/module-loader/call-context.js +5 -9
- package/dist/udf/module-loader/module-loader.js +26 -4
- package/dist/udf/runtime/udf-rand.js +1 -1
- package/dist/udf/runtime/udf-setup.d.ts +21 -0
- package/dist/udf/runtime/udf-setup.js +149 -53
- package/dist/utils/base64.d.ts +4 -0
- package/dist/utils/base64.js +58 -0
- package/dist/utils/crypto.d.ts +2 -0
- package/dist/utils/crypto.js +40 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/utils.d.ts +6 -1
- package/dist/utils/utils.js +6 -1
- package/package.json +5 -1
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ambient Authentication Context
|
|
2
|
+
* Ambient Authentication Context.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Benefits:
|
|
8
|
-
* - No need to pass auth through every function call
|
|
9
|
-
* - Works automatically with nested UDF calls
|
|
10
|
-
* - Simplifies function signatures
|
|
11
|
-
* - Prevents auth context from being accidentally lost
|
|
4
|
+
* Context storage automatically propagates through async call chains when
|
|
5
|
+
* AsyncLocalStorage is available (Node/Bun). In runtimes without ALS, the
|
|
6
|
+
* fallback behaves correctly for serialized execution.
|
|
12
7
|
*/
|
|
13
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
14
8
|
import type { UserIdentityAttributes } from "convex/server";
|
|
15
|
-
/**
|
|
16
|
-
* AsyncLocalStorage for authentication context.
|
|
17
|
-
* This provides automatic context propagation through async call chains.
|
|
18
|
-
*/
|
|
19
|
-
export declare const authContext: AsyncLocalStorage<UserIdentityAttributes | undefined>;
|
|
20
9
|
/**
|
|
21
10
|
* Get the current authentication context.
|
|
22
11
|
* Returns undefined if no auth context is set (unauthenticated request).
|
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ambient Authentication Context
|
|
2
|
+
* Ambient Authentication Context.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Benefits:
|
|
8
|
-
* - No need to pass auth through every function call
|
|
9
|
-
* - Works automatically with nested UDF calls
|
|
10
|
-
* - Simplifies function signatures
|
|
11
|
-
* - Prevents auth context from being accidentally lost
|
|
12
|
-
*/
|
|
13
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
14
|
-
/**
|
|
15
|
-
* AsyncLocalStorage for authentication context.
|
|
16
|
-
* This provides automatic context propagation through async call chains.
|
|
4
|
+
* Context storage automatically propagates through async call chains when
|
|
5
|
+
* AsyncLocalStorage is available (Node/Bun). In runtimes without ALS, the
|
|
6
|
+
* fallback behaves correctly for serialized execution.
|
|
17
7
|
*/
|
|
18
|
-
|
|
8
|
+
import { ContextStorage } from "../kernel/context-storage";
|
|
9
|
+
const authContext = new ContextStorage();
|
|
19
10
|
/**
|
|
20
11
|
* Get the current authentication context.
|
|
21
12
|
* Returns undefined if no auth context is set (unauthenticated request).
|
package/dist/docstore/index.d.ts
CHANGED
package/dist/docstore/index.js
CHANGED
|
@@ -129,12 +129,20 @@ export interface DocStore {
|
|
|
129
129
|
nextCursor: string | null;
|
|
130
130
|
hasMore: boolean;
|
|
131
131
|
}>;
|
|
132
|
+
/**
|
|
133
|
+
* @deprecated Prefer the `SearchCapable` interface from `./search-interfaces`.
|
|
134
|
+
* This method will be removed from `DocStore` in a future version.
|
|
135
|
+
*/
|
|
132
136
|
search(indexId: ArrayBufferHex, searchQuery: string, filters: Map<string, unknown>, options?: {
|
|
133
137
|
limit?: number;
|
|
134
138
|
}): Promise<{
|
|
135
139
|
doc: LatestDocument;
|
|
136
140
|
score: number;
|
|
137
141
|
}[]>;
|
|
142
|
+
/**
|
|
143
|
+
* @deprecated Prefer the `VectorSearchCapable` interface from `./search-interfaces`.
|
|
144
|
+
* This method will be removed from `DocStore` in a future version.
|
|
145
|
+
*/
|
|
138
146
|
vectorSearch(indexId: ArrayBufferHex, vector: number[], limit: number, filters: Map<string, string>): Promise<{
|
|
139
147
|
doc: LatestDocument;
|
|
140
148
|
score: number;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ArrayBufferHex, LatestDocument, DocStore } from "./interface";
|
|
2
|
+
/**
|
|
3
|
+
* Interface for DocStore implementations that support full-text search.
|
|
4
|
+
* Prefer checking for this capability via `isSearchCapable()` type guard
|
|
5
|
+
* rather than assuming all DocStore implementations support search.
|
|
6
|
+
*/
|
|
7
|
+
export interface SearchCapable {
|
|
8
|
+
search(indexId: ArrayBufferHex, searchQuery: string, filters: Map<string, unknown>, options?: {
|
|
9
|
+
limit?: number;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
doc: LatestDocument;
|
|
12
|
+
score: number;
|
|
13
|
+
}[]>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Interface for DocStore implementations that support vector similarity search.
|
|
17
|
+
* Prefer checking for this capability via `isVectorSearchCapable()` type guard
|
|
18
|
+
* rather than assuming all DocStore implementations support vector search.
|
|
19
|
+
*/
|
|
20
|
+
export interface VectorSearchCapable {
|
|
21
|
+
vectorSearch(indexId: ArrayBufferHex, vector: number[], limit: number, filters: Map<string, string>): Promise<{
|
|
22
|
+
doc: LatestDocument;
|
|
23
|
+
score: number;
|
|
24
|
+
}[]>;
|
|
25
|
+
}
|
|
26
|
+
/** Type guard for DocStore implementations that support full-text search */
|
|
27
|
+
export declare function isSearchCapable(store: DocStore): store is DocStore & SearchCapable;
|
|
28
|
+
/** Type guard for DocStore implementations that support vector similarity search */
|
|
29
|
+
export declare function isVectorSearchCapable(store: DocStore): store is DocStore & VectorSearchCapable;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Type guard for DocStore implementations that support full-text search */
|
|
2
|
+
export function isSearchCapable(store) {
|
|
3
|
+
return typeof store.search === "function";
|
|
4
|
+
}
|
|
5
|
+
/** Type guard for DocStore implementations that support vector similarity search */
|
|
6
|
+
export function isVectorSearchCapable(store) {
|
|
7
|
+
return typeof store.vectorSearch === "function";
|
|
8
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { loadConvexModule } from "../udf";
|
|
8
8
|
import { createClientAdapter } from "../udf/execution-adapter";
|
|
9
9
|
import { UdfKernel } from "../queryengine";
|
|
10
|
-
import { applyCors, computeCorsHeaders, handleCoreHttpApiRequest, resolveAuthContext } from "./
|
|
10
|
+
import { applyCors, computeCorsHeaders, handleCoreHttpApiRequest, resolveAuthContext } from "./api-router";
|
|
11
11
|
import { writtenTablesFromRanges } from "../utils";
|
|
12
12
|
import { AdminAuthError, identityFromToken, isAdminToken, isSystemToken, JWTValidationError, SystemAuthError, } from "../auth";
|
|
13
13
|
const VERSIONED_API_PREFIX = /^\/api\/\d+\.\d+(?:\.\d+)?(?=\/|$)/;
|
|
@@ -115,7 +115,7 @@ export class HttpHandler {
|
|
|
115
115
|
storage: this.docstore && this.blobstore
|
|
116
116
|
? {
|
|
117
117
|
store: async (blob) => {
|
|
118
|
-
const convex = new UdfKernel(this.docstore,
|
|
118
|
+
const convex = new UdfKernel({ docstore: this.docstore, storage: this.blobstore });
|
|
119
119
|
const storageId = await convex.jsSyscall("storage/storeBlob", { blob });
|
|
120
120
|
const writtenRanges = convex.getTrackedWriteRanges();
|
|
121
121
|
return {
|
|
@@ -125,7 +125,7 @@ export class HttpHandler {
|
|
|
125
125
|
};
|
|
126
126
|
},
|
|
127
127
|
get: async (storageId) => {
|
|
128
|
-
const convex = new UdfKernel(this.docstore,
|
|
128
|
+
const convex = new UdfKernel({ docstore: this.docstore, storage: this.blobstore });
|
|
129
129
|
const blob = await convex.jsSyscall("storage/getBlob", { storageId });
|
|
130
130
|
return { blob: blob ?? null };
|
|
131
131
|
},
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { base32Encode, base32Decode, isValidBase32 } from './base32';
|
|
14
14
|
import { vintEncode, vintDecode, vintEncodedLength } from './vint';
|
|
15
15
|
import { fletcher16, verifyFletcher16 } from './fletcher16';
|
|
16
|
+
import { fillRandomValues } from '../utils/crypto';
|
|
16
17
|
/** Internal ID is always 16 bytes (128 bits) */
|
|
17
18
|
export const INTERNAL_ID_LENGTH = 16;
|
|
18
19
|
/** Minimum encoded length: 1 (vint) + 16 (id) + 2 (checksum) = 19 bytes = 31 base32 chars */
|
|
@@ -98,11 +99,11 @@ export function isValidDocumentId(encoded) {
|
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
// NOTE: Deterministic ID generation for retries is handled by the UDF runtime
|
|
101
|
-
// via an injected generator. This helper remains cryptographically random
|
|
102
|
-
|
|
102
|
+
// via an injected generator. This helper remains cryptographically random when
|
|
103
|
+
// native crypto is available, and degrades gracefully in runtimes without it.
|
|
103
104
|
function randomBytes(n) {
|
|
104
105
|
const buf = new Uint8Array(n);
|
|
105
|
-
|
|
106
|
+
fillRandomValues(buf);
|
|
106
107
|
return buf;
|
|
107
108
|
}
|
|
108
109
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -10,12 +10,12 @@ export { type ArrayBufferHex, type InternalDocumentId, documentIdKey, parseDocum
|
|
|
10
10
|
export * from "./docstore/sql/schema";
|
|
11
11
|
export * from "./docstore/search-index-registration";
|
|
12
12
|
export * from "./transactor";
|
|
13
|
-
export { UdfKernel } from "./kernel/udf-kernel";
|
|
13
|
+
export { UdfKernel, type UdfKernelConfig } from "./kernel/udf-kernel";
|
|
14
14
|
export { snapshotContext, transactionContext } from "./kernel/contexts";
|
|
15
15
|
export { QueryRuntime } from "./query/query-runtime";
|
|
16
16
|
export { SchemaService } from "./kernel/schema-service";
|
|
17
17
|
export { BlobStoreGateway } from "./kernel/blob-store-gateway";
|
|
18
|
-
export { DocStoreGateway } from "./kernel/docstore-gateway";
|
|
18
|
+
export { DocStoreGateway, createDocAccess, type DocAccess } from "./kernel/docstore-gateway";
|
|
19
19
|
export { SchedulerGateway } from "./kernel/scheduler-gateway";
|
|
20
20
|
export { UdfInvocationManager } from "./kernel/udf-invocation-manager";
|
|
21
21
|
export * from "./queryengine/cursor";
|
|
@@ -40,6 +40,7 @@ export * from "./utils/timestamp";
|
|
|
40
40
|
export * from "./utils/keyspace";
|
|
41
41
|
export * from "./utils/serialization";
|
|
42
42
|
export * from "./utils/written-ranges";
|
|
43
|
+
export * from "./utils/base64";
|
|
43
44
|
export type { TransactionHandle, CommitResult, OplogDelta, Transactor } from "./interfaces/transactor";
|
|
44
45
|
export type { KeyRange as InterfaceKeyRange } from "./interfaces/transactor";
|
|
45
46
|
export { type ChangeDelta, type ChangeStreamConsumer, } from "./interfaces/change-stream-consumer";
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ export { snapshotContext, transactionContext } from "./kernel/contexts";
|
|
|
15
15
|
export { QueryRuntime } from "./query/query-runtime";
|
|
16
16
|
export { SchemaService } from "./kernel/schema-service";
|
|
17
17
|
export { BlobStoreGateway } from "./kernel/blob-store-gateway";
|
|
18
|
-
export { DocStoreGateway } from "./kernel/docstore-gateway";
|
|
18
|
+
export { DocStoreGateway, createDocAccess } from "./kernel/docstore-gateway";
|
|
19
19
|
export { SchedulerGateway } from "./kernel/scheduler-gateway";
|
|
20
20
|
export { UdfInvocationManager } from "./kernel/udf-invocation-manager";
|
|
21
21
|
export * from "./queryengine/cursor";
|
|
@@ -49,6 +49,7 @@ export * from "./utils/timestamp";
|
|
|
49
49
|
export * from "./utils/keyspace";
|
|
50
50
|
export * from "./utils/serialization";
|
|
51
51
|
export * from "./utils/written-ranges";
|
|
52
|
+
export * from "./utils/base64";
|
|
52
53
|
export * from "./interfaces/shard-router";
|
|
53
54
|
export * from "./interfaces/cache-strategy";
|
|
54
55
|
export * from "./interfaces/execution-context";
|
|
@@ -1,21 +1,39 @@
|
|
|
1
|
+
function resolveFromRequire(req) {
|
|
2
|
+
for (const specifier of ["node:async_hooks", "async_hooks"]) {
|
|
3
|
+
try {
|
|
4
|
+
const mod = req(specifier);
|
|
5
|
+
if (mod?.AsyncLocalStorage) {
|
|
6
|
+
return mod.AsyncLocalStorage;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
// Try the next candidate module specifier.
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
1
15
|
const resolveAsyncLocalStorage = () => {
|
|
2
|
-
|
|
3
|
-
|
|
16
|
+
const globalCtor = globalThis?.AsyncLocalStorage;
|
|
17
|
+
if (globalCtor) {
|
|
18
|
+
return globalCtor;
|
|
4
19
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
if (
|
|
9
|
-
return
|
|
20
|
+
const runtimeRequire = typeof require === "function" ? require : undefined;
|
|
21
|
+
if (runtimeRequire) {
|
|
22
|
+
const ctor = resolveFromRequire(runtimeRequire);
|
|
23
|
+
if (ctor) {
|
|
24
|
+
return ctor;
|
|
10
25
|
}
|
|
11
|
-
const mod = req("node:async_hooks");
|
|
12
|
-
return mod.AsyncLocalStorage;
|
|
13
26
|
}
|
|
14
|
-
|
|
15
|
-
|
|
27
|
+
const globalRequire = globalThis?.require;
|
|
28
|
+
if (typeof globalRequire === "function") {
|
|
29
|
+
return resolveFromRequire(globalRequire);
|
|
16
30
|
}
|
|
31
|
+
return undefined;
|
|
17
32
|
};
|
|
18
33
|
const AsyncLocalStorageCtor = resolveAsyncLocalStorage();
|
|
34
|
+
export function hasAsyncLocalStorageSupport() {
|
|
35
|
+
return AsyncLocalStorageCtor !== undefined;
|
|
36
|
+
}
|
|
19
37
|
export class ContextStorage {
|
|
20
38
|
als;
|
|
21
39
|
stack = [];
|
|
@@ -5,15 +5,40 @@ type IndexEntries = Set<{
|
|
|
5
5
|
ts: bigint;
|
|
6
6
|
update: DatabaseIndexUpdate;
|
|
7
7
|
}>;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Polymorphic document access interface.
|
|
10
|
+
*
|
|
11
|
+
* Two implementations replace the previous `if (this.mutationTransaction)` branching:
|
|
12
|
+
* - `SnapshotDocAccess` — wraps DocStore directly (queries, actions)
|
|
13
|
+
* - `TransactionalDocAccess` — wraps OccMutationTransaction + DocStore (mutations)
|
|
14
|
+
*/
|
|
15
|
+
export interface DocAccess {
|
|
16
|
+
computeSnapshotTimestamp(snapshotOverride: bigint | null | undefined, inheritedSnapshot: bigint | null): bigint;
|
|
17
|
+
allocateTimestamp(): bigint;
|
|
18
|
+
fetchLatestDocument(id: InternalDocumentId, snapshotTimestamp: bigint): Promise<LatestDocument | null>;
|
|
19
|
+
fetchTable(tableId: string, snapshotTimestamp: bigint): Promise<LatestDocument[]>;
|
|
20
|
+
applyWrites(documents: DocumentLogEntry[], indexes: IndexEntries, conflictStrategy: "Error" | "Overwrite"): Promise<void>;
|
|
21
|
+
hasTransaction(): boolean;
|
|
22
|
+
getTransaction(): OccMutationTransaction | undefined;
|
|
23
|
+
getDocStore(): DocStore;
|
|
24
|
+
/** Attach the kernel context for read/write tracking (non-transactional path) */
|
|
25
|
+
attachContext(context: KernelContext): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Factory function that selects the appropriate DocAccess implementation
|
|
29
|
+
* based on whether a mutation transaction is active.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createDocAccess(docstore: DocStore, transaction?: OccMutationTransaction): DocAccess;
|
|
32
|
+
/**
|
|
33
|
+
* @deprecated Use `createDocAccess` factory instead.
|
|
34
|
+
* This class is kept as an alias for backwards compatibility.
|
|
35
|
+
*/
|
|
36
|
+
export declare class DocStoreGateway implements DocAccess {
|
|
37
|
+
private readonly delegate;
|
|
38
|
+
constructor(docstore: DocStore, transaction?: OccMutationTransaction);
|
|
13
39
|
attachContext(context: KernelContext): void;
|
|
14
40
|
getTransaction(): OccMutationTransaction | undefined;
|
|
15
41
|
hasTransaction(): boolean;
|
|
16
|
-
private getTimestampOracle;
|
|
17
42
|
computeSnapshotTimestamp(snapshotOverride: bigint | null | undefined, inheritedSnapshot: bigint | null): bigint;
|
|
18
43
|
allocateTimestamp(): bigint;
|
|
19
44
|
fetchLatestDocument(id: InternalDocumentId, snapshotTimestamp: bigint): Promise<LatestDocument | null>;
|
|
@@ -1,63 +1,137 @@
|
|
|
1
|
-
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Snapshot (non-transactional) path — queries, actions
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
class SnapshotDocAccess {
|
|
2
5
|
docstore;
|
|
3
|
-
mutationTransaction;
|
|
4
6
|
context;
|
|
5
|
-
|
|
7
|
+
oracle;
|
|
8
|
+
constructor(docstore) {
|
|
6
9
|
this.docstore = docstore;
|
|
7
|
-
this.
|
|
10
|
+
this.oracle = docstore.timestampOracle;
|
|
8
11
|
}
|
|
9
12
|
attachContext(context) {
|
|
10
13
|
this.context = context;
|
|
11
14
|
}
|
|
12
|
-
getTransaction() {
|
|
13
|
-
return this.mutationTransaction;
|
|
14
|
-
}
|
|
15
|
-
hasTransaction() {
|
|
16
|
-
return !!this.mutationTransaction;
|
|
17
|
-
}
|
|
18
|
-
getTimestampOracle() {
|
|
19
|
-
return this.docstore.timestampOracle;
|
|
20
|
-
}
|
|
21
15
|
computeSnapshotTimestamp(snapshotOverride, inheritedSnapshot) {
|
|
22
|
-
|
|
23
|
-
return this.mutationTransaction.getSnapshotTimestamp();
|
|
24
|
-
}
|
|
25
|
-
const oracle = this.getTimestampOracle();
|
|
26
|
-
const docstoreTs = oracle?.getCurrentTimestamp() ?? 0n;
|
|
16
|
+
const docstoreTs = this.oracle?.getCurrentTimestamp() ?? 0n;
|
|
27
17
|
const wallClockTs = BigInt(Date.now());
|
|
28
18
|
const maxTs = docstoreTs > wallClockTs ? docstoreTs : wallClockTs;
|
|
29
19
|
return snapshotOverride ?? inheritedSnapshot ?? maxTs;
|
|
30
20
|
}
|
|
31
21
|
allocateTimestamp() {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return oracle.allocateTimestamp();
|
|
22
|
+
if (this.oracle) {
|
|
23
|
+
return this.oracle.allocateTimestamp();
|
|
35
24
|
}
|
|
36
25
|
return BigInt(Date.now());
|
|
37
26
|
}
|
|
38
27
|
async fetchLatestDocument(id, snapshotTimestamp) {
|
|
39
|
-
if (this.mutationTransaction) {
|
|
40
|
-
return this.mutationTransaction.getLatest(id);
|
|
41
|
-
}
|
|
42
28
|
return this.docstore.get(id, snapshotTimestamp);
|
|
43
29
|
}
|
|
44
30
|
async fetchTable(tableId, snapshotTimestamp) {
|
|
45
|
-
if (this.mutationTransaction) {
|
|
46
|
-
return this.mutationTransaction.scanTable(tableId);
|
|
47
|
-
}
|
|
48
31
|
return this.docstore.scan(tableId, snapshotTimestamp);
|
|
49
32
|
}
|
|
50
33
|
async applyWrites(documents, indexes, conflictStrategy) {
|
|
51
|
-
if (this.mutationTransaction) {
|
|
52
|
-
this.mutationTransaction.stageWrite(documents, indexes, conflictStrategy);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
34
|
for (const { update } of indexes) {
|
|
56
35
|
this.context?.recordIndexWrite(update.index_id, update.key);
|
|
57
36
|
}
|
|
58
37
|
await this.docstore.write(documents, indexes, conflictStrategy);
|
|
59
38
|
}
|
|
39
|
+
hasTransaction() {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
getTransaction() {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
60
45
|
getDocStore() {
|
|
61
46
|
return this.docstore;
|
|
62
47
|
}
|
|
63
48
|
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Transactional path — mutations
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
class TransactionalDocAccess {
|
|
53
|
+
docstore;
|
|
54
|
+
transaction;
|
|
55
|
+
oracle;
|
|
56
|
+
constructor(docstore, transaction) {
|
|
57
|
+
this.docstore = docstore;
|
|
58
|
+
this.transaction = transaction;
|
|
59
|
+
this.oracle = docstore.timestampOracle;
|
|
60
|
+
}
|
|
61
|
+
attachContext(_context) {
|
|
62
|
+
// Transaction path: context read/write tracking is handled by OccMutationTransaction
|
|
63
|
+
}
|
|
64
|
+
computeSnapshotTimestamp(_snapshotOverride, _inheritedSnapshot) {
|
|
65
|
+
return this.transaction.getSnapshotTimestamp();
|
|
66
|
+
}
|
|
67
|
+
allocateTimestamp() {
|
|
68
|
+
if (this.oracle) {
|
|
69
|
+
return this.oracle.allocateTimestamp();
|
|
70
|
+
}
|
|
71
|
+
return BigInt(Date.now());
|
|
72
|
+
}
|
|
73
|
+
async fetchLatestDocument(id, _snapshotTimestamp) {
|
|
74
|
+
return this.transaction.getLatest(id);
|
|
75
|
+
}
|
|
76
|
+
async fetchTable(tableId, _snapshotTimestamp) {
|
|
77
|
+
return this.transaction.scanTable(tableId);
|
|
78
|
+
}
|
|
79
|
+
async applyWrites(documents, indexes, conflictStrategy) {
|
|
80
|
+
this.transaction.stageWrite(documents, indexes, conflictStrategy);
|
|
81
|
+
}
|
|
82
|
+
hasTransaction() {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
getTransaction() {
|
|
86
|
+
return this.transaction;
|
|
87
|
+
}
|
|
88
|
+
getDocStore() {
|
|
89
|
+
return this.docstore;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Factory function that selects the appropriate DocAccess implementation
|
|
94
|
+
* based on whether a mutation transaction is active.
|
|
95
|
+
*/
|
|
96
|
+
export function createDocAccess(docstore, transaction) {
|
|
97
|
+
return transaction
|
|
98
|
+
? new TransactionalDocAccess(docstore, transaction)
|
|
99
|
+
: new SnapshotDocAccess(docstore);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* @deprecated Use `createDocAccess` factory instead.
|
|
103
|
+
* This class is kept as an alias for backwards compatibility.
|
|
104
|
+
*/
|
|
105
|
+
export class DocStoreGateway {
|
|
106
|
+
delegate;
|
|
107
|
+
constructor(docstore, transaction) {
|
|
108
|
+
this.delegate = createDocAccess(docstore, transaction);
|
|
109
|
+
}
|
|
110
|
+
attachContext(context) {
|
|
111
|
+
this.delegate.attachContext(context);
|
|
112
|
+
}
|
|
113
|
+
getTransaction() {
|
|
114
|
+
return this.delegate.getTransaction();
|
|
115
|
+
}
|
|
116
|
+
hasTransaction() {
|
|
117
|
+
return this.delegate.hasTransaction();
|
|
118
|
+
}
|
|
119
|
+
computeSnapshotTimestamp(snapshotOverride, inheritedSnapshot) {
|
|
120
|
+
return this.delegate.computeSnapshotTimestamp(snapshotOverride, inheritedSnapshot);
|
|
121
|
+
}
|
|
122
|
+
allocateTimestamp() {
|
|
123
|
+
return this.delegate.allocateTimestamp();
|
|
124
|
+
}
|
|
125
|
+
async fetchLatestDocument(id, snapshotTimestamp) {
|
|
126
|
+
return this.delegate.fetchLatestDocument(id, snapshotTimestamp);
|
|
127
|
+
}
|
|
128
|
+
async fetchTable(tableId, snapshotTimestamp) {
|
|
129
|
+
return this.delegate.fetchTable(tableId, snapshotTimestamp);
|
|
130
|
+
}
|
|
131
|
+
async applyWrites(documents, indexes, conflictStrategy) {
|
|
132
|
+
return this.delegate.applyWrites(documents, indexes, conflictStrategy);
|
|
133
|
+
}
|
|
134
|
+
getDocStore() {
|
|
135
|
+
return this.delegate.getDocStore();
|
|
136
|
+
}
|
|
137
|
+
}
|
package/dist/kernel/index.d.ts
CHANGED
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { UdfKernel } from "./udf-kernel";
|
|
9
9
|
export { KernelContext } from "./kernel-context";
|
|
10
|
-
export { ContextStorage } from "./context-storage";
|
|
10
|
+
export { ContextStorage, hasAsyncLocalStorageSupport } from "./context-storage";
|
|
11
11
|
export { snapshotContext, transactionContext } from "./contexts";
|
|
12
12
|
export { AccessLog } from "./access-log";
|
|
13
13
|
export { BlobStoreGateway } from "./blob-store-gateway";
|
|
14
|
-
export { DocStoreGateway } from "./docstore-gateway";
|
|
14
|
+
export { DocStoreGateway, createDocAccess, type DocAccess } from "./docstore-gateway";
|
|
15
15
|
export { SchedulerGateway } from "./scheduler-gateway";
|
|
16
|
-
export { SchemaService
|
|
16
|
+
export { SchemaService } from "./schema-service";
|
|
17
|
+
export { isMissingSchemaModuleError } from "./missing-schema-error";
|
|
17
18
|
export type { DbIndexConfig, SearchIndexConfig, VectorIndexConfig } from "./schema-service";
|
|
18
19
|
export { KernelSyscalls, SyscallRouter } from "./syscalls";
|
|
19
20
|
export type { KernelAuthContext, KernelResources, LocalWrite } from "./types";
|
package/dist/kernel/index.js
CHANGED
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { UdfKernel } from "./udf-kernel";
|
|
9
9
|
export { KernelContext } from "./kernel-context";
|
|
10
|
-
export { ContextStorage } from "./context-storage";
|
|
10
|
+
export { ContextStorage, hasAsyncLocalStorageSupport } from "./context-storage";
|
|
11
11
|
export { snapshotContext, transactionContext } from "./contexts";
|
|
12
12
|
export { AccessLog } from "./access-log";
|
|
13
13
|
export { BlobStoreGateway } from "./blob-store-gateway";
|
|
14
|
-
export { DocStoreGateway } from "./docstore-gateway";
|
|
14
|
+
export { DocStoreGateway, createDocAccess } from "./docstore-gateway";
|
|
15
15
|
export { SchedulerGateway } from "./scheduler-gateway";
|
|
16
|
-
export { SchemaService
|
|
16
|
+
export { SchemaService } from "./schema-service";
|
|
17
|
+
export { isMissingSchemaModuleError } from "./missing-schema-error";
|
|
17
18
|
export { KernelSyscalls, SyscallRouter } from "./syscalls";
|
|
18
19
|
export { UdfInvocationManager } from "./udf-invocation-manager";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether an error indicates a missing convex/schema module.
|
|
3
|
+
* Schema is optional in Convex, so this is a normal condition — not an error.
|
|
4
|
+
*/
|
|
5
|
+
export function isMissingSchemaModuleError(error) {
|
|
6
|
+
if (!error)
|
|
7
|
+
return false;
|
|
8
|
+
// Convert error to string and check for known messages
|
|
9
|
+
const errorString = String(error);
|
|
10
|
+
const isMissing = errorString.includes('Unable to resolve module "schema"') ||
|
|
11
|
+
errorString.includes("Module not found: schema") ||
|
|
12
|
+
errorString.includes('Unable to resolve module "convex/schema"');
|
|
13
|
+
if (isMissing)
|
|
14
|
+
return true;
|
|
15
|
+
// Recursively check causes if present (common in ModuleRegistry errors)
|
|
16
|
+
const causes = error.causes;
|
|
17
|
+
if (Array.isArray(causes)) {
|
|
18
|
+
return causes.some(isMissingSchemaModuleError);
|
|
19
|
+
}
|
|
20
|
+
const cause = error.cause;
|
|
21
|
+
if (cause) {
|
|
22
|
+
return isMissingSchemaModuleError(cause);
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type NativeTimers = {
|
|
2
|
+
setTimeout: typeof globalThis.setTimeout;
|
|
3
|
+
clearTimeout: typeof globalThis.clearTimeout;
|
|
4
|
+
setInterval: typeof globalThis.setInterval;
|
|
5
|
+
clearInterval: typeof globalThis.clearInterval;
|
|
6
|
+
};
|
|
7
|
+
export declare const nativeTimers: NativeTimers;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const NATIVE_TIMERS_SYMBOL = Symbol.for("@concavejs/core/native-timers");
|
|
2
|
+
function captureNativeTimers() {
|
|
3
|
+
return {
|
|
4
|
+
setTimeout: globalThis.setTimeout.bind(globalThis),
|
|
5
|
+
clearTimeout: globalThis.clearTimeout.bind(globalThis),
|
|
6
|
+
setInterval: globalThis.setInterval.bind(globalThis),
|
|
7
|
+
clearInterval: globalThis.clearInterval.bind(globalThis),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
const globalWithNativeTimers = globalThis;
|
|
11
|
+
if (!globalWithNativeTimers[NATIVE_TIMERS_SYMBOL]) {
|
|
12
|
+
globalWithNativeTimers[NATIVE_TIMERS_SYMBOL] = captureNativeTimers();
|
|
13
|
+
}
|
|
14
|
+
export const nativeTimers = globalWithNativeTimers[NATIVE_TIMERS_SYMBOL];
|
|
@@ -51,8 +51,3 @@ export declare class SchemaService {
|
|
|
51
51
|
getTableNames(): Promise<string[]>;
|
|
52
52
|
private getSchemaDefinition;
|
|
53
53
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Check whether an error indicates a missing convex/schema module.
|
|
56
|
-
* Schema is optional in Convex, so this is a normal condition — not an error.
|
|
57
|
-
*/
|
|
58
|
-
export declare function isMissingSchemaModuleError(error: unknown): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getStandardIndexes } from "../queryengine/indexing/index-manager";
|
|
2
2
|
import { SchemaValidator, ValidatorError } from "../udf/analysis/validator";
|
|
3
3
|
import { loadConvexModule } from "../udf/module-loader/module-loader";
|
|
4
|
+
import { isMissingSchemaModuleError } from "./missing-schema-error";
|
|
4
5
|
export class SchemaService {
|
|
5
6
|
cachedSchemaDefinition = undefined;
|
|
6
7
|
tableCache = new Map();
|
|
@@ -146,28 +147,3 @@ export class SchemaService {
|
|
|
146
147
|
return this.cachedSchemaDefinition;
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
|
-
/**
|
|
150
|
-
* Check whether an error indicates a missing convex/schema module.
|
|
151
|
-
* Schema is optional in Convex, so this is a normal condition — not an error.
|
|
152
|
-
*/
|
|
153
|
-
export function isMissingSchemaModuleError(error) {
|
|
154
|
-
if (!error)
|
|
155
|
-
return false;
|
|
156
|
-
// Convert error to string and check for known messages
|
|
157
|
-
const errorString = String(error);
|
|
158
|
-
const isMissing = errorString.includes('Unable to resolve module "schema"') ||
|
|
159
|
-
errorString.includes("Module not found: schema") ||
|
|
160
|
-
errorString.includes('Unable to resolve module "convex/schema"');
|
|
161
|
-
if (isMissing)
|
|
162
|
-
return true;
|
|
163
|
-
// Recursively check causes if present (common in ModuleRegistry errors)
|
|
164
|
-
const causes = error.causes;
|
|
165
|
-
if (Array.isArray(causes)) {
|
|
166
|
-
return causes.some(isMissingSchemaModuleError);
|
|
167
|
-
}
|
|
168
|
-
const cause = error.cause;
|
|
169
|
-
if (cause) {
|
|
170
|
-
return isMissingSchemaModuleError(cause);
|
|
171
|
-
}
|
|
172
|
-
return false;
|
|
173
|
-
}
|