@tanstack/db
Version:
A reactive client store for building super fast apps on sync
261 lines (260 loc) • 11 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const index = require("./query/builder/index.cjs");
const ir = require("./query/ir.cjs");
const liveQueryPrimitives = require("./live-query-primitives.cjs");
const lifecycle = require("./collection/lifecycle.cjs");
const liveQueryCollection = require("./query/live-query-collection.cjs");
const internal = require("./query/live/internal.cjs");
const collectionRegistry = require("./query/live/collection-registry.cjs");
const irStableIdentity = require("./query/ir-stable-identity.cjs");
const runtimeReferenceIdentity = require("./query/runtime-reference-identity.cjs");
const errorMessage = require("./error-message.cjs");
const errors = require("./errors.cjs");
function configPool(config) {
const { pool } = config;
if (pool !== void 0 && typeof pool?.createPooledLiveQuery !== `function`)
throw new errors.InvalidPoolOptionError();
return pool;
}
function rejectLiveQueryInput(collections, rejection) {
try {
if (collections) resumeDeferredLiveQueryCollections(collections);
} catch {
}
throw rejection;
}
function releaseDeferredOnReclaim(liveQuery, deferredCollections) {
if (deferredCollections.size === 0) return void 0;
const pending = new Set(deferredCollections);
const release = () => {
try {
resumeDeferredLiveQueryCollections(pending);
} catch {
}
};
const gcTime = liveQuery.config.gcTime ?? lifecycle.DEFAULT_GC_TIME_MS;
if (gcTime > 0 && Number.isFinite(gcTime) && liveQuery.status !== `idle`) {
return liveQuery.once(`status:cleaned-up`, release);
}
const timer = setTimeout(release, lifecycle.UNSUBSCRIBED_GC_FLOOR_MS);
return () => clearTimeout(timer);
}
function resumeDeferredLiveQueryCollections(collections, reportSourceErrors = false) {
const pending = Array.from(collections);
collections.clear();
let firstFailure;
for (const collection of pending) {
try {
collection._resumeSyncStart();
if (reportSourceErrors && liveQueryPrimitives.isFailedStatus(collection.status)) {
throw collection._lifecycle.getSyncError();
}
} catch (error) {
firstFailure ??= { error };
}
}
if (firstFailure) throw firstFailure.error;
}
function prepareLiveQueryValue(value, dbClient, deferredCollections) {
if (typeof value === `function`) {
return prepareLiveQueryValue(
value(new index.BaseQueryBuilder()),
dbClient,
deferredCollections
);
}
if (value instanceof index.BaseQueryBuilder) {
return value._bindCollectionSources(
dbClient ? (options) => {
const config = options;
const collection = deferredCollections ? dbClient._materializeCollectionForRender(config) : dbClient.collection(config);
if (deferredCollections && collection._deferSyncStart())
deferredCollections.add(collection);
return collection;
} : void 0
);
}
if (value && typeof value === `object` && !liveQueryPrimitives.isCollection(value) && `query` in value) {
const pool = configPool(value);
const {
query,
queryKey: _queryKey,
client: _client,
pool: _pool,
...config
} = value;
const preparedQuery = prepareLiveQueryValue(
query,
dbClient,
deferredCollections
);
if (preparedQuery === void 0 || preparedQuery === null) {
return preparedQuery;
}
return {
...config,
// A DbClient query never pools, so its identity must not depend on
// pool: a server preload and a client hook must hash alike.
...pool && !dbClient ? { pool } : {},
query: preparedQuery
};
}
return value;
}
function getPreparedLiveQuerySources(value, onUnboundDescriptor) {
if (liveQueryPrimitives.isCollection(value)) return [value];
const query = queryConfig(value)?.query ?? value;
if (query instanceof index.BaseQueryBuilder) {
return ir.collectSourceRefs(query._getQuery()).map(
(source) => source.type === `descriptorRef` && onUnboundDescriptor ? onUnboundDescriptor(source.alias) : ir.requireCollectionSource(source)
);
}
return liveQueryPrimitives.isCollection(query) ? [query] : [];
}
function getLiveQueryRowOptions(value) {
if (liveQueryPrimitives.isCollection(value)) {
const { config: config2 } = value;
const internal$1 = config2.utils?.[internal.LIVE_QUERY_INTERNAL];
return [
internal$1?.hasCustomGetKey ? config2.getKey : void 0,
config2.schema
];
}
const config = value;
return [config?.getKey, config?.schema];
}
function getPreparedLiveQueryIdentity(value, pool) {
if (liveQueryPrimitives.isCollection(value)) return [`collection`, value.id];
if (value instanceof index.BaseQueryBuilder) {
const pooled = pool?.getPooledQueryIdentity(value);
return pooled === void 0 ? [`query`, irStableIdentity.getStableQueryBuilderHash(value)] : [`pooled`, pooled];
}
if (value && typeof value === `object` && `query` in value) {
const config = value;
return [
// getKey and schema bind for this query identity. Inline options must
// not turn a normal framework render into a new Collection.
`config`,
getPreparedLiveQueryIdentity(config.query, configPool(config)),
[`singleResult`, config.singleResult === true],
[
`defaultStringCollation`,
runtimeReferenceIdentity.getStringCollationIdentity(config.defaultStringCollation)
]
];
}
if (value === void 0 || value === null) return [`disabled`];
return [`value`, value];
}
function getLiveQueryHash(preparedValue, queryKey) {
if (queryKey?.length) {
const query = queryConfig(preparedValue)?.query;
const ir2 = liveQueryPrimitives.isCollection(preparedValue) ? collectionRegistry.getBuilderFromConfig(preparedValue.config)?.getWindow() : query instanceof index.BaseQueryBuilder ? query._getQuery() : void 0;
const identity2 = [`queryKey`, queryKey];
if (ir2 && (ir2.limit !== void 0 || (ir2.offset ?? 0) !== 0))
identity2.push({ limit: ir2.limit, offset: ir2.offset ?? 0 });
return irStableIdentity.getStableValueHash(identity2, `queryKey`);
}
const identity = liveQueryPrimitives.isCollection(preparedValue) ? [`collection`, preparedValue.id] : [`derived`, getPreparedLiveQueryIdentity(preparedValue)];
return irStableIdentity.getStableValueHash(identity, `queryKey`);
}
const resolvedQueryValues = /* @__PURE__ */ new WeakMap();
function queryConfig(value) {
return value instanceof index.BaseQueryBuilder ? { query: value } : value && typeof value === `object` ? value : void 0;
}
function moveExistingWindow(after, previous, owner) {
if (!previous || previous.status !== `ready` && previous.status !== `loading`)
return;
const retained = owner && resolvedQueryValues.get(owner);
if (!retained || retained.collection !== previous) return;
const before = retained.config;
if (!(before.query instanceof index.BaseQueryBuilder) || !(after?.query instanceof index.BaseQueryBuilder))
return;
const oldQuery = before.query._getQuery();
const query = after.query._getQuery();
const limit = query.limit;
const offset = query.offset ?? 0;
if (after.singleResult === true || query.singleResult || !query.orderBy?.length || oldQuery.limit === void 0 || limit === void 0 || !Number.isSafeInteger(limit) || limit < 0 || !Number.isSafeInteger(offset) || offset < 0)
return;
const keys = Object.keys(before);
if (keys.length !== Object.keys(after).length || keys.some((key) => key !== `query` && before[key] !== after[key]))
return;
const sources = getPreparedLiveQuerySources(before);
const nextSources = getPreparedLiveQuerySources(after);
if (sources.length !== nextSources.length || sources.some((source, index2) => source !== nextSources[index2]))
return;
try {
if (irStableIdentity.getQueryIdentity({ ...oldQuery, limit: void 0, offset: void 0 }) !== irStableIdentity.getQueryIdentity({ ...query, limit: void 0, offset: void 0 }))
return;
} catch (error) {
if (error instanceof errors.UnhashableQueryIRError) return;
throw error;
}
const builder = collectionRegistry.getBuilderFromConfig(previous.config);
if (!builder) return;
return () => {
queueMicrotask(() => {
if (resolvedQueryValues.get(owner)?.collection !== previous || previous.subscriberCount === 0)
return;
const result = builder.setWindow({ offset, limit });
if (result !== true) void result.catch(() => {
});
});
};
}
function resolveLiveQueryValue(value, {
gcTime,
pool,
startSync = true,
previousCollection,
windowOwner,
onWindowChange
} = {}) {
if (previousCollection !== void 0)
value = prepareLiveQueryValue(value, void 0);
const input = queryConfig(value);
const move = moveExistingWindow(input, previousCollection, windowOwner);
if (move) {
if (onWindowChange) onWindowChange(move);
else move();
return previousCollection;
}
if (value === void 0 || value === null) return null;
if (liveQueryPrimitives.isCollection(value)) {
value.startSyncImmediate();
return value;
}
if (input) {
const requested = configPool(input);
const { pool: _pool, ...given } = input;
const pooling = pool === false ? void 0 : requested ?? pool;
const built = pooling && typeof given.query === `function` ? given.query(new index.BaseQueryBuilder()) : void 0;
const config = built instanceof index.BaseQueryBuilder ? { ...given, query: built } : given;
const pooled = pooling && config.query instanceof index.BaseQueryBuilder && Object.keys(config).every((key) => key === `query` || key === `gcTime`) ? pooling.createPooledLiveQuery(config.query, {
gcTime: config.gcTime ?? gcTime
}) : void 0;
const collection = pooled ?? liveQueryCollection.createLiveQueryCollection({
...config,
startSync: config.startSync ?? startSync,
gcTime: config.gcTime ?? gcTime
});
if (previousCollection !== void 0 && windowOwner)
resolvedQueryValues.set(windowOwner, { collection, config: input });
return collection;
}
throw new Error(
errorMessage.devBuild() && process.env.NODE_ENV !== `production` ? `A live query must be a QueryBuilder, LiveQueryCollectionConfig, Collection, undefined, or null. Got: ${typeof value}` : errorMessage.codedMessage(102, { type: typeof value })
);
}
exports.configPool = configPool;
exports.getLiveQueryHash = getLiveQueryHash;
exports.getLiveQueryRowOptions = getLiveQueryRowOptions;
exports.getPreparedLiveQueryIdentity = getPreparedLiveQueryIdentity;
exports.getPreparedLiveQuerySources = getPreparedLiveQuerySources;
exports.prepareLiveQueryValue = prepareLiveQueryValue;
exports.rejectLiveQueryInput = rejectLiveQueryInput;
exports.releaseDeferredOnReclaim = releaseDeferredOnReclaim;
exports.resolveLiveQueryValue = resolveLiveQueryValue;
exports.resumeDeferredLiveQueryCollections = resumeDeferredLiveQueryCollections;
//# sourceMappingURL=live-query-options.cjs.map