@defer/client
Advanced tools
Comparing version 1.11.0-alpha-20230919170114-b9c4a8e to 1.11.0
325
cjs/index.js
@@ -29,14 +29,30 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getExecutionTries = exports.cancelExecution = exports.getExecution = exports.awaitResult = exports.discardAfter = exports.addMetadata = exports.delay = exports.defer = exports.deferEnabled = exports.__database = void 0; | ||
exports.getExecutionTries = exports.cancelExecution = exports.getExecution = exports.awaitResult = exports.addMetadata = exports.delay = exports.defer = exports.deferEnabled = exports.__database = void 0; | ||
const parse_duration_1 = __importDefault(require("parse-duration")); | ||
const constants_js_1 = require("./constants.js"); | ||
const client = __importStar(require("./client.js")); | ||
const constants_js_1 = require("./constants.js"); | ||
const errors_js_1 = require("./errors.js"); | ||
const httpClient_js_1 = require("./httpClient.js"); | ||
const utils_js_1 = require("./utils.js"); | ||
// Although the function implementation may not be completely secure, | ||
// it is suitable for local use. | ||
const randomUUID = () => { | ||
return URL.createObjectURL(new Blob([])).slice(-36); | ||
}; | ||
const withDelay = (dt, delay) => new Date(dt.getTime() + (0, parse_duration_1.default)(delay)); | ||
exports.__database = new Map(); | ||
function debug(...args) { | ||
if (getEnv("DEFER_DEBUG")) { | ||
console.debug(...args); | ||
} | ||
} | ||
function getEnv(key) { | ||
if (typeof process !== "undefined") | ||
return process.env[key]; | ||
if (typeof globalThis !== "undefined") | ||
return globalThis[key]; | ||
return; | ||
} | ||
function getHTTPClient() { | ||
const accessToken = (0, utils_js_1.getEnv)("DEFER_TOKEN"); | ||
const endpoint = (0, utils_js_1.getEnv)("DEFER_ENDPOINT") || "https://api.defer.run"; | ||
const accessToken = getEnv("DEFER_TOKEN"); | ||
const endpoint = getEnv("DEFER_ENDPOINT") || "https://api.defer.run"; | ||
if (accessToken) | ||
@@ -46,3 +62,3 @@ return (0, httpClient_js_1.makeHTTPClient)(endpoint, accessToken); | ||
} | ||
const deferEnabled = () => !!(0, utils_js_1.getEnv)("DEFER_TOKEN"); | ||
const deferEnabled = () => !!process?.env["DEFER_TOKEN"]; | ||
exports.deferEnabled = deferEnabled; | ||
@@ -77,38 +93,2 @@ async function execLocally(id, fn, args) { | ||
} | ||
async function enqueue(func, ...args) { | ||
const originalFunction = func.__fn; | ||
const functionArguments = (0, utils_js_1.sanitizeFunctionArguments)(args); | ||
(0, utils_js_1.debug)(`[defer.run][${originalFunction.name}] invoked.`); | ||
const httpClient = getHTTPClient(); | ||
if (httpClient) { | ||
const request = { | ||
name: originalFunction.name, | ||
arguments: functionArguments, | ||
scheduleFor: new Date(), | ||
metadata: func.__execOptions?.metadata || {}, | ||
}; | ||
const delay = func.__execOptions?.delay; | ||
if (delay instanceof Date) { | ||
request.scheduleFor = delay; | ||
} | ||
else if (delay) { | ||
const now = new Date(); | ||
request.scheduleFor = withDelay(now, delay); | ||
} | ||
const after = func.__execOptions?.discardAfter; | ||
if (after instanceof Date) { | ||
request.discardAfter = after; | ||
} | ||
else if (after) { | ||
const now = new Date(); | ||
request.discardAfter = withDelay(now, after); | ||
} | ||
return client.enqueueExecution(httpClient, request); | ||
} | ||
(0, utils_js_1.debug)(`[defer.run][${originalFunction.name}] defer ignore, no token found.`); | ||
const id = (0, utils_js_1.randomUUID)(); | ||
exports.__database.set(id, { id: id, state: "started" }); | ||
execLocally(id, originalFunction, functionArguments); | ||
return { id }; | ||
} | ||
function defaultRetryPolicy() { | ||
@@ -163,95 +143,205 @@ return { | ||
} | ||
function defer(fn, config) { | ||
const wrapped = async function (...args) { | ||
return enqueue(wrapped, ...args); | ||
const defer = (fn, options) => { | ||
const ret = async (...args) => { | ||
debug(`[defer.run][${fn.name}] invoked.`); | ||
let functionArguments; | ||
try { | ||
functionArguments = JSON.parse(JSON.stringify(args)); | ||
} | ||
catch (error) { | ||
const e = error; | ||
throw new errors_js_1.DeferError(`cannot serialize argument: ${e.message}`); | ||
} | ||
const httpClient = getHTTPClient(); | ||
if (httpClient) { | ||
return client.enqueueExecution(httpClient, { | ||
name: fn.name, | ||
arguments: functionArguments, | ||
scheduleFor: new Date(), | ||
metadata: {}, | ||
}); | ||
} | ||
debug(`[defer.run][${fn.name}] defer ignore, no token found.`); | ||
const id = randomUUID(); | ||
exports.__database.set(id, { id: id, state: "started" }); | ||
execLocally(id, fn, functionArguments); | ||
return { id }; | ||
}; | ||
wrapped.__fn = fn; | ||
wrapped.__metadata = { | ||
ret.__fn = fn; | ||
ret.__metadata = { | ||
version: constants_js_1.INTERNAL_VERSION, | ||
retry: parseRetryPolicy(config), | ||
concurrency: config?.concurrency, | ||
maxDuration: config?.maxDuration, | ||
retry: parseRetryPolicy(options), | ||
concurrency: options?.concurrency, | ||
maxDuration: options?.maxDuration, | ||
}; | ||
return wrapped; | ||
} | ||
return ret; | ||
}; | ||
exports.defer = defer; | ||
defer.cron = function (fn, cronExpr, config) { | ||
const wrapped = async function (...args) { | ||
return enqueue(wrapped, ...args); | ||
exports.defer.cron = (fn, schedule, options) => { | ||
const ret = () => { | ||
throw new Error("`defer.cron()` functions should not be invoked."); | ||
}; | ||
wrapped.__fn = fn; | ||
wrapped.__metadata = { | ||
ret.__fn = fn; | ||
ret.__metadata = { | ||
version: constants_js_1.INTERNAL_VERSION, | ||
retry: parseRetryPolicy(config), | ||
cron: cronExpr, | ||
concurrency: config?.concurrency, | ||
maxDuration: config?.maxDuration, | ||
cron: schedule, | ||
retry: parseRetryPolicy(options), | ||
concurrency: options?.concurrency, | ||
maxDuration: options?.maxDuration, | ||
}; | ||
return wrapped; | ||
return ret; | ||
}; | ||
function delay(fn, delay) { | ||
const wrapped = async function (...args) { | ||
return enqueue(wrapped, ...args); | ||
}; | ||
wrapped.__fn = fn.__fn; | ||
wrapped.__metadata = fn.__metadata; | ||
wrapped.__execOptions = { ...fn.__execOptions, delay }; | ||
return wrapped; | ||
} | ||
exports.delay = delay; | ||
function addMetadata(fn, metadata) { | ||
const gatheredMetadata = { ...fn.__execOptions?.metadata, ...metadata }; | ||
const wrapped = async function (...args) { | ||
return enqueue(wrapped, ...args); | ||
}; | ||
wrapped.__fn = fn.__fn; | ||
wrapped.__metadata = fn.__metadata; | ||
wrapped.__execOptions = { ...fn.__execOptions, metadata: gatheredMetadata }; | ||
return wrapped; | ||
} | ||
exports.addMetadata = addMetadata; | ||
function discardAfter(fn, value) { | ||
const wrapped = async function (...args) { | ||
return enqueue(wrapped, ...args); | ||
}; | ||
wrapped.__fn = fn.__fn; | ||
wrapped.__metadata = fn.__metadata; | ||
wrapped.__execOptions = { ...fn.__execOptions, discardAfter: value }; | ||
return wrapped; | ||
} | ||
exports.discardAfter = discardAfter; | ||
function awaitResult(fn) { | ||
return async function (...args) { | ||
const originalFunction = fn.__fn; | ||
const functionArguments = (0, utils_js_1.sanitizeFunctionArguments)(args); | ||
/** | ||
* Delay the execution of a background function | ||
* @constructor | ||
* @param {Function} deferFn - A background function (`defer(...)` result) | ||
* @param {string|Date} delay - The delay (ex: "1h" or a Date object) | ||
* @returns Function | ||
*/ | ||
const delay = (deferFn, delay) => { | ||
const delayedDeferFn = async (...args) => { | ||
const fn = deferFn.__fn; | ||
let functionArguments; | ||
try { | ||
functionArguments = JSON.parse(JSON.stringify(args)); | ||
} | ||
catch (error) { | ||
const e = error; | ||
throw new errors_js_1.DeferError(`cannot serialize argument: ${e.message}`); | ||
} | ||
debug(`[defer.run][${fn.name}] invoked.`); | ||
const httpClient = getHTTPClient(); | ||
let response; | ||
if (httpClient) { | ||
const { id } = await client.enqueueExecution(httpClient, { | ||
name: originalFunction.name, | ||
let scheduleFor; | ||
if (delay instanceof Date) { | ||
scheduleFor = delay; | ||
} | ||
else { | ||
const now = new Date(); | ||
scheduleFor = withDelay(now, delay); | ||
} | ||
return client.enqueueExecution(httpClient, { | ||
name: fn.name, | ||
arguments: functionArguments, | ||
scheduleFor: new Date(), | ||
metadata: {}, | ||
scheduleFor, | ||
metadata: deferFn.__execOptions?.metadata || {}, | ||
}); | ||
response = await client.waitExecutionResult(httpClient, { id: id }); | ||
} | ||
else { | ||
const id = (0, utils_js_1.randomUUID)(); | ||
exports.__database.set(id, { id: id, state: "started" }); | ||
response = await execLocally(id, originalFunction, functionArguments); | ||
debug(`[defer.run][${fn.name}] defer ignore, no token found.`); | ||
const id = randomUUID(); | ||
exports.__database.set(id, { id: id, state: "started" }); | ||
execLocally(id, fn, functionArguments); | ||
return { id }; | ||
}; | ||
delayedDeferFn.__fn = deferFn.__fn; | ||
delayedDeferFn.__metadata = deferFn.__metadata; | ||
delayedDeferFn.__execOptions = { | ||
...deferFn.__execOptions, | ||
delay, | ||
}; | ||
return delayedDeferFn; | ||
}; | ||
exports.delay = delay; | ||
/** | ||
* Add metadata to the the execution of a background function | ||
* @constructor | ||
* @param {Function} deferFn - A background function (`defer(...)` result) | ||
* @param {Metadata} metadata - The metadata (ex: `{ foo: "bar" }`) | ||
* @returns Function | ||
*/ | ||
const addMetadata = (deferFn, metadata) => { | ||
const newMetadata = { ...deferFn.__execOptions?.metadata, ...metadata }; | ||
const deferFnWithMetadata = async (...args) => { | ||
const fn = deferFn.__fn; | ||
let functionArguments; | ||
try { | ||
functionArguments = JSON.parse(JSON.stringify(args)); | ||
} | ||
if (response.state === "failed") { | ||
let error = new errors_js_1.DeferError("Defer execution failed"); | ||
if (response.result?.message) { | ||
error = new errors_js_1.DeferError(response.result.message); | ||
error.stack = response.result.stack; | ||
catch (error) { | ||
const e = error; | ||
throw new errors_js_1.DeferError(`cannot serialize argument: ${e.message}`); | ||
} | ||
debug(`[defer.run][${fn.name}] invoked.`); | ||
const httpClient = getHTTPClient(); | ||
if (httpClient) { | ||
let scheduleFor; | ||
const delay = deferFn.__execOptions?.delay; | ||
if (delay instanceof Date) { | ||
scheduleFor = delay; | ||
} | ||
else if (response.result) { | ||
error = response.result; | ||
else if (delay) { | ||
const now = new Date(); | ||
scheduleFor = withDelay(now, delay); | ||
} | ||
throw error; | ||
else { | ||
scheduleFor = new Date(); | ||
} | ||
return client.enqueueExecution(httpClient, { | ||
name: fn.name, | ||
arguments: functionArguments, | ||
scheduleFor, | ||
metadata: newMetadata, | ||
}); | ||
} | ||
return response.result; | ||
debug(`[defer.run][${fn.name}] defer ignore, no token found.`); | ||
const id = randomUUID(); | ||
exports.__database.set(id, { id: id, state: "started" }); | ||
execLocally(id, fn, functionArguments); | ||
return { id }; | ||
}; | ||
} | ||
deferFnWithMetadata.__fn = deferFn.__fn; | ||
deferFnWithMetadata.__metadata = deferFn.__metadata; | ||
deferFnWithMetadata.__execOptions = { | ||
...deferFn.__execOptions, | ||
metadata: newMetadata, | ||
}; | ||
return deferFnWithMetadata; | ||
}; | ||
exports.addMetadata = addMetadata; | ||
/** | ||
* Trigger the execution of a background function and waits for its result | ||
* @constructor | ||
* @param {Function} deferFn - A background function (`defer(...)` result) | ||
* @returns Function | ||
*/ | ||
const awaitResult = (deferFn) => async (...args) => { | ||
const fnName = deferFn.__fn.name; | ||
const fn = deferFn.__fn; | ||
let functionArguments; | ||
try { | ||
functionArguments = JSON.parse(JSON.stringify(args)); | ||
} | ||
catch (error) { | ||
const e = error; | ||
throw new errors_js_1.DeferError(`cannot serialize argument: ${e.message}`); | ||
} | ||
let response; | ||
const httpClient = getHTTPClient(); | ||
if (httpClient) { | ||
const { id } = await client.enqueueExecution(httpClient, { | ||
name: fnName, | ||
arguments: functionArguments, | ||
scheduleFor: new Date(), | ||
metadata: {}, | ||
}); | ||
response = await client.waitExecutionResult(httpClient, { id: id }); | ||
} | ||
else { | ||
const id = randomUUID(); | ||
exports.__database.set(id, { id: id, state: "started" }); | ||
response = await execLocally(id, fn, functionArguments); | ||
} | ||
if (response.state === "failed") { | ||
let error = new errors_js_1.DeferError("Defer execution failed"); | ||
if (response.result?.message) { | ||
error = new errors_js_1.DeferError(response.result.message); | ||
error.stack = response.result.stack; | ||
} | ||
else if (response.result) { | ||
error = response.result; | ||
} | ||
throw error; | ||
} | ||
return response.result; | ||
}; | ||
exports.awaitResult = awaitResult; | ||
@@ -262,2 +352,3 @@ async function getExecution(id) { | ||
return client.fetchExecution(httpClient, { id }); | ||
console.log("getExecution", id); | ||
const response = exports.__database.get(id); | ||
@@ -264,0 +355,0 @@ if (response) |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.asNextRoute = void 0; | ||
const index_js_1 = require("../index.js"); | ||
const errors_js_1 = require("../errors.js"); | ||
const index_js_1 = require("../index.js"); | ||
const ResponseJSON = Response.json; | ||
@@ -7,0 +7,0 @@ function asNextRoute(deferFn, options) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.default = "1.11.0-alpha-20230919170114-b9c4a8e"; | ||
exports.default = "1.11.0"; |
314
esm/index.js
import parseDuration from "parse-duration"; | ||
import { INTERNAL_VERSION, RETRY_MAX_ATTEMPTS_PLACEHOLDER, } from "./constants.js"; | ||
import * as client from "./client.js"; | ||
import { INTERNAL_VERSION, RETRY_MAX_ATTEMPTS_PLACEHOLDER, } from "./constants.js"; | ||
import { APIError, DeferError } from "./errors.js"; | ||
import { makeHTTPClient } from "./httpClient.js"; | ||
import { debug, getEnv, randomUUID, sanitizeFunctionArguments, } from "./utils.js"; | ||
// Although the function implementation may not be completely secure, | ||
// it is suitable for local use. | ||
const randomUUID = () => { | ||
return URL.createObjectURL(new Blob([])).slice(-36); | ||
}; | ||
const withDelay = (dt, delay) => new Date(dt.getTime() + parseDuration(delay)); | ||
export const __database = new Map(); | ||
function debug(...args) { | ||
if (getEnv("DEFER_DEBUG")) { | ||
console.debug(...args); | ||
} | ||
} | ||
function getEnv(key) { | ||
if (typeof process !== "undefined") | ||
return process.env[key]; | ||
if (typeof globalThis !== "undefined") | ||
return globalThis[key]; | ||
return; | ||
} | ||
function getHTTPClient() { | ||
@@ -16,3 +32,3 @@ const accessToken = getEnv("DEFER_TOKEN"); | ||
} | ||
export const deferEnabled = () => !!getEnv("DEFER_TOKEN"); | ||
export const deferEnabled = () => !!process?.env["DEFER_TOKEN"]; | ||
async function execLocally(id, fn, args) { | ||
@@ -46,38 +62,2 @@ let state = "succeed"; | ||
} | ||
async function enqueue(func, ...args) { | ||
const originalFunction = func.__fn; | ||
const functionArguments = sanitizeFunctionArguments(args); | ||
debug(`[defer.run][${originalFunction.name}] invoked.`); | ||
const httpClient = getHTTPClient(); | ||
if (httpClient) { | ||
const request = { | ||
name: originalFunction.name, | ||
arguments: functionArguments, | ||
scheduleFor: new Date(), | ||
metadata: func.__execOptions?.metadata || {}, | ||
}; | ||
const delay = func.__execOptions?.delay; | ||
if (delay instanceof Date) { | ||
request.scheduleFor = delay; | ||
} | ||
else if (delay) { | ||
const now = new Date(); | ||
request.scheduleFor = withDelay(now, delay); | ||
} | ||
const after = func.__execOptions?.discardAfter; | ||
if (after instanceof Date) { | ||
request.discardAfter = after; | ||
} | ||
else if (after) { | ||
const now = new Date(); | ||
request.discardAfter = withDelay(now, after); | ||
} | ||
return client.enqueueExecution(httpClient, request); | ||
} | ||
debug(`[defer.run][${originalFunction.name}] defer ignore, no token found.`); | ||
const id = randomUUID(); | ||
__database.set(id, { id: id, state: "started" }); | ||
execLocally(id, originalFunction, functionArguments); | ||
return { id }; | ||
} | ||
function defaultRetryPolicy() { | ||
@@ -132,91 +112,202 @@ return { | ||
} | ||
export function defer(fn, config) { | ||
const wrapped = async function (...args) { | ||
return enqueue(wrapped, ...args); | ||
export const defer = (fn, options) => { | ||
const ret = async (...args) => { | ||
debug(`[defer.run][${fn.name}] invoked.`); | ||
let functionArguments; | ||
try { | ||
functionArguments = JSON.parse(JSON.stringify(args)); | ||
} | ||
catch (error) { | ||
const e = error; | ||
throw new DeferError(`cannot serialize argument: ${e.message}`); | ||
} | ||
const httpClient = getHTTPClient(); | ||
if (httpClient) { | ||
return client.enqueueExecution(httpClient, { | ||
name: fn.name, | ||
arguments: functionArguments, | ||
scheduleFor: new Date(), | ||
metadata: {}, | ||
}); | ||
} | ||
debug(`[defer.run][${fn.name}] defer ignore, no token found.`); | ||
const id = randomUUID(); | ||
__database.set(id, { id: id, state: "started" }); | ||
execLocally(id, fn, functionArguments); | ||
return { id }; | ||
}; | ||
wrapped.__fn = fn; | ||
wrapped.__metadata = { | ||
ret.__fn = fn; | ||
ret.__metadata = { | ||
version: INTERNAL_VERSION, | ||
retry: parseRetryPolicy(config), | ||
concurrency: config?.concurrency, | ||
maxDuration: config?.maxDuration, | ||
retry: parseRetryPolicy(options), | ||
concurrency: options?.concurrency, | ||
maxDuration: options?.maxDuration, | ||
}; | ||
return wrapped; | ||
} | ||
defer.cron = function (fn, cronExpr, config) { | ||
const wrapped = async function (...args) { | ||
return enqueue(wrapped, ...args); | ||
return ret; | ||
}; | ||
defer.cron = (fn, schedule, options) => { | ||
const ret = () => { | ||
throw new Error("`defer.cron()` functions should not be invoked."); | ||
}; | ||
wrapped.__fn = fn; | ||
wrapped.__metadata = { | ||
ret.__fn = fn; | ||
ret.__metadata = { | ||
version: INTERNAL_VERSION, | ||
retry: parseRetryPolicy(config), | ||
cron: cronExpr, | ||
concurrency: config?.concurrency, | ||
maxDuration: config?.maxDuration, | ||
cron: schedule, | ||
retry: parseRetryPolicy(options), | ||
concurrency: options?.concurrency, | ||
maxDuration: options?.maxDuration, | ||
}; | ||
return wrapped; | ||
return ret; | ||
}; | ||
export function delay(fn, delay) { | ||
const wrapped = async function (...args) { | ||
return enqueue(wrapped, ...args); | ||
}; | ||
wrapped.__fn = fn.__fn; | ||
wrapped.__metadata = fn.__metadata; | ||
wrapped.__execOptions = { ...fn.__execOptions, delay }; | ||
return wrapped; | ||
} | ||
export function addMetadata(fn, metadata) { | ||
const gatheredMetadata = { ...fn.__execOptions?.metadata, ...metadata }; | ||
const wrapped = async function (...args) { | ||
return enqueue(wrapped, ...args); | ||
}; | ||
wrapped.__fn = fn.__fn; | ||
wrapped.__metadata = fn.__metadata; | ||
wrapped.__execOptions = { ...fn.__execOptions, metadata: gatheredMetadata }; | ||
return wrapped; | ||
} | ||
export function discardAfter(fn, value) { | ||
const wrapped = async function (...args) { | ||
return enqueue(wrapped, ...args); | ||
}; | ||
wrapped.__fn = fn.__fn; | ||
wrapped.__metadata = fn.__metadata; | ||
wrapped.__execOptions = { ...fn.__execOptions, discardAfter: value }; | ||
return wrapped; | ||
} | ||
export function awaitResult(fn) { | ||
return async function (...args) { | ||
const originalFunction = fn.__fn; | ||
const functionArguments = sanitizeFunctionArguments(args); | ||
/** | ||
* Delay the execution of a background function | ||
* @constructor | ||
* @param {Function} deferFn - A background function (`defer(...)` result) | ||
* @param {string|Date} delay - The delay (ex: "1h" or a Date object) | ||
* @returns Function | ||
*/ | ||
export const delay = (deferFn, delay) => { | ||
const delayedDeferFn = async (...args) => { | ||
const fn = deferFn.__fn; | ||
let functionArguments; | ||
try { | ||
functionArguments = JSON.parse(JSON.stringify(args)); | ||
} | ||
catch (error) { | ||
const e = error; | ||
throw new DeferError(`cannot serialize argument: ${e.message}`); | ||
} | ||
debug(`[defer.run][${fn.name}] invoked.`); | ||
const httpClient = getHTTPClient(); | ||
let response; | ||
if (httpClient) { | ||
const { id } = await client.enqueueExecution(httpClient, { | ||
name: originalFunction.name, | ||
let scheduleFor; | ||
if (delay instanceof Date) { | ||
scheduleFor = delay; | ||
} | ||
else { | ||
const now = new Date(); | ||
scheduleFor = withDelay(now, delay); | ||
} | ||
return client.enqueueExecution(httpClient, { | ||
name: fn.name, | ||
arguments: functionArguments, | ||
scheduleFor: new Date(), | ||
metadata: {}, | ||
scheduleFor, | ||
metadata: deferFn.__execOptions?.metadata || {}, | ||
}); | ||
response = await client.waitExecutionResult(httpClient, { id: id }); | ||
} | ||
else { | ||
const id = randomUUID(); | ||
__database.set(id, { id: id, state: "started" }); | ||
response = await execLocally(id, originalFunction, functionArguments); | ||
debug(`[defer.run][${fn.name}] defer ignore, no token found.`); | ||
const id = randomUUID(); | ||
__database.set(id, { id: id, state: "started" }); | ||
execLocally(id, fn, functionArguments); | ||
return { id }; | ||
}; | ||
delayedDeferFn.__fn = deferFn.__fn; | ||
delayedDeferFn.__metadata = deferFn.__metadata; | ||
delayedDeferFn.__execOptions = { | ||
...deferFn.__execOptions, | ||
delay, | ||
}; | ||
return delayedDeferFn; | ||
}; | ||
/** | ||
* Add metadata to the the execution of a background function | ||
* @constructor | ||
* @param {Function} deferFn - A background function (`defer(...)` result) | ||
* @param {Metadata} metadata - The metadata (ex: `{ foo: "bar" }`) | ||
* @returns Function | ||
*/ | ||
export const addMetadata = (deferFn, metadata) => { | ||
const newMetadata = { ...deferFn.__execOptions?.metadata, ...metadata }; | ||
const deferFnWithMetadata = async (...args) => { | ||
const fn = deferFn.__fn; | ||
let functionArguments; | ||
try { | ||
functionArguments = JSON.parse(JSON.stringify(args)); | ||
} | ||
if (response.state === "failed") { | ||
let error = new DeferError("Defer execution failed"); | ||
if (response.result?.message) { | ||
error = new DeferError(response.result.message); | ||
error.stack = response.result.stack; | ||
catch (error) { | ||
const e = error; | ||
throw new DeferError(`cannot serialize argument: ${e.message}`); | ||
} | ||
debug(`[defer.run][${fn.name}] invoked.`); | ||
const httpClient = getHTTPClient(); | ||
if (httpClient) { | ||
let scheduleFor; | ||
const delay = deferFn.__execOptions?.delay; | ||
if (delay instanceof Date) { | ||
scheduleFor = delay; | ||
} | ||
else if (response.result) { | ||
error = response.result; | ||
else if (delay) { | ||
const now = new Date(); | ||
scheduleFor = withDelay(now, delay); | ||
} | ||
throw error; | ||
else { | ||
scheduleFor = new Date(); | ||
} | ||
return client.enqueueExecution(httpClient, { | ||
name: fn.name, | ||
arguments: functionArguments, | ||
scheduleFor, | ||
metadata: newMetadata, | ||
}); | ||
} | ||
return response.result; | ||
debug(`[defer.run][${fn.name}] defer ignore, no token found.`); | ||
const id = randomUUID(); | ||
__database.set(id, { id: id, state: "started" }); | ||
execLocally(id, fn, functionArguments); | ||
return { id }; | ||
}; | ||
} | ||
deferFnWithMetadata.__fn = deferFn.__fn; | ||
deferFnWithMetadata.__metadata = deferFn.__metadata; | ||
deferFnWithMetadata.__execOptions = { | ||
...deferFn.__execOptions, | ||
metadata: newMetadata, | ||
}; | ||
return deferFnWithMetadata; | ||
}; | ||
/** | ||
* Trigger the execution of a background function and waits for its result | ||
* @constructor | ||
* @param {Function} deferFn - A background function (`defer(...)` result) | ||
* @returns Function | ||
*/ | ||
export const awaitResult = (deferFn) => async (...args) => { | ||
const fnName = deferFn.__fn.name; | ||
const fn = deferFn.__fn; | ||
let functionArguments; | ||
try { | ||
functionArguments = JSON.parse(JSON.stringify(args)); | ||
} | ||
catch (error) { | ||
const e = error; | ||
throw new DeferError(`cannot serialize argument: ${e.message}`); | ||
} | ||
let response; | ||
const httpClient = getHTTPClient(); | ||
if (httpClient) { | ||
const { id } = await client.enqueueExecution(httpClient, { | ||
name: fnName, | ||
arguments: functionArguments, | ||
scheduleFor: new Date(), | ||
metadata: {}, | ||
}); | ||
response = await client.waitExecutionResult(httpClient, { id: id }); | ||
} | ||
else { | ||
const id = randomUUID(); | ||
__database.set(id, { id: id, state: "started" }); | ||
response = await execLocally(id, fn, functionArguments); | ||
} | ||
if (response.state === "failed") { | ||
let error = new DeferError("Defer execution failed"); | ||
if (response.result?.message) { | ||
error = new DeferError(response.result.message); | ||
error.stack = response.result.stack; | ||
} | ||
else if (response.result) { | ||
error = response.result; | ||
} | ||
throw error; | ||
} | ||
return response.result; | ||
}; | ||
export async function getExecution(id) { | ||
@@ -226,2 +317,3 @@ const httpClient = getHTTPClient(); | ||
return client.fetchExecution(httpClient, { id }); | ||
console.log("getExecution", id); | ||
const response = __database.get(id); | ||
@@ -228,0 +320,0 @@ if (response) |
@@ -0,3 +1,3 @@ | ||
import { getExecution } from "../index.js"; | ||
import { APIError } from "../errors.js"; | ||
import { getExecution } from "../index.js"; | ||
const ResponseJSON = Response.json; | ||
@@ -4,0 +4,0 @@ export function asNextRoute(deferFn, options) { |
@@ -1,1 +0,1 @@ | ||
export default "1.11.0-alpha-20230919170114-b9c4a8e"; | ||
export default "1.11.0"; |
{ | ||
"name": "@defer/client", | ||
"version": "1.11.0-alpha-20230919170114-b9c4a8e", | ||
"version": "1.11.0", | ||
"description": "Zero infrastructure NodeJS background jobs", | ||
@@ -5,0 +5,0 @@ "dependencies": { |
import type { HTTPClient } from "./httpClient.js"; | ||
import type { ExecutionMetadata } from "./index.js"; | ||
import type { Metadata } from "./index.js"; | ||
export interface EnqueueExecutionRequest { | ||
@@ -7,4 +7,3 @@ name: string; | ||
scheduleFor: Date; | ||
discardAfter?: Date; | ||
metadata: ExecutionMetadata; | ||
metadata: Metadata; | ||
} | ||
@@ -11,0 +10,0 @@ export interface EnqueueExecutionResponse { |
@@ -9,11 +9,12 @@ import { Units } from "parse-duration"; | ||
export declare const deferEnabled: () => boolean; | ||
export type Duration = `${string}${Units}`; | ||
export interface ExecutionMetadata { | ||
export type UnPromise<F> = F extends Promise<infer R> ? R : F; | ||
export type DelayString = `${string}${Units}`; | ||
export interface Metadata { | ||
[key: string]: string; | ||
} | ||
export interface DeferredFunctionOptions { | ||
delay?: Duration | Date; | ||
metadata?: ExecutionMetadata; | ||
discardAfter?: Duration | Date; | ||
export interface DeferExecutionOptions { | ||
delay?: DelayString | Date; | ||
metadata?: Metadata; | ||
} | ||
export type DeferRetFnParameters<F extends (...args: any | undefined) => Promise<any>> = [...first: Parameters<F>, options: DeferExecutionOptions]; | ||
type Enumerate<N extends number, Acc extends number[] = []> = Acc["length"] extends N ? Acc[number] : Enumerate<N, [...Acc, Acc["length"]]>; | ||
@@ -23,9 +24,27 @@ type Range<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>; | ||
export type NextRouteString = `/api/${string}`; | ||
export interface Manifest { | ||
version: number; | ||
cron?: string; | ||
retry?: RetryPolicy; | ||
concurrency?: Concurrency | undefined; | ||
maxDuration?: number | undefined; | ||
export interface HasDeferMetadata { | ||
__metadata: { | ||
version: number; | ||
cron?: string; | ||
retry?: RetryPolicy; | ||
concurrency?: Concurrency | undefined; | ||
maxDuration?: number | undefined; | ||
}; | ||
} | ||
export interface DeferRetFn<F extends (...args: any | undefined) => Promise<any>> extends HasDeferMetadata { | ||
(...args: Parameters<F>): Promise<client.EnqueueExecutionResponse>; | ||
__fn: F; | ||
__execOptions?: DeferExecutionOptions; | ||
} | ||
export interface DeferScheduledFn<F extends (...args: never) => Promise<any>> extends HasDeferMetadata { | ||
(...args: Parameters<F>): void; | ||
__fn: F; | ||
} | ||
export interface DeferAwaitRetFn<F extends (...args: any | undefined) => Promise<any>> { | ||
(...args: Parameters<F>): Promise<UnPromise<ReturnType<F>>>; | ||
} | ||
export interface Defer { | ||
<F extends (...args: any | undefined) => Promise<any>>(fn: F, options?: DeferOptions): DeferRetFn<F>; | ||
cron: <F extends (args: never[]) => Promise<any>>(fn: F, schedule: string, options?: DeferOptions) => DeferScheduledFn<F>; | ||
} | ||
export interface RetryPolicy { | ||
@@ -43,27 +62,35 @@ maxAttempts: number; | ||
} | ||
export type DeferableFunction = (...args: any) => Promise<any>; | ||
export interface ExecutionOptions { | ||
delay?: Duration | Date; | ||
metadata?: ExecutionMetadata; | ||
discardAfter?: Duration | Date; | ||
export declare const defer: Defer; | ||
interface DeferDelay { | ||
<F extends (...args: any | undefined) => Promise<any>>(deferFn: DeferRetFn<F>, delay: DelayString | Date): DeferRetFn<F>; | ||
} | ||
export interface DeferredFunction<F extends DeferableFunction> { | ||
(...args: Parameters<F>): Promise<client.EnqueueExecutionResponse>; | ||
__metadata: Manifest; | ||
__fn: F; | ||
__execOptions?: ExecutionOptions; | ||
/** | ||
* Delay the execution of a background function | ||
* @constructor | ||
* @param {Function} deferFn - A background function (`defer(...)` result) | ||
* @param {string|Date} delay - The delay (ex: "1h" or a Date object) | ||
* @returns Function | ||
*/ | ||
export declare const delay: DeferDelay; | ||
interface DeferAddMetadata { | ||
<F extends (...args: any | undefined) => Promise<any>>(deferFn: DeferRetFn<F>, metadata: Metadata): DeferRetFn<F>; | ||
} | ||
export interface DeferredFunctionConfiguration { | ||
retry?: boolean | number | Partial<RetryPolicy>; | ||
concurrency?: Concurrency; | ||
maxDuration?: number; | ||
/** | ||
* Add metadata to the the execution of a background function | ||
* @constructor | ||
* @param {Function} deferFn - A background function (`defer(...)` result) | ||
* @param {Metadata} metadata - The metadata (ex: `{ foo: "bar" }`) | ||
* @returns Function | ||
*/ | ||
export declare const addMetadata: DeferAddMetadata; | ||
interface DeferAwaitResult { | ||
<F extends (...args: any | undefined) => Promise<any>>(deferFn: DeferRetFn<F>): DeferAwaitRetFn<F>; | ||
} | ||
export declare function defer<F extends DeferableFunction>(fn: F, config?: DeferredFunctionConfiguration): DeferredFunction<F>; | ||
export declare namespace defer { | ||
var cron: (fn: DeferableFunction, cronExpr: string, config?: DeferredFunctionConfiguration | undefined) => DeferredFunction<DeferableFunction>; | ||
} | ||
export declare function delay<F extends DeferableFunction>(fn: DeferredFunction<F>, delay: Duration | Date): DeferredFunction<F>; | ||
export declare function addMetadata<F extends DeferableFunction>(fn: DeferredFunction<F>, metadata: ExecutionMetadata): DeferredFunction<F>; | ||
export declare function discardAfter<F extends DeferableFunction>(fn: DeferredFunction<F>, value: Duration | Date): DeferredFunction<F>; | ||
export declare function awaitResult<F extends DeferableFunction>(fn: DeferredFunction<F>): (...args: Parameters<F>) => Promise<Awaited<ReturnType<F>>>; | ||
/** | ||
* Trigger the execution of a background function and waits for its result | ||
* @constructor | ||
* @param {Function} deferFn - A background function (`defer(...)` result) | ||
* @returns Function | ||
*/ | ||
export declare const awaitResult: DeferAwaitResult; | ||
export declare function getExecution(id: string): Promise<client.FetchExecutionResponse>; | ||
@@ -70,0 +97,0 @@ export declare function cancelExecution(id: string, force?: boolean): Promise<client.CancelExecutionResponse>; |
import type { NextRequest, NextResponse } from "next/server"; | ||
import { DeferredFunction } from "../index.js"; | ||
import { type DeferRetFn } from "../index.js"; | ||
export interface DeferNextRoute { | ||
@@ -10,3 +10,3 @@ GetHandler: (request: NextRequest) => Promise<NextResponse | Response>; | ||
} | ||
export declare function asNextRoute<F extends (...args: any) => Promise<any>>(deferFn: DeferredFunction<F>, options?: Options<F>): DeferNextRoute; | ||
export declare function asNextRoute<F extends (...args: any) => Promise<any>>(deferFn: DeferRetFn<F>, options?: Options<F>): DeferNextRoute; | ||
export {}; |
@@ -1,2 +0,2 @@ | ||
import type { DeferredFunction } from "../index.js"; | ||
import type { DeferRetFn } from "../index.js"; | ||
export type UseDeferRoute<ARA extends boolean, A extends any[], R> = [ | ||
@@ -10,5 +10,5 @@ execute: (...args: ARA extends true ? any : A) => void, | ||
]; | ||
export declare const useDeferRoute: <DFR extends DeferredFunction<any> = any, F extends (...args: any[]) => Promise<any> = DFR extends DeferredFunction<infer RR extends import("../index.js").DeferableFunction> ? RR : any, HP extends boolean = false, R = ReturnType<F> extends Promise<infer RR_1> ? RR_1 : ReturnType<F>, A extends any[] = Parameters<F>>(routePath: string, { refreshInterval }?: { | ||
export declare const useDeferRoute: <DFR extends DeferRetFn<any> = any, F extends (...args: any[]) => Promise<any> = DFR extends DeferRetFn<infer RR extends (...args: any) => Promise<any>> ? RR : any, HP extends boolean = false, R = ReturnType<F> extends Promise<infer RR_1> ? RR_1 : ReturnType<F>, A extends any[] = Parameters<F>>(routePath: string, { refreshInterval }?: { | ||
hasProxy: HP; | ||
refreshInterval: number; | ||
}) => UseDeferRoute<HP, A, R>; |
@@ -1,2 +0,2 @@ | ||
declare const _default: "1.11.0-alpha-20230919170114-b9c4a8e"; | ||
declare const _default: "1.11.0"; | ||
export default _default; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances 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
67812
1517
1
48
5