@jini-ai/platform 0.6.15-rc.1 → 0.6.15-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/fetch-with-timeout.d.ts +23 -0
- package/dist/fetch-with-timeout.js +19 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/mail/adapter-module.d.ts +18 -0
- package/dist/mail/adapter-module.js +13 -1
- package/dist/mail/index.d.ts +1 -0
- package/dist/mail/index.js +1 -0
- package/dist/mail/provider.js +4 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -37,6 +37,7 @@ need a `PtySpawn` implementation supplied by the caller — this package defines
|
|
|
37
37
|
so profile-only `PATH` entries are visible).
|
|
38
38
|
- **Filesystem** — `pathContains`, `atomicCopyFile`, `removePathBestEffort`, `readLogTail`.
|
|
39
39
|
- **HTTP readiness** — `waitForHttpOk`.
|
|
40
|
+
- **Deadline signals** — `createDeadlineSignal({ waitMs }, { signal, startedAt, now })` from `@jini-ai/platform/fetch-with-timeout`; subtracts elapsed milliseconds using the optional `now: () => number` clock (default `Date.now`) and combines the remaining budget with caller cancellation.
|
|
40
41
|
- **Toolchain discovery** — `wellKnownUserToolchainBins` (npm/pnpm/bun/cargo/deno/go/pyenv,
|
|
41
42
|
asdf/volta/mise/nvm/fnm shims).
|
|
42
43
|
- **Proxy-aware env** — `resolveSystemProxyEnv`, `mergeProxyAwareEnv` (macOS `scutil` / Windows
|
|
@@ -221,6 +222,7 @@ vendor-shaped `NativeSmtpTransport` so the factory adapts the payload once. Poli
|
|
|
221
222
|
|
|
222
223
|
| Subpath | API and required host ports/policy |
|
|
223
224
|
| --- | --- |
|
|
225
|
+
| `./mail` | `createMailAdapterContext({ credential, kit }, { now, settings })` supplies a live clock and fresh empty settings by default; modules validate opaque per-install settings. |
|
|
224
226
|
| `@jini-ai/analytics` (separate package) | `ingestHit({ input, deps }, { hooks, hash })`, `normalizeIngestContext({ input, dailySalt }, { hash })`, `deriveDailySalt({ rootKeySeed, workspaceId, utcDate, saltContext })`. The host supplies sink/config, host resolution, HKDF context, and privacy bounds. `LocalBufferSink({}, { initialHits })` is process-local. |
|
|
225
227
|
| `@jini-ai/cms/trash` (separate package) | `createTrashService({ repo, adapters, transaction, idGen, entityPolicy }, { retentionDays, ...optional })`, `createTrashSweep({ repo, adapters, transaction, entityPolicy })`, and `startTrashSweeper({ sweep, clock, scheduler, leaseOwner }, optional)`. The host owns authorization, transactions, retention, scheduling, and adapters; follow-up hooks are in argument two. |
|
|
226
228
|
| `./fs` | `writeFileAtomic({ filePath, content, fs, tempName })`, `writeJsonFileAtomic({ filePath, data, fs, tempName })`, `upsertEnvLine({ source, key, value })`, `readEnvLine({ source, key })`, `resolvePathWithin({ root, segment })`. Atomic writes preserve existing modes and require filesystem/temp-name ports. |
|
|
@@ -89,6 +89,29 @@ export interface FetchWithTimeoutOptions {
|
|
|
89
89
|
/** Host-supplied transport; defaults to the global fetch. */
|
|
90
90
|
fetch?: typeof fetch;
|
|
91
91
|
}
|
|
92
|
+
/** Optional caller cancellation and elapsed-time accounting for a wait budget. */
|
|
93
|
+
export interface DeadlineSignalOptions {
|
|
94
|
+
signal?: AbortSignal;
|
|
95
|
+
/** Start time in milliseconds, on the same clock as `now`. */
|
|
96
|
+
startedAt?: number;
|
|
97
|
+
/** Clock port in milliseconds; defaults to Date.now and is read only with startedAt. */
|
|
98
|
+
now?: () => number;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Creates one signal that aborts when the remaining wait budget expires or the caller cancels.
|
|
102
|
+
* Spent time is subtracted once; future start times cannot increase the original budget.
|
|
103
|
+
* @param input - Wait budget in milliseconds. Negative remaining budgets become zero.
|
|
104
|
+
* @param options - Optional caller signal, start time and clock port.
|
|
105
|
+
* @returns A native deadline signal preserving the first abort reason. Zero expires asynchronously,
|
|
106
|
+
* following AbortSignal.timeout(0); an already-aborted caller is reflected immediately.
|
|
107
|
+
* Creates a native timeout without mutating inputs or the caller's signal.
|
|
108
|
+
* @throws {RangeError} If the remaining delay is invalid for AbortSignal.timeout.
|
|
109
|
+
* @complexity O(1) time and space.
|
|
110
|
+
* @example createDeadlineSignal({ waitMs: 5_000 }, { signal, startedAt, now: Date.now })
|
|
111
|
+
*/
|
|
112
|
+
export declare function createDeadlineSignal({ waitMs }: {
|
|
113
|
+
waitMs: number;
|
|
114
|
+
}, { signal, startedAt, now }?: DeadlineSignalOptions): AbortSignal;
|
|
92
115
|
/** Opt-in byte and decoding guards; defaults remain unlimited and replacement-decoded. */
|
|
93
116
|
export type TextReadOptions = {
|
|
94
117
|
/** Nonnegative safe integer. Actual streamed bytes, independent of Content-Length. */
|
|
@@ -87,6 +87,25 @@ export const FETCH_TIMEOUT_MS = {
|
|
|
87
87
|
* legitimately still-generating request early. */
|
|
88
88
|
GENERATE: 10 * 60_000,
|
|
89
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Creates one signal that aborts when the remaining wait budget expires or the caller cancels.
|
|
92
|
+
* Spent time is subtracted once; future start times cannot increase the original budget.
|
|
93
|
+
* @param input - Wait budget in milliseconds. Negative remaining budgets become zero.
|
|
94
|
+
* @param options - Optional caller signal, start time and clock port.
|
|
95
|
+
* @returns A native deadline signal preserving the first abort reason. Zero expires asynchronously,
|
|
96
|
+
* following AbortSignal.timeout(0); an already-aborted caller is reflected immediately.
|
|
97
|
+
* Creates a native timeout without mutating inputs or the caller's signal.
|
|
98
|
+
* @throws {RangeError} If the remaining delay is invalid for AbortSignal.timeout.
|
|
99
|
+
* @complexity O(1) time and space.
|
|
100
|
+
* @example createDeadlineSignal({ waitMs: 5_000 }, { signal, startedAt, now: Date.now })
|
|
101
|
+
*/
|
|
102
|
+
export function createDeadlineSignal({ waitMs }, { signal, startedAt, now = Date.now } = {}) {
|
|
103
|
+
const elapsedMs = startedAt === undefined ? 0 : Math.max(0, now() - startedAt);
|
|
104
|
+
const deadline = AbortSignal.timeout(Math.max(0, waitMs - elapsedMs));
|
|
105
|
+
if (signal === undefined)
|
|
106
|
+
return deadline;
|
|
107
|
+
return AbortSignal.any([deadline, signal]);
|
|
108
|
+
}
|
|
90
109
|
export async function readFetchResponseText(input, { maxResponseBytes, strictUtf8 = false } = {}) {
|
|
91
110
|
const details = "result" in input && input.result === "details";
|
|
92
111
|
const options = details ? input : { ...(maxResponseBytes === undefined ? {} : { maxResponseBytes }), strictUtf8 };
|
package/dist/index.d.ts
CHANGED
|
@@ -42,8 +42,8 @@ export type { AtomicCopyFileOptions, AtomicCopyFileResult, RemovePathBestEffortO
|
|
|
42
42
|
export { atomicCopyFile, pathContains, readLogTail, removePathBestEffort } from "./fs.js";
|
|
43
43
|
export type { HttpWaitOptions } from "./http.js";
|
|
44
44
|
export { waitForHttpOk } from "./http.js";
|
|
45
|
-
export type { FetchWithTimeoutOptions } from "./fetch-with-timeout.js";
|
|
46
|
-
export { FETCH_TIMEOUT_MS, FetchTimeoutError, fetchWithTimeout } from "./fetch-with-timeout.js";
|
|
45
|
+
export type { DeadlineSignalOptions, FetchWithTimeoutOptions } from "./fetch-with-timeout.js";
|
|
46
|
+
export { FETCH_TIMEOUT_MS, FetchTimeoutError, createDeadlineSignal, fetchWithTimeout } from "./fetch-with-timeout.js";
|
|
47
47
|
export type { WellKnownUserToolchainOptions } from "./toolchain.js";
|
|
48
48
|
export { wellKnownUserToolchainBins } from "./toolchain.js";
|
|
49
49
|
export type { BufferedCommandResult } from "./shell.js";
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ export { mergeProxyAwareEnv, parseMacosScutilProxyOutput, parseWindowsInternetSe
|
|
|
37
37
|
export { collectProcessTreePids, createProcessStampArgs, isProcessAlive, listProcessSnapshots, listProcessUsage, matchesProcessStamp, matchesStampedProcess, readFlagValue, readProcessStamp, readProcessStampFromCommand, spawnBackgroundProcess, spawnLoggedProcess, stopProcesses, waitForProcessExit, } from "./process.js";
|
|
38
38
|
export { atomicCopyFile, pathContains, readLogTail, removePathBestEffort } from "./fs.js";
|
|
39
39
|
export { waitForHttpOk } from "./http.js";
|
|
40
|
-
export { FETCH_TIMEOUT_MS, FetchTimeoutError, fetchWithTimeout } from "./fetch-with-timeout.js";
|
|
40
|
+
export { FETCH_TIMEOUT_MS, FetchTimeoutError, createDeadlineSignal, fetchWithTimeout } from "./fetch-with-timeout.js";
|
|
41
41
|
export { wellKnownUserToolchainBins } from "./toolchain.js";
|
|
42
42
|
export { execCommandViaLoginShell, execFileBuffered } from "./shell.js";
|
|
43
43
|
export { AssetCacheError, assertSafePublicUrl, assetCacheKey, assetCacheRewriteUrl, createAssetCache, createAssetCacheDispatcher, createValidatingLookup, isCacheableExternalUrl, } from "./asset-cache.js";
|
|
@@ -12,7 +12,25 @@ export interface MailAdapterKit {
|
|
|
12
12
|
export interface MailAdapterCreateContext {
|
|
13
13
|
readonly credential: MailAdapterCredential;
|
|
14
14
|
readonly kit: MailAdapterKit;
|
|
15
|
+
/** Sample at the point of use. Hosts normalize this to the real clock when omitted. */
|
|
16
|
+
readonly now?: () => Date;
|
|
17
|
+
/** Per-install configuration; only the provider module owns its schema and validation. */
|
|
18
|
+
readonly settings?: unknown;
|
|
15
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Normalize a host's module context without inspecting settings or replacing its guarded ports.
|
|
22
|
+
* The optional fields on {@link MailAdapterCreateContext} preserve legacy direct create calls;
|
|
23
|
+
* hosts use this boundary to supply both defaults before invoking a provider module.
|
|
24
|
+
* @param required Sending credential and guarded adapter kit, passed through unchanged.
|
|
25
|
+
* @param optional Clock and opaque per-install settings. Only undefined selects a default.
|
|
26
|
+
* @returns A context with a live clock and settings (a fresh empty object when omitted).
|
|
27
|
+
* @complexity O(1) time and space; does not sample the clock or perform I/O.
|
|
28
|
+
* @example provider.create(createMailAdapterContext({ credential, kit }, { now, settings }));
|
|
29
|
+
*/
|
|
30
|
+
export declare function createMailAdapterContext(required: {
|
|
31
|
+
credential: MailAdapterCredential;
|
|
32
|
+
kit: MailAdapterKit;
|
|
33
|
+
}, { now, settings }?: Pick<MailAdapterCreateContext, "now" | "settings">): Required<MailAdapterCreateContext>;
|
|
16
34
|
/** Provider modules return the ordinary mail port; the shared contract names no vendor. */
|
|
17
35
|
export interface MailAdapterModule {
|
|
18
36
|
/** Optional host-resolved account connection for domain reads only. The host validates the
|
|
@@ -1,2 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a host's module context without inspecting settings or replacing its guarded ports.
|
|
3
|
+
* The optional fields on {@link MailAdapterCreateContext} preserve legacy direct create calls;
|
|
4
|
+
* hosts use this boundary to supply both defaults before invoking a provider module.
|
|
5
|
+
* @param required Sending credential and guarded adapter kit, passed through unchanged.
|
|
6
|
+
* @param optional Clock and opaque per-install settings. Only undefined selects a default.
|
|
7
|
+
* @returns A context with a live clock and settings (a fresh empty object when omitted).
|
|
8
|
+
* @complexity O(1) time and space; does not sample the clock or perform I/O.
|
|
9
|
+
* @example provider.create(createMailAdapterContext({ credential, kit }, { now, settings }));
|
|
10
|
+
*/
|
|
11
|
+
export function createMailAdapterContext(required, { now = () => new Date(), settings = {} } = {}) {
|
|
12
|
+
return { credential: required.credential, kit: required.kit, now, settings };
|
|
13
|
+
}
|
|
2
14
|
//# sourceMappingURL=adapter-module.js.map
|
package/dist/mail/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type * from "./types.js";
|
|
2
2
|
export type * from "./ports.js";
|
|
3
3
|
export type * from "./adapter-module.js";
|
|
4
|
+
export { createMailAdapterContext } from "./adapter-module.js";
|
|
4
5
|
export type * from "./engine-contracts.js";
|
|
5
6
|
export { wrapMailerWithPurposeGate, isPurposeGateOpen } from "./purpose-scoped-mailer.js";
|
|
6
7
|
export type { WrapMailerWithPurposeGateOptions, PurposeGateRequired } from "./purpose-scoped-mailer.js";
|
package/dist/mail/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { createMailAdapterContext } from "./adapter-module.js";
|
|
1
2
|
export { wrapMailerWithPurposeGate, isPurposeGateOpen } from "./purpose-scoped-mailer.js";
|
|
2
3
|
export { isMailDeliveryAvailable } from "./delivery-availability.js";
|
|
3
4
|
export { createMailSenderIdentityResolver, NON_DELIVERING_MAIL_DRIVERS, SenderIdentityNotConfiguredError } from "./sender-identity.js";
|
package/dist/mail/provider.js
CHANGED
|
@@ -21,11 +21,14 @@ export function validateMailProvider({ provider }) {
|
|
|
21
21
|
// A capability claim without the code behind it is how Resend once advertised feedback with no route.
|
|
22
22
|
if (caps.feedback !== (provider.feedback !== undefined))
|
|
23
23
|
return "capabilities.feedback must match the presence of a feedback parser";
|
|
24
|
-
const hasDomain =
|
|
24
|
+
const hasDomain = hasDomainVerification(provider);
|
|
25
25
|
if (caps.domainVerification !== hasDomain)
|
|
26
26
|
return "capabilities.domainVerification must match domainRequirements + domainStatus";
|
|
27
27
|
return undefined;
|
|
28
28
|
}
|
|
29
|
+
function hasDomainVerification(provider) {
|
|
30
|
+
return typeof provider.domainRequirements === "function" && typeof provider.domainStatus === "function";
|
|
31
|
+
}
|
|
29
32
|
/** Mail registry over the shared keyed registry; built-ins throw on a bad registration, plugins are refused. */
|
|
30
33
|
export function createMailProviderRegistry(_required = {}, optional = {}) {
|
|
31
34
|
const seed = (optional.builtins ?? []).map((provider) => ({ id: provider.id, provider, label: provider.label }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/platform",
|
|
3
|
-
"version": "0.6.15-rc.
|
|
3
|
+
"version": "0.6.15-rc.2",
|
|
4
4
|
"description": "Generic OS/platform primitives: process exec and lifecycle, filesystem containment, HTTP readiness polling, toolchain discovery, blob storage, AWS signing, and an SSRF-safe asset cache.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
"!dist/**/*.map"
|
|
207
207
|
],
|
|
208
208
|
"dependencies": {
|
|
209
|
-
"@jini-ai/core": "^0.6.15-rc.
|
|
209
|
+
"@jini-ai/core": "^0.6.15-rc.2"
|
|
210
210
|
},
|
|
211
211
|
"publishConfig": {
|
|
212
212
|
"access": "public",
|