@magnetarjs/core
Advanced tools
Comparing version 0.20.1 to 1.0.1
@@ -1,20 +0,3 @@ | ||
import { GlobalConfig, MagnetarInstance, WriteLock } from '@magnetarjs/types'; | ||
export * from '@magnetarjs/types'; | ||
/** | ||
* Creates a magnetar instance. | ||
* @see {@link GlobalConfig} | ||
* @see {@link MagnetarInstance} | ||
*/ | ||
declare function Magnetar(magnetarConfig: GlobalConfig): MagnetarInstance; | ||
/** | ||
* Returns a tuple with `[CollectionPath, DocId]` if the `DocId` is `undefined` that means that the `modulePath` passed is a collection! | ||
*/ | ||
declare function getCollectionPathDocIdEntry(modulePath: string): [CollectionPath: string, DocId: string | undefined]; | ||
/** | ||
* Gets all WriteLock objects of a certain `collectionPath` from the `WriteLockMap` | ||
*/ | ||
declare function getCollectionWriteLocks(collectionPath: string, writeLockMap: Map<string, WriteLock>): WriteLock[]; | ||
export { Magnetar, getCollectionPathDocIdEntry, getCollectionWriteLocks }; | ||
export * from './Magnetar.js'; | ||
export * from './helpers/pathHelpers.js'; |
@@ -1,963 +0,3 @@ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __export = (target, all) => { | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __copyProps = (to, from, except, desc) => { | ||
if (from && typeof from === "object" || typeof from === "function") { | ||
for (let key of __getOwnPropNames(from)) | ||
if (!__hasOwnProp.call(to, key) && key !== except) | ||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
} | ||
return to; | ||
}; | ||
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default")); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
// src/index.ts | ||
var src_exports = {}; | ||
__export(src_exports, { | ||
Magnetar: () => Magnetar, | ||
getCollectionPathDocIdEntry: () => getCollectionPathDocIdEntry, | ||
getCollectionWriteLocks: () => getCollectionWriteLocks | ||
}); | ||
module.exports = __toCommonJS(src_exports); | ||
__reExport(src_exports, require("@magnetarjs/types"), module.exports); | ||
// src/Magnetar.ts | ||
var import_types3 = require("@magnetarjs/types"); | ||
var import_getorset_anything2 = require("getorset-anything"); | ||
var import_is_what7 = require("is-what"); | ||
// src/Collection.ts | ||
var import_types = require("@magnetarjs/types"); | ||
var import_merge_anything = require("merge-anything"); | ||
// src/helpers/moduleHelpers.ts | ||
var import_is_what2 = require("is-what"); | ||
// src/helpers/throwFns.ts | ||
var import_utils = require("@magnetarjs/utils"); | ||
var import_is_what = require("is-what"); | ||
function logError(errorMessage) { | ||
console.error("[@magnetarjs error]\n", errorMessage); | ||
} | ||
function logErrorAndThrow(errorMessage) { | ||
logError(errorMessage); | ||
throw new Error(errorMessage); | ||
} | ||
function throwOnIncompleteStreamResponses(streamInfoPerStore, doOnStreamFns) { | ||
const noStreamLogic = !Object.keys(streamInfoPerStore).length; | ||
if (noStreamLogic) { | ||
const errorMessage = "None of your store plugins have implemented logic to open a stream."; | ||
logErrorAndThrow(errorMessage); | ||
} | ||
const noDoOnStreamLogic = !Object.values(doOnStreamFns).flat().length; | ||
if (noDoOnStreamLogic) { | ||
const errorMessage = "None of your store plugins have implemented logic to do something with the data coming in from streams."; | ||
logErrorAndThrow(errorMessage); | ||
} | ||
} | ||
function throwIfNoFnsToExecute(storesToExecute) { | ||
if (storesToExecute.length === 0) { | ||
const errorMessage = "None of your store plugins have implemented this function or you have not defined an executionOrder anywhere."; | ||
logErrorAndThrow(errorMessage); | ||
} | ||
} | ||
function throwIfNolocalStoreName(localStoreName) { | ||
if ((0, import_is_what.isFullString)(localStoreName)) | ||
return; | ||
const errorMessage = `No 'localStoreName' provided.`; | ||
logErrorAndThrow(errorMessage); | ||
} | ||
function throwIfInvalidModulePath(modulePath, moduleType) { | ||
let errorMessage = ""; | ||
if (moduleType === "collection") { | ||
if (!modulePath) | ||
errorMessage = 'You must provide a collection id (or a "path" like so: collection/doc/collection).'; | ||
if ((0, import_utils.isDocModule)(modulePath)) | ||
errorMessage = `Your collection id (or "path") must be of odd segments. The expected pattern is: collection/doc/collection ... Yours was ${modulePath}`; | ||
} | ||
if (moduleType === "doc") { | ||
if (!modulePath) | ||
errorMessage = 'You must provide a document id (or a "path" like so: collection/doc).'; | ||
if ((0, import_utils.isCollectionModule)(modulePath)) | ||
errorMessage = `Your doc id (or "path") must be of even segments. The expected pattern is: collection/doc/collection/doc ... Yours was ${modulePath}`; | ||
} | ||
if (errorMessage) | ||
logErrorAndThrow(errorMessage); | ||
} | ||
// src/helpers/moduleHelpers.ts | ||
function getPluginModuleConfig(moduleConfig, storeName) { | ||
const { query, where, orderBy, limit, startAfter, configPerStore = {} } = moduleConfig; | ||
const extraStoreConfig = (0, import_is_what2.isPlainObject)(configPerStore[storeName]) ? configPerStore[storeName] : {}; | ||
return { ...extraStoreConfig, query, where, orderBy, limit, startAfter }; | ||
} | ||
function executeSetupModulePerStore(globalConfigStores, [collectionPath, docId], moduleConfig) { | ||
for (const storeName in globalConfigStores) { | ||
const { setupModule } = globalConfigStores[storeName]; | ||
if ((0, import_is_what2.isFunction)(setupModule)) { | ||
const pluginModuleConfig = getPluginModuleConfig(moduleConfig, storeName); | ||
setupModule({ collectionPath, docId, pluginModuleConfig }); | ||
} | ||
} | ||
} | ||
function getDataFromDataStore(moduleConfig, globalConfig, collectionPath, docId) { | ||
const localStoreName = globalConfig.localStoreName; | ||
throwIfNolocalStoreName(localStoreName); | ||
const getModuleData = globalConfig.stores[localStoreName].getModuleData; | ||
if (!getModuleData) { | ||
throw new Error("The data store did not provide a getModuleData function!"); | ||
} | ||
const pluginModuleConfig = getPluginModuleConfig(moduleConfig, localStoreName); | ||
return getModuleData({ collectionPath, docId, pluginModuleConfig }); | ||
} | ||
function getExistsFromDataStore(globalConfig, collectionPath, docId) { | ||
const localStoreName = globalConfig.localStoreName; | ||
throwIfNolocalStoreName(localStoreName); | ||
const getModuleExists = globalConfig.stores[localStoreName].getModuleExists; | ||
if (!getModuleExists) { | ||
throw new Error("The data store did not provide a getModuleExists function!"); | ||
} | ||
return getModuleExists({ collectionPath, docId }); | ||
} | ||
function getCountFromDataStore(moduleConfig, globalConfig, collectionPath) { | ||
const localStoreName = globalConfig.localStoreName; | ||
throwIfNolocalStoreName(localStoreName); | ||
const getModuleCount = globalConfig.stores[localStoreName].getModuleCount; | ||
if (!getModuleCount) { | ||
throw new Error("The data store did not provide a getModuleCount function!"); | ||
} | ||
const pluginModuleConfig = getPluginModuleConfig(moduleConfig, localStoreName); | ||
return getModuleCount({ collectionPath, pluginModuleConfig }); | ||
} | ||
function proxify(target, propExecutionDic) { | ||
const dataHandler = { | ||
get: function(target2, key, proxyRef) { | ||
if (key in propExecutionDic) { | ||
return propExecutionDic[key](); | ||
} | ||
return Reflect.get(target2, key, proxyRef); | ||
} | ||
}; | ||
return new Proxy(target, dataHandler); | ||
} | ||
// src/moduleActions/handleActionPerStore.ts | ||
var import_getorset_anything = require("getorset-anything"); | ||
var import_is_what4 = require("is-what"); | ||
// src/helpers/eventHelpers.ts | ||
function getEventNameFnsMap(...onMaps) { | ||
const _onMaps = onMaps.filter(Boolean); | ||
const result = { | ||
before: _onMaps.flatMap((on) => on.before ?? []), | ||
success: _onMaps.flatMap((on) => on.success ?? []), | ||
error: _onMaps.flatMap((on) => on.error ?? []), | ||
revert: _onMaps.flatMap((on) => on.revert ?? []) | ||
}; | ||
return result; | ||
} | ||
// src/helpers/executeOnFns.ts | ||
function executeOnFns(params) { | ||
const { modifyReadResultFns, localStoreFns, payload, docMetaData } = params; | ||
let newPayload = payload; | ||
for (const fn of modifyReadResultFns) { | ||
if (newPayload) | ||
newPayload = fn(newPayload, docMetaData); | ||
} | ||
for (const fn of localStoreFns) { | ||
newPayload = fn(newPayload, docMetaData); | ||
} | ||
return newPayload; | ||
} | ||
// src/helpers/modifyPayload.ts | ||
function getModifyPayloadFnsMap(...onMaps) { | ||
const _onMaps = onMaps.filter(Boolean); | ||
const writeFns = _onMaps.flatMap((on) => on.write ?? []); | ||
const readFns = _onMaps.flatMap((on) => on.read ?? []); | ||
const result = { | ||
insert: _onMaps.flatMap((on) => on.insert ?? []).concat(writeFns), | ||
merge: _onMaps.flatMap((on) => on.merge ?? []).concat(writeFns), | ||
assign: _onMaps.flatMap((on) => on.assign ?? []).concat(writeFns), | ||
replace: _onMaps.flatMap((on) => on.replace ?? []).concat(writeFns), | ||
deleteProp: _onMaps.flatMap((on) => on.deleteProp ?? []), | ||
delete: [], | ||
// delete has no payload | ||
stream: _onMaps.flatMap((on) => on.stream ?? []).concat(readFns), | ||
fetch: _onMaps.flatMap((on) => on.fetch ?? []).concat(readFns) | ||
}; | ||
return result; | ||
} | ||
// src/helpers/modifyReadResponse.ts | ||
function getModifyReadResponseFnsMap(...onMaps) { | ||
const _onMaps = onMaps.filter(Boolean); | ||
const result = { | ||
added: _onMaps.flatMap((on) => on.added ?? []), | ||
modified: _onMaps.flatMap((on) => on.modified ?? []), | ||
removed: _onMaps.flatMap((on) => on.removed ?? []) | ||
}; | ||
return result; | ||
} | ||
// src/helpers/pathHelpers.ts | ||
var import_utils2 = require("@magnetarjs/utils"); | ||
function getCollectionPathDocIdEntry(modulePath) { | ||
if ((0, import_utils2.isCollectionModule)(modulePath)) | ||
return [modulePath, void 0]; | ||
const collectionPath = modulePath.split("/").slice(0, -1).join("/"); | ||
const docId = modulePath.split("/").slice(-1)[0]; | ||
return [collectionPath, docId]; | ||
} | ||
function getCollectionWriteLocks(collectionPath, writeLockMap) { | ||
return [...writeLockMap.entries()].filter(([modulePath]) => { | ||
const [_collectionPath] = getCollectionPathDocIdEntry(modulePath); | ||
return _collectionPath === collectionPath; | ||
}).map(([modulePath, writeLock]) => writeLock); | ||
} | ||
// src/helpers/pluginHelpers.ts | ||
var import_is_what3 = require("is-what"); | ||
function isDoOnStream(payload) { | ||
const isNotDoOnStream = !(0, import_is_what3.isPlainObject)(payload) || payload.streaming || payload.stop || !(payload.added || payload.modified || payload.removed); | ||
return !isNotDoOnStream; | ||
} | ||
function isDoOnFetchCount(payload) { | ||
return (0, import_is_what3.isFunction)(payload); | ||
} | ||
function isFetchCountResponse(payload) { | ||
return (0, import_is_what3.isPlainObject)(payload) && (0, import_is_what3.isNumber)(payload.count); | ||
} | ||
function isDoOnFetch(payload) { | ||
return (0, import_is_what3.isFunction)(payload); | ||
} | ||
function isFetchResponse(payload) { | ||
return (0, import_is_what3.isPlainObject)(payload) && (0, import_is_what3.isArray)(payload.docs); | ||
} | ||
// src/moduleActions/handleAction.ts | ||
async function handleAction(args) { | ||
const { | ||
collectionPath, | ||
docId, | ||
modulePath, | ||
pluginModuleConfig, | ||
pluginAction, | ||
payload, | ||
actionConfig = {}, | ||
eventNameFnsMap: on, | ||
onError, | ||
actionName, | ||
stopExecutionAfterAction, | ||
storeName | ||
} = args; | ||
let abortExecution = false; | ||
const abort = () => { | ||
abortExecution = true; | ||
}; | ||
for (const fn of on.before) { | ||
await fn({ payload, actionName, storeName, abort, collectionPath, docId, path: modulePath, pluginModuleConfig }); | ||
} | ||
if (abortExecution) { | ||
stopExecutionAfterAction(); | ||
return; | ||
} | ||
let result; | ||
try { | ||
result = await pluginAction({ | ||
payload, | ||
actionConfig, | ||
collectionPath, | ||
docId, | ||
pluginModuleConfig | ||
}); | ||
} catch (error) { | ||
for (const fn of on.error) { | ||
await fn({ payload, actionName, storeName, abort, error, collectionPath, docId, path: modulePath, pluginModuleConfig }); | ||
} | ||
if (abortExecution || onError === "stop") { | ||
stopExecutionAfterAction(); | ||
throw error; | ||
} | ||
if (onError === "revert") { | ||
stopExecutionAfterAction("revert"); | ||
} | ||
return error; | ||
} | ||
for (const fn of on.success) { | ||
await fn({ payload, result, actionName, storeName, abort, collectionPath, docId, path: modulePath, pluginModuleConfig }); | ||
} | ||
if (abortExecution) { | ||
stopExecutionAfterAction(); | ||
return result; | ||
} | ||
return result; | ||
} | ||
// src/moduleActions/handleActionPerStore.ts | ||
function handleActionPerStore(sharedParams, actionName, actionType) { | ||
const { collectionPath, _docId, moduleConfig, globalConfig, fetchPromises, writeLockMap, docFn, collectionFn, setLastFetched } = sharedParams; | ||
return function(payload, actionConfig = {}) { | ||
const fetchPromiseKey = JSON.stringify(payload); | ||
const foundFetchPromise = fetchPromises.get(fetchPromiseKey); | ||
if (actionName === "fetch" && (0, import_is_what4.isPromise)(foundFetchPromise)) | ||
return foundFetchPromise; | ||
const writeLockId = _docId ? `${collectionPath}/${_docId}` : collectionPath; | ||
const writeLock = (0, import_getorset_anything.mapGetOrSet)(writeLockMap, writeLockId, () => { | ||
return { promise: null, resolve: () => { | ||
}, countdown: null }; | ||
}); | ||
if (actionName !== "fetch" && actionName !== "fetchCount") { | ||
if (writeLock.promise === null) { | ||
writeLock.promise = new Promise((resolve) => { | ||
writeLock.resolve = () => { | ||
resolve(); | ||
writeLock.resolve = () => { | ||
}; | ||
writeLock.promise = null; | ||
if (writeLock.countdown !== null) { | ||
clearTimeout(writeLock.countdown); | ||
writeLock.countdown = null; | ||
} | ||
}; | ||
}); | ||
} | ||
if (writeLock.promise !== null && writeLock.countdown !== null) { | ||
clearTimeout(writeLock.countdown); | ||
writeLock.countdown = null; | ||
} | ||
} | ||
const actionPromise = new Promise(async (resolve, reject) => { | ||
const force = payload?.force === true; | ||
if (actionName === "fetch" && force) { | ||
await writeLock.promise; | ||
if (!_docId) { | ||
const collectionWriteMaps = getCollectionWriteLocks(collectionPath, writeLockMap); | ||
await Promise.allSettled(collectionWriteMaps.map((w) => w.promise)); | ||
} | ||
} | ||
try { | ||
let stopExecutionAfterAction2 = function(trueOrRevert = true) { | ||
stopExecution = trueOrRevert; | ||
}; | ||
var stopExecutionAfterAction = stopExecutionAfterAction2; | ||
let docId = _docId; | ||
let modulePath = [collectionPath, docId].filter(Boolean).join("/"); | ||
const onError = actionConfig.onError || moduleConfig.onError || globalConfig.onError; | ||
const modifyPayloadFnsMap = getModifyPayloadFnsMap( | ||
globalConfig.modifyPayloadOn, | ||
moduleConfig.modifyPayloadOn, | ||
actionConfig.modifyPayloadOn | ||
); | ||
const modifyReadResponseMap = getModifyReadResponseFnsMap( | ||
globalConfig.modifyReadResponseOn, | ||
moduleConfig.modifyReadResponseOn, | ||
actionConfig.modifyReadResponseOn | ||
); | ||
const eventNameFnsMap = getEventNameFnsMap( | ||
globalConfig.on, | ||
moduleConfig.on, | ||
actionConfig.on | ||
); | ||
const storesToExecute = actionConfig.executionOrder || (moduleConfig.executionOrder || {})[actionName] || (moduleConfig.executionOrder || {})[actionType] || (globalConfig.executionOrder || {})[actionName] || (globalConfig.executionOrder || {})[actionType] || []; | ||
throwIfNoFnsToExecute(storesToExecute); | ||
if (actionName !== "fetchCount") { | ||
for (const modifyFn of modifyPayloadFnsMap[actionName]) { | ||
payload = modifyFn(payload, docId); | ||
} | ||
} | ||
let stopExecution = false; | ||
const doOnAddedFns = modifyReadResponseMap.added; | ||
const doOnFetchFns = []; | ||
const doOnFetchCountFns = []; | ||
const collectionFetchResult = /* @__PURE__ */ new Map(); | ||
let fetchCount = NaN; | ||
let resultFromPlugin; | ||
for (const [i, storeName] of storesToExecute.entries()) { | ||
if (stopExecution === true) | ||
break; | ||
const pluginAction = globalConfig.stores[storeName].actions[actionName]; | ||
const pluginModuleConfig = getPluginModuleConfig(moduleConfig, storeName); | ||
resultFromPlugin = !pluginAction ? resultFromPlugin : await handleAction({ | ||
collectionPath, | ||
docId, | ||
modulePath, | ||
pluginModuleConfig, | ||
pluginAction, | ||
payload, | ||
// should always use the payload as passed originally for clarity | ||
actionConfig, | ||
eventNameFnsMap, | ||
onError, | ||
actionName, | ||
stopExecutionAfterAction: stopExecutionAfterAction2, | ||
storeName | ||
}); | ||
if (stopExecution === "revert") { | ||
const storesToRevert = storesToExecute.slice(0, i); | ||
storesToRevert.reverse(); | ||
for (const storeToRevert of storesToRevert) { | ||
const pluginRevertAction = globalConfig.stores[storeToRevert].revert; | ||
const pluginModuleConfig2 = getPluginModuleConfig(moduleConfig, storeToRevert); | ||
await pluginRevertAction({ | ||
payload, | ||
actionConfig, | ||
collectionPath, | ||
docId, | ||
pluginModuleConfig: pluginModuleConfig2, | ||
actionName, | ||
error: resultFromPlugin | ||
// in this case the result is the error | ||
}); | ||
for (const fn of eventNameFnsMap.revert) { | ||
await fn({ payload, result: resultFromPlugin, actionName, storeName, collectionPath, docId, path: modulePath, pluginModuleConfig: pluginModuleConfig2 }); | ||
} | ||
} | ||
if (actionName === "fetch" && docId) { | ||
doOnFetchFns.forEach((fn) => fn(void 0, "error")); | ||
} | ||
if (actionName !== "fetch" && actionName !== "fetchCount") { | ||
writeLock.resolve(); | ||
} | ||
throw resultFromPlugin; | ||
} | ||
if (actionName === "insert") { | ||
if (!docId) { | ||
if ((0, import_is_what4.isFullString)(resultFromPlugin)) { | ||
docId = resultFromPlugin; | ||
} | ||
if ((0, import_is_what4.isFullArray)(resultFromPlugin) && (0, import_is_what4.isFullString)(resultFromPlugin[0])) { | ||
docId = resultFromPlugin[0]; | ||
} | ||
modulePath = [collectionPath, docId].filter(Boolean).join("/"); | ||
} | ||
} | ||
if (actionName === "fetch") { | ||
if (isDoOnFetch(resultFromPlugin)) { | ||
doOnFetchFns.push(resultFromPlugin); | ||
} | ||
if (isFetchResponse(resultFromPlugin)) { | ||
const { docs, reachedEnd, cursor } = resultFromPlugin; | ||
if ((0, import_is_what4.isBoolean)(reachedEnd)) | ||
setLastFetched?.({ reachedEnd, cursor }); | ||
for (const docMetaData of docs) { | ||
const docResult = executeOnFns({ | ||
modifyReadResultFns: doOnAddedFns, | ||
localStoreFns: doOnFetchFns, | ||
payload: docMetaData.data, | ||
docMetaData | ||
}); | ||
if (docResult) | ||
collectionFetchResult.set(docMetaData.id, docResult); | ||
const optimisticFetch = !force; | ||
if (optimisticFetch) { | ||
stopExecutionAfterAction2(true); | ||
} | ||
} | ||
} | ||
} | ||
if (actionName === "fetchCount") { | ||
if (isDoOnFetchCount(resultFromPlugin)) { | ||
doOnFetchCountFns.push(resultFromPlugin); | ||
} | ||
if (isFetchCountResponse(resultFromPlugin)) { | ||
for (const doOnFetchCountFn of doOnFetchCountFns) { | ||
doOnFetchCountFn(resultFromPlugin); | ||
} | ||
if (isNaN(fetchCount) || resultFromPlugin.count > fetchCount) { | ||
fetchCount = resultFromPlugin.count; | ||
} | ||
} | ||
} | ||
} | ||
if (actionName === "fetchCount") { | ||
resolve(fetchCount); | ||
return; | ||
} | ||
if (actionName !== "fetch" && !writeLock.countdown) { | ||
writeLock.countdown = setTimeout(writeLock.resolve, 5e3); | ||
} | ||
if (actionName === "insert" && docId) { | ||
resolve(docFn(modulePath, moduleConfig)); | ||
return; | ||
} | ||
if (docId || !collectionFn) { | ||
resolve(docFn(modulePath, moduleConfig).data); | ||
if (actionName === "fetch") | ||
fetchPromises.delete(fetchPromiseKey); | ||
return; | ||
} | ||
resolve(collectionFetchResult); | ||
if (actionName === "fetch") | ||
fetchPromises.delete(fetchPromiseKey); | ||
} catch (error) { | ||
reject(error); | ||
if (actionName === "fetch") | ||
fetchPromises.delete(fetchPromiseKey); | ||
} | ||
}); | ||
if (actionName === "fetch") { | ||
fetchPromises.set(fetchPromiseKey, actionPromise); | ||
} | ||
return actionPromise; | ||
}; | ||
} | ||
// src/moduleActions/handleStreamPerStore.ts | ||
var import_is_what6 = require("is-what"); | ||
// src/helpers/writeLockHelpers.ts | ||
var import_is_what5 = require("is-what"); | ||
async function writeLockPromise(writeLockMap, docIdentifier) { | ||
const writeLock = writeLockMap.get(docIdentifier); | ||
if (writeLock && (0, import_is_what5.isPromise)(writeLock.promise)) { | ||
await writeLock.promise; | ||
} | ||
} | ||
async function getDocAfterWritelock(params) { | ||
const { writeLockMap, lastIncomingDocs, docIdentifier, payload, meta } = params; | ||
lastIncomingDocs.set(docIdentifier, { payload, meta }); | ||
await writeLockPromise(writeLockMap, docIdentifier); | ||
const lastIncoming = lastIncomingDocs.get(docIdentifier); | ||
if (!lastIncoming) { | ||
return; | ||
} | ||
lastIncomingDocs.delete(docIdentifier); | ||
return lastIncoming; | ||
} | ||
// src/moduleActions/handleStream.ts | ||
async function handleStream(args) { | ||
const { | ||
collectionPath, | ||
docId, | ||
pluginModuleConfig, | ||
pluginAction, | ||
payload, | ||
actionConfig = {}, | ||
eventNameFnsMap: on, | ||
actionName, | ||
storeName, | ||
mustExecuteOnRead | ||
} = args; | ||
const abort = () => { | ||
}; | ||
const path = [collectionPath, docId].filter(Boolean).join("/"); | ||
for (const fn of on.before) { | ||
await fn({ payload, actionName, storeName, abort, collectionPath, docId, path, pluginModuleConfig }); | ||
} | ||
let result; | ||
try { | ||
const pluginStreamAction = pluginAction; | ||
result = await pluginStreamAction({ | ||
payload, | ||
actionConfig, | ||
collectionPath, | ||
docId, | ||
pluginModuleConfig, | ||
mustExecuteOnRead | ||
}); | ||
} catch (error) { | ||
for (const fn of on.error) { | ||
await fn({ payload, actionName, storeName, error, abort, collectionPath, docId, path, pluginModuleConfig }); | ||
} | ||
throw error; | ||
} | ||
for (const fn of on.success) { | ||
await fn({ payload, result, actionName, storeName, abort, collectionPath, docId, path, pluginModuleConfig }); | ||
} | ||
return result; | ||
} | ||
// src/moduleActions/handleStreamPerStore.ts | ||
function handleStreamPerStore([collectionPath, docId], moduleConfig, globalConfig, actionType, streaming, cacheStream, writeLockMap) { | ||
return async function(payload, actionConfig = {}) { | ||
const foundStream = streaming(); | ||
if ((0, import_is_what6.isPromise)(foundStream)) | ||
return foundStream; | ||
const eventNameFnsMap = getEventNameFnsMap(globalConfig.on, moduleConfig.on, actionConfig.on); | ||
const modifyPayloadFnsMap = getModifyPayloadFnsMap( | ||
globalConfig.modifyPayloadOn, | ||
moduleConfig.modifyPayloadOn, | ||
actionConfig.modifyPayloadOn | ||
); | ||
const modifyReadResponseMap = getModifyReadResponseFnsMap( | ||
globalConfig.modifyReadResponseOn, | ||
moduleConfig.modifyReadResponseOn, | ||
actionConfig.modifyReadResponseOn | ||
); | ||
const storesToExecute = actionConfig.executionOrder || (moduleConfig.executionOrder || {})["stream"] || (moduleConfig.executionOrder || {})[actionType] || (globalConfig.executionOrder || {})["stream"] || (globalConfig.executionOrder || {})[actionType] || []; | ||
throwIfNoFnsToExecute(storesToExecute); | ||
for (const modifyFn of modifyPayloadFnsMap["stream"]) { | ||
payload = modifyFn(payload, docId); | ||
} | ||
const streamInfoPerStore = {}; | ||
const modifyReadResponseFns = { | ||
added: modifyReadResponseMap.added, | ||
modified: modifyReadResponseMap.modified, | ||
removed: modifyReadResponseMap.removed | ||
}; | ||
const doOnStreamFns = { | ||
added: [], | ||
modified: [], | ||
removed: [] | ||
}; | ||
const lastIncomingDocs = /* @__PURE__ */ new Map(); | ||
const mustExecuteOnRead = { | ||
added: async (_payload, _meta) => { | ||
const docIdentifier = `${collectionPath}/${_meta.id}`; | ||
const result = await getDocAfterWritelock({ | ||
writeLockMap, | ||
docIdentifier, | ||
lastIncomingDocs, | ||
meta: _meta, | ||
payload: _payload | ||
}); | ||
if (!result) | ||
return; | ||
return executeOnFns({ | ||
modifyReadResultFns: modifyReadResponseFns.added, | ||
localStoreFns: doOnStreamFns.added, | ||
payload: result.payload, | ||
docMetaData: result.meta | ||
}); | ||
}, | ||
modified: async (_payload, _meta) => { | ||
const docIdentifier = `${collectionPath}/${_meta.id}`; | ||
const result = await getDocAfterWritelock({ | ||
writeLockMap, | ||
docIdentifier, | ||
lastIncomingDocs, | ||
meta: _meta, | ||
payload: _payload | ||
}); | ||
if (!result) | ||
return; | ||
return executeOnFns({ | ||
modifyReadResultFns: modifyReadResponseFns.added, | ||
localStoreFns: doOnStreamFns.added, | ||
payload: result.payload, | ||
docMetaData: result.meta | ||
}); | ||
}, | ||
removed: async (_payload, _meta) => { | ||
const docIdentifier = `${collectionPath}/${_meta.id}`; | ||
lastIncomingDocs.delete(docIdentifier); | ||
await writeLockPromise(writeLockMap, docIdentifier); | ||
return executeOnFns({ | ||
modifyReadResultFns: modifyReadResponseFns.removed, | ||
localStoreFns: doOnStreamFns.removed, | ||
payload: _payload, | ||
docMetaData: _meta | ||
}); | ||
} | ||
}; | ||
for (const storeName of storesToExecute) { | ||
const pluginAction = globalConfig.stores[storeName].actions["stream"]; | ||
const pluginModuleConfig = getPluginModuleConfig(moduleConfig, storeName); | ||
if (pluginAction) { | ||
const result = await handleStream({ | ||
collectionPath, | ||
docId, | ||
pluginModuleConfig, | ||
pluginAction, | ||
actionConfig, | ||
payload, | ||
// should always use the payload as passed originally for clarity | ||
eventNameFnsMap, | ||
actionName: "stream", | ||
storeName, | ||
mustExecuteOnRead | ||
}); | ||
if (isDoOnStream(result)) { | ||
for (const [doOn, doFn] of Object.entries(result)) { | ||
if (doFn) | ||
doOnStreamFns[doOn].push(doFn); | ||
} | ||
} | ||
if (!isDoOnStream(result)) { | ||
streamInfoPerStore[storeName] = result; | ||
} | ||
} | ||
} | ||
throwOnIncompleteStreamResponses(streamInfoPerStore, doOnStreamFns); | ||
const closeStream = () => { | ||
Object.values(streamInfoPerStore).forEach(({ stop }) => stop()); | ||
cacheStream(() => { | ||
}, null); | ||
}; | ||
const streamPromises = Object.values(streamInfoPerStore).map((res) => res.streaming); | ||
const streamPromise = new Promise((resolve, reject) => { | ||
Promise.all(streamPromises).then(() => { | ||
resolve(); | ||
closeStream(); | ||
}).catch((e) => { | ||
reject(e); | ||
closeStream(); | ||
}); | ||
}); | ||
cacheStream(closeStream, streamPromise); | ||
return streamPromise; | ||
}; | ||
} | ||
// src/Collection.ts | ||
function createCollectionWithContext(collectionPath, moduleConfig, globalConfig, docFn, collectionFn, streamAndFetchPromises, fetchMeta) { | ||
const { writeLockMap, fetchPromises, cacheStream, streaming, closeStream, closeAllStreams } = streamAndFetchPromises; | ||
const id = collectionPath.split("/").slice(-1)[0]; | ||
const path = collectionPath; | ||
const doc = (docId, _moduleConfig = {}) => { | ||
return docFn(`${path}/${docId}`, (0, import_merge_anything.merge)(moduleConfig, _moduleConfig)); | ||
}; | ||
const sharedParams = { | ||
collectionPath, | ||
_docId: void 0, | ||
moduleConfig, | ||
globalConfig, | ||
fetchPromises, | ||
writeLockMap, | ||
docFn, | ||
collectionFn, | ||
setLastFetched: fetchMeta.set | ||
}; | ||
const insert = handleActionPerStore(sharedParams, "insert", import_types.actionNameTypeMap.insert); | ||
const _delete = handleActionPerStore(sharedParams, "delete", import_types.actionNameTypeMap.delete); | ||
const fetch = handleActionPerStore(sharedParams, "fetch", import_types.actionNameTypeMap.fetch); | ||
const fetchCount = handleActionPerStore(sharedParams, "fetchCount", import_types.actionNameTypeMap.fetchCount); | ||
const stream = handleStreamPerStore([collectionPath, void 0], moduleConfig, globalConfig, import_types.actionNameTypeMap.stream, streaming, cacheStream, writeLockMap); | ||
const actions = { stream, fetch, fetchCount, insert, delete: _delete }; | ||
executeSetupModulePerStore(globalConfig.stores, [collectionPath, void 0], moduleConfig); | ||
function query(query2) { | ||
const moduleConfigWithClause = (0, import_merge_anything.mergeAndConcat)(moduleConfig, { query: [query2] }); | ||
return collectionFn(path, moduleConfigWithClause); | ||
} | ||
function where(fieldPath, operator, value) { | ||
const whereClause = [fieldPath, operator, value]; | ||
const moduleConfigWithClause = (0, import_merge_anything.mergeAndConcat)(moduleConfig, { where: [whereClause] }); | ||
return collectionFn(path, moduleConfigWithClause); | ||
} | ||
function orderBy(fieldPath, direction = "asc") { | ||
const orderByClause = [fieldPath, direction]; | ||
const moduleConfigWithClause = (0, import_merge_anything.mergeAndConcat)(moduleConfig, { orderBy: [orderByClause] }); | ||
return collectionFn(path, moduleConfigWithClause); | ||
} | ||
function limit(limitCount) { | ||
return collectionFn(path, { ...moduleConfig, limit: limitCount }); | ||
} | ||
function startAfter(...values) { | ||
if (values[0] === void 0) | ||
return collectionFn(path, moduleConfig); | ||
const isDoc = values[0] && typeof values[0] === "object"; | ||
return collectionFn(path, { | ||
...moduleConfig, | ||
startAfter: isDoc ? values[0] : values | ||
}); | ||
} | ||
const queryFns = { query, where, orderBy, limit, startAfter }; | ||
const moduleInstance = { | ||
doc, | ||
id, | ||
path, | ||
streaming, | ||
closeStream, | ||
closeAllStreams, | ||
...actions, | ||
...queryFns | ||
}; | ||
return proxify(moduleInstance, { | ||
count: () => getCountFromDataStore(moduleConfig, globalConfig, collectionPath), | ||
data: () => getDataFromDataStore(moduleConfig, globalConfig, collectionPath), | ||
fetched: fetchMeta.get | ||
}); | ||
} | ||
// src/Doc.ts | ||
var import_types2 = require("@magnetarjs/types"); | ||
function createDocWithContext([collectionPath, docId], moduleConfig, globalConfig, docFn, collectionFn, streamAndFetchPromises) { | ||
const { writeLockMap, fetchPromises, cacheStream, streaming, closeStream } = streamAndFetchPromises; | ||
const path = [collectionPath, docId].join("/"); | ||
const collection = (collectionId, _moduleConfig = {}) => { | ||
return collectionFn(`${path}/${collectionId}`, _moduleConfig); | ||
}; | ||
const sharedParams = { | ||
collectionPath, | ||
_docId: docId, | ||
moduleConfig, | ||
globalConfig, | ||
fetchPromises, | ||
writeLockMap, | ||
docFn | ||
}; | ||
const actions = { | ||
insert: handleActionPerStore(sharedParams, "insert", import_types2.actionNameTypeMap.insert), | ||
// prettier-ignore | ||
merge: handleActionPerStore(sharedParams, "merge", import_types2.actionNameTypeMap.merge), | ||
// prettier-ignore | ||
assign: handleActionPerStore(sharedParams, "assign", import_types2.actionNameTypeMap.assign), | ||
// prettier-ignore | ||
replace: handleActionPerStore(sharedParams, "replace", import_types2.actionNameTypeMap.replace), | ||
// prettier-ignore | ||
deleteProp: handleActionPerStore(sharedParams, "deleteProp", import_types2.actionNameTypeMap.deleteProp), | ||
// prettier-ignore | ||
delete: handleActionPerStore(sharedParams, "delete", import_types2.actionNameTypeMap.delete), | ||
// prettier-ignore | ||
fetch: handleActionPerStore(sharedParams, "fetch", import_types2.actionNameTypeMap.fetch), | ||
// prettier-ignore | ||
stream: handleStreamPerStore([collectionPath, docId], moduleConfig, globalConfig, import_types2.actionNameTypeMap.stream, streaming, cacheStream, writeLockMap) | ||
// prettier-ignore | ||
}; | ||
executeSetupModulePerStore(globalConfig.stores, [collectionPath, docId], moduleConfig); | ||
const moduleInstance = { | ||
collection, | ||
id: docId, | ||
path, | ||
streaming, | ||
closeStream, | ||
...actions | ||
}; | ||
return proxify(moduleInstance, { | ||
data: () => getDataFromDataStore(moduleConfig, globalConfig, collectionPath, docId), | ||
exists: () => getExistsFromDataStore(globalConfig, collectionPath, docId) | ||
}); | ||
} | ||
// src/helpers/configHelpers.ts | ||
var import_merge_anything2 = require("merge-anything"); | ||
function defaultsGlobalConfig(config) { | ||
const defaults = { | ||
localStoreName: "", | ||
stores: {}, | ||
executionOrder: { | ||
read: [], | ||
write: [] | ||
}, | ||
onError: "revert", | ||
on: {}, | ||
modifyPayloadOn: {}, | ||
modifyReadResponseOn: {} | ||
}; | ||
const merged = (0, import_merge_anything2.merge)(defaults, config); | ||
return merged; | ||
} | ||
// src/Magnetar.ts | ||
function Magnetar(magnetarConfig) { | ||
const globalConfig = defaultsGlobalConfig(magnetarConfig); | ||
const collectionNames = /* @__PURE__ */ new Set(); | ||
const writeLockMap = /* @__PURE__ */ new Map(); | ||
const closeStreamFnMap = /* @__PURE__ */ new Map(); | ||
const streamingPromiseMap = /* @__PURE__ */ new Map(); | ||
const fetchPromiseMap = /* @__PURE__ */ new Map(); | ||
const fetchMetaMap = /* @__PURE__ */ new Map(); | ||
async function clearAllData(options) { | ||
for (const collectionName of collectionNames) { | ||
if (options?.exclude?.includes(collectionName)) | ||
continue; | ||
collection(collectionName).data?.clear(); | ||
} | ||
} | ||
async function _closeAllStreams(options) { | ||
for (const collectionName of collectionNames) { | ||
if (options?.exclude?.includes(collectionName)) | ||
continue; | ||
collection(collectionName).closeAllStreams(); | ||
} | ||
} | ||
function getModuleInstance(modulePath, moduleConfig = {}, moduleType, docFn, collectionFn) { | ||
throwIfInvalidModulePath(modulePath, moduleType); | ||
const [collectionPath, docId] = getCollectionPathDocIdEntry(modulePath); | ||
collectionNames.add(collectionPath); | ||
const pathFilterIdentifier = (0, import_types3.getPathFilterIdentifier)(modulePath, moduleConfig); | ||
const fetchPromises = (0, import_getorset_anything2.mapGetOrSet)( | ||
fetchPromiseMap, | ||
pathFilterIdentifier, | ||
() => /* @__PURE__ */ new Map() | ||
); | ||
const pathWhereOrderByIdentifier = (0, import_types3.getPathWhereOrderByIdentifier)(modulePath, moduleConfig); | ||
const fetchMeta = { | ||
get: () => fetchMetaMap.get(pathWhereOrderByIdentifier) || { reachedEnd: false, cursor: void 0 }, | ||
set: (payload) => fetchMetaMap.set(pathWhereOrderByIdentifier, payload) | ||
}; | ||
function cacheStream(closeStreamFn, streamingPromise) { | ||
closeStreamFnMap.set(pathFilterIdentifier, closeStreamFn); | ||
streamingPromiseMap.set(pathFilterIdentifier, streamingPromise); | ||
} | ||
function streaming() { | ||
return streamingPromiseMap.get(pathFilterIdentifier) || null; | ||
} | ||
function closeStream() { | ||
const closeStreamFn = closeStreamFnMap.get(pathFilterIdentifier); | ||
if (closeStreamFn) { | ||
closeStreamFn(); | ||
setTimeout(() => { | ||
streamingPromiseMap.delete(pathFilterIdentifier); | ||
closeStreamFnMap.delete(pathFilterIdentifier); | ||
}); | ||
} | ||
} | ||
function closeAllStreams() { | ||
for (const [identifier, closeStreamFn] of closeStreamFnMap) { | ||
const openStreamPath = identifier.split(import_types3.MODULE_IDENTIFIER_SPLIT)[0]; | ||
if (openStreamPath === modulePath || openStreamPath.startsWith(modulePath + "/")) { | ||
closeStreamFn(); | ||
} | ||
} | ||
} | ||
const streamAndFetchPromises = { | ||
writeLockMap, | ||
fetchPromises, | ||
cacheStream, | ||
streaming, | ||
closeStream, | ||
closeAllStreams | ||
}; | ||
if (moduleType === "doc" && (0, import_is_what7.isString)(docId)) { | ||
return createDocWithContext( | ||
[collectionPath, docId], | ||
moduleConfig, | ||
globalConfig, | ||
docFn, | ||
collectionFn, | ||
streamAndFetchPromises | ||
); | ||
} | ||
return createCollectionWithContext( | ||
collectionPath, | ||
moduleConfig, | ||
globalConfig, | ||
docFn, | ||
collectionFn, | ||
streamAndFetchPromises, | ||
fetchMeta | ||
); | ||
} | ||
function collection(modulePath, moduleConfig = {}) { | ||
return getModuleInstance(modulePath, moduleConfig, "collection", doc, collection); | ||
} | ||
function doc(modulePath, moduleConfig = {}) { | ||
return getModuleInstance(modulePath, moduleConfig, "doc", doc, collection); | ||
} | ||
const instance = { | ||
globalConfig, | ||
collection, | ||
doc, | ||
clearAllData, | ||
closeAllStreams: _closeAllStreams | ||
}; | ||
return instance; | ||
} | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
Magnetar, | ||
getCollectionPathDocIdEntry, | ||
getCollectionWriteLocks, | ||
...require("@magnetarjs/types") | ||
}); | ||
export * from '@magnetarjs/types'; | ||
export * from './Magnetar.js'; | ||
export * from './helpers/pathHelpers.js'; |
{ | ||
"name": "@magnetarjs/core", | ||
"version": "0.20.1", | ||
"version": "1.0.1", | ||
"type": "module", | ||
"sideEffects": false, | ||
"description": "Magnetar core library.", | ||
"main": "dist/index.js", | ||
"module": "dist/index.mjs", | ||
"types": "dist/index.d.ts", | ||
"exports": { | ||
".": { | ||
"import": "./dist/index.mjs", | ||
"default": "./dist/index.js" | ||
} | ||
".": "./dist/index.js" | ||
}, | ||
"engines": { | ||
"node": ">=18" | ||
}, | ||
"files": [ | ||
@@ -23,11 +21,8 @@ "dist" | ||
"dependencies": { | ||
"getorset-anything": "^0.0.5", | ||
"is-what": "^4.1.16", | ||
"getorset-anything": "^0.1.0", | ||
"is-what": "^5.0.0", | ||
"merge-anything": "^5.1.7", | ||
"@magnetarjs/utils": "0.20.1", | ||
"@magnetarjs/types": "0.20.1" | ||
"@magnetarjs/types": "1.0.1", | ||
"@magnetarjs/utils": "1.0.1" | ||
}, | ||
"devDependencies": { | ||
"@magnetarjs/test-utils": "0.20.1" | ||
}, | ||
"keywords": [ | ||
@@ -65,19 +60,8 @@ "vuex-easy-firestore", | ||
}, | ||
"ava": { | ||
"extensions": [ | ||
"ts" | ||
], | ||
"require": [ | ||
"esbuild-register" | ||
], | ||
"timeout": "60s" | ||
}, | ||
"scripts": { | ||
"typecheck": "tsc --noEmit", | ||
"build": "tsup src/index.ts --clean --format esm,cjs --dts", | ||
"build": "del-cli dist && tsc", | ||
"dev": "pnpm build --watch", | ||
"test-and-build": "npm run test && npm run build", | ||
"test": "ava", | ||
"test--only": "ava --match='*only:*'" | ||
"test": "vitest run" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
40
0
Yes
70135
1397
+ Added@magnetarjs/types@1.0.1(transitive)
+ Added@magnetarjs/utils@1.0.1(transitive)
+ Addedgetorset-anything@0.1.0(transitive)
+ Addedis-what@5.0.2(transitive)
- Removed@magnetarjs/types@0.20.1(transitive)
- Removed@magnetarjs/utils@0.20.1(transitive)
- Removedgetorset-anything@0.0.5(transitive)
Updated@magnetarjs/types@1.0.1
Updated@magnetarjs/utils@1.0.1
Updatedgetorset-anything@^0.1.0
Updatedis-what@^5.0.0