@nivaro/extension-kit 0.1.1
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 +78 -0
- package/dist/column-probe.d.ts +9 -0
- package/dist/column-probe.d.ts.map +1 -0
- package/dist/column-probe.js +58 -0
- package/dist/column-probe.js.map +1 -0
- package/dist/context.d.ts +361 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/flows.d.ts +58 -0
- package/dist/flows.d.ts.map +1 -0
- package/dist/flows.js +2 -0
- package/dist/flows.js.map +1 -0
- package/dist/hooks.d.ts +26 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +2 -0
- package/dist/hooks.js.map +1 -0
- package/dist/imports.d.ts +155 -0
- package/dist/imports.d.ts.map +1 -0
- package/dist/imports.js +2 -0
- package/dist/imports.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications.d.ts +183 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +2 -0
- package/dist/notifications.js.map +1 -0
- package/dist/obligations.d.ts +66 -0
- package/dist/obligations.d.ts.map +1 -0
- package/dist/obligations.js +2 -0
- package/dist/obligations.js.map +1 -0
- package/dist/registrations.d.ts +470 -0
- package/dist/registrations.d.ts.map +1 -0
- package/dist/registrations.js +2 -0
- package/dist/registrations.js.map +1 -0
- package/dist/requester-columns.d.ts +26 -0
- package/dist/requester-columns.d.ts.map +1 -0
- package/dist/requester-columns.js +17 -0
- package/dist/requester-columns.js.map +1 -0
- package/dist/signals.d.ts +77 -0
- package/dist/signals.d.ts.map +1 -0
- package/dist/signals.js +2 -0
- package/dist/signals.js.map +1 -0
- package/dist/testing.d.ts +193 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +615 -0
- package/dist/testing.js.map +1 -0
- package/dist/user.d.ts +27 -0
- package/dist/user.d.ts.map +1 -0
- package/dist/user.js +2 -0
- package/dist/user.js.map +1 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @nivaro/extension-kit
|
|
2
|
+
|
|
3
|
+
The contract a Nivaro extension is written against.
|
|
4
|
+
|
|
5
|
+
An extension's entry module cannot import `api/src` — the API compiles with
|
|
6
|
+
`rootDir: src`, and a mounted extension runs against a built image that ships
|
|
7
|
+
no source. Before this package, every extension kept hand-copied mirrors of
|
|
8
|
+
the types it needed and re-implemented the small helpers the API already had.
|
|
9
|
+
This package is the one place those live: the API imports its extension
|
|
10
|
+
contract from here, and so does the extension.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import {
|
|
14
|
+
defineExtension,
|
|
15
|
+
requesterInsertFields,
|
|
16
|
+
type ExtensionContext,
|
|
17
|
+
type IntegrationSignal
|
|
18
|
+
} from '@nivaro/extension-kit'
|
|
19
|
+
|
|
20
|
+
export default defineExtension({
|
|
21
|
+
id: 'my-extension',
|
|
22
|
+
env: [{ name: 'PARTNER_TOKEN', required: true, secret: true }],
|
|
23
|
+
async register(ctx: ExtensionContext) {
|
|
24
|
+
ctx.hooks.after('orders', 'create', async ({ result }) => {
|
|
25
|
+
/* … */
|
|
26
|
+
})
|
|
27
|
+
ctx.integrations.registerSignal(mySignal satisfies IntegrationSignal)
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## What is in it
|
|
33
|
+
|
|
34
|
+
- **Context** — `ExtensionContext`, `ExtensionDefinition`, settings and
|
|
35
|
+
environment declarations, cron options.
|
|
36
|
+
- **Registrations** — every shape the context accepts: bulk and item actions,
|
|
37
|
+
readiness and integrity checks, notes providers, mail types, digest
|
|
38
|
+
sections, links, storage adapters, field types, validators, import parsers,
|
|
39
|
+
chat-bot tools, event sources.
|
|
40
|
+
- **Flows** — operation and trigger registrations, the execution context.
|
|
41
|
+
- **Integrations** — obligation kinds, signals, signal actions.
|
|
42
|
+
- **Imports** — the import-processor contract and run-report shapes.
|
|
43
|
+
- **Operations** — `OpsTaskDef` (repairs and backfills run from the console),
|
|
44
|
+
`ConfigSeedDef` (checked-in configuration rows with a drift report) and
|
|
45
|
+
`SchemaStepDef` (versioned DDL an extension owns, run once at load under
|
|
46
|
+
the migration lock).
|
|
47
|
+
- **Helpers** — `hasColumn(db, table, column)` (a per-database column probe
|
|
48
|
+
for writers that must survive an unmigrated database) and
|
|
49
|
+
`requesterInsertFields` / `requesterSelectColumns` for the ERP submission
|
|
50
|
+
tables.
|
|
51
|
+
|
|
52
|
+
Types are exported with `export type`; the helpers and the test context are
|
|
53
|
+
the only runtime code.
|
|
54
|
+
|
|
55
|
+
## Testing an extension
|
|
56
|
+
|
|
57
|
+
`createTestContext()` is a context whose every effect is recorded instead of
|
|
58
|
+
sent — notifications, external calls, activity, flow emits, obligations —
|
|
59
|
+
and whose registrations can be run by the test: `runHooks`, `runCron`,
|
|
60
|
+
`deliverEvent`, `invoke` (a route the extension registered). Its database is
|
|
61
|
+
`createTestDb({ tables })`, an in-memory knex-shaped fake covering the chain
|
|
62
|
+
extensions use (where / whereIn / first / pluck / count / insert / update /
|
|
63
|
+
del, `raw`, `schema.hasColumn`, `transaction`); joins are accepted and
|
|
64
|
+
ignored, so seed the joined columns on the row you expect back.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const ctx = createTestContext({ tables: { orders: [{ id: 1, owner: 'u1' }] } })
|
|
68
|
+
await myExtension.register(ctx)
|
|
69
|
+
await ctx.runHooks('orders', 'create', 'after', { keys: [1], result: { id: 1, owner: 'u1' } })
|
|
70
|
+
expect(ctx.calls.notifications).toEqual([{ userId: 'u1', opts: { subject: 'Order 1', message: 'created' } }])
|
|
71
|
+
const res = await ctx.invoke('GET', '/api/demo/orders/1')
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Versioning
|
|
75
|
+
|
|
76
|
+
The kit follows the API: a release that adds to the context adds to the kit
|
|
77
|
+
in the same commit. An extension built against an older kit keeps working —
|
|
78
|
+
members are only ever added.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/** server/database of a knex client's connection config — the default probe scope. */
|
|
3
|
+
export declare function databaseKey(db: Knex): string;
|
|
4
|
+
export declare function hasColumn(db: Knex, table: string, column: string, opts?: {
|
|
5
|
+
scope?: string;
|
|
6
|
+
}): Promise<boolean>;
|
|
7
|
+
/** Test-only: forget every probed result so the next call re-checks. */
|
|
8
|
+
export declare function resetColumnProbes(): void;
|
|
9
|
+
//# sourceMappingURL=column-probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column-probe.d.ts","sourceRoot":"","sources":["../src/column-probe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAyBhC,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,EAAE,EAAE,IAAI,GAAG,MAAM,CAW5C;AAED,wBAAsB,SAAS,CAC7B,EAAE,EAAE,IAAI,EACR,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5B,OAAO,CAAC,OAAO,CAAC,CAiBlB;AAED,wEAAwE;AACxE,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Does this column exist yet?" — asked by writers that must keep working on
|
|
3
|
+
* a database that has not run the migration adding it (a cloud tenant, an
|
|
4
|
+
* image ahead of its ledger, a teammate's dev API restarting under you).
|
|
5
|
+
*
|
|
6
|
+
* A hit is kept for the life of the process: a column, once added, is never
|
|
7
|
+
* dropped from under a running process. A miss is asked again after a
|
|
8
|
+
* minute, so a database that catches up is picked up without a restart.
|
|
9
|
+
*
|
|
10
|
+
* Probes are kept per database. Pass `scope` when the process serves several
|
|
11
|
+
* databases through one knex (cloud mode: the tenant id); otherwise the knex
|
|
12
|
+
* client's own connection config keys the probe.
|
|
13
|
+
*/
|
|
14
|
+
const MISS_TTL_MS = 60_000;
|
|
15
|
+
const probes = new Map();
|
|
16
|
+
/** server/database of a knex client's connection config — the default probe scope. */
|
|
17
|
+
export function databaseKey(db) {
|
|
18
|
+
try {
|
|
19
|
+
const conn = db.client
|
|
20
|
+
?.config?.connection;
|
|
21
|
+
if (!conn)
|
|
22
|
+
return '';
|
|
23
|
+
if (typeof conn === 'string')
|
|
24
|
+
return conn;
|
|
25
|
+
const c = conn;
|
|
26
|
+
return `${c.server ?? c.host ?? ''}/${c.database ?? ''}`;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return '';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function hasColumn(db, table, column, opts = {}) {
|
|
33
|
+
const key = `${opts.scope ?? databaseKey(db)}\u0000${table}\u0000${column}`;
|
|
34
|
+
const state = probes.get(key);
|
|
35
|
+
if (state?.hit)
|
|
36
|
+
return true;
|
|
37
|
+
if (state?.inflight)
|
|
38
|
+
return state.inflight;
|
|
39
|
+
if (state && Date.now() - state.at < MISS_TTL_MS)
|
|
40
|
+
return false;
|
|
41
|
+
const inflight = (async () => {
|
|
42
|
+
try {
|
|
43
|
+
return await db.schema.hasColumn(table, column);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
probes.set(key, { hit: false, at: state?.at ?? 0, inflight });
|
|
50
|
+
const hit = await inflight;
|
|
51
|
+
probes.set(key, { hit, at: Date.now(), inflight: null });
|
|
52
|
+
return hit;
|
|
53
|
+
}
|
|
54
|
+
/** Test-only: forget every probed result so the next call re-checks. */
|
|
55
|
+
export function resetColumnProbes() {
|
|
56
|
+
probes.clear();
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=column-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column-probe.js","sourceRoot":"","sources":["../src/column-probe.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,GAAG,MAAM,CAAA;AAQ1B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAA;AAE5C,sFAAsF;AACtF,MAAM,UAAU,WAAW,CAAC,EAAQ;IAClC,IAAI,CAAC;QACH,MAAM,IAAI,GAAI,EAAoE,CAAC,MAAM;YACvF,EAAE,MAAM,EAAE,UAAU,CAAA;QACtB,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,CAAA;QACpB,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QACzC,MAAM,CAAC,GAAG,IAA6D,CAAA;QACvE,OAAO,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAA;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,EAAQ,EACR,KAAa,EACb,MAAc,EACd,OAA2B,EAAE;IAE7B,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,IAAI,WAAW,CAAC,EAAE,CAAC,SAAS,KAAK,SAAS,MAAM,EAAE,CAAA;IAC3E,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC7B,IAAI,KAAK,EAAE,GAAG;QAAE,OAAO,IAAI,CAAA;IAC3B,IAAI,KAAK,EAAE,QAAQ;QAAE,OAAO,KAAK,CAAC,QAAQ,CAAA;IAC1C,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,EAAE,GAAG,WAAW;QAAE,OAAO,KAAK,CAAA;IAC9D,MAAM,QAAQ,GAAG,CAAC,KAAK,IAAI,EAAE;QAC3B,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC,CAAC,EAAE,CAAA;IACJ,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC7D,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAA;IAC1B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IACxD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,iBAAiB;IAC/B,MAAM,CAAC,KAAK,EAAE,CAAA;AAChB,CAAC"}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
|
2
|
+
import type { Inngest } from 'inngest';
|
|
3
|
+
import type { Knex } from 'knex';
|
|
4
|
+
import type { FlowOpRegistration, FlowTriggerRegistration } from './flows.js';
|
|
5
|
+
import type { ExtensionHookHandler, HookAction } from './hooks.js';
|
|
6
|
+
import type { ImportProcessorDef } from './imports.js';
|
|
7
|
+
import type { DigestSectionProvider, MailTypeDef, NotificationChannelDef, NotificationSourceProvider, NotifyUserOptions } from './notifications.js';
|
|
8
|
+
import type { ObligationKindDef, ObligationResolvePatch, ObligationTrigger, ObligationTriggerContext } from './obligations.js';
|
|
9
|
+
import type { BotToolDef, BriefLineProvider, BulkActionDef, CallOptions, CallResult, ChainTable, CollectionViewDef, ConfigSeedDef, DashboardWidgetDef, EventSourceDef, FieldTypeDef, ImportParserDef, IntegrityCheck, ItemActionDef, LinkRegistration, MachineMarkerSet, OpsTaskDef, ReadinessCheck, RelatedNoteProvider, SchemaStepDef, StorageAdapter, ValidatorDef } from './registrations.js';
|
|
10
|
+
import type { IntegrationSignal, SignalActionHandler } from './signals.js';
|
|
11
|
+
export type ExtensionSettingValue = string | number | boolean | null;
|
|
12
|
+
/** A handler for the extension's own durable events (ctx.events). */
|
|
13
|
+
export type ExtensionEventHandler = (event: {
|
|
14
|
+
id: number;
|
|
15
|
+
extension: string;
|
|
16
|
+
event_type: string;
|
|
17
|
+
payload: unknown;
|
|
18
|
+
}) => void | Promise<void>;
|
|
19
|
+
export interface ExtensionCronOpts {
|
|
20
|
+
/** Plain-language purpose — what the job does and what it touches. */
|
|
21
|
+
description?: string;
|
|
22
|
+
/** Serialised with every other heavy job; yields to pool pressure. */
|
|
23
|
+
heavy?: boolean;
|
|
24
|
+
/** 'unsafe' = run-now duplicates mail or mutations. */
|
|
25
|
+
idempotent?: 'safe' | 'unsafe' | 'unknown';
|
|
26
|
+
/** The job's own no-write report, offered on Background Jobs. */
|
|
27
|
+
dryRun?: () => Promise<unknown>;
|
|
28
|
+
}
|
|
29
|
+
export interface ExtensionCronMeta {
|
|
30
|
+
description?: string;
|
|
31
|
+
heavy?: boolean;
|
|
32
|
+
idempotent?: 'safe' | 'unsafe' | 'unknown';
|
|
33
|
+
/** The deployment flag this job no-ops behind, and whether it currently
|
|
34
|
+
* lets the job run. */
|
|
35
|
+
gate?: {
|
|
36
|
+
flag: string;
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** A scheduled job as the core's cron manager lists it. */
|
|
41
|
+
export interface ExtensionCronEntry {
|
|
42
|
+
id: string;
|
|
43
|
+
expression: string;
|
|
44
|
+
defaultExpression: string;
|
|
45
|
+
overridden: boolean;
|
|
46
|
+
extensionId?: string;
|
|
47
|
+
nextRun: Date | null;
|
|
48
|
+
heavy?: boolean;
|
|
49
|
+
idempotent?: 'safe' | 'unsafe' | 'unknown';
|
|
50
|
+
description?: string;
|
|
51
|
+
gate?: {
|
|
52
|
+
flag: string;
|
|
53
|
+
enabled: boolean;
|
|
54
|
+
};
|
|
55
|
+
paused?: boolean;
|
|
56
|
+
supports_dry_run?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/** The core's cron manager as extensions may read it (`ctx.app.cron`). */
|
|
59
|
+
export interface ExtensionCronManager {
|
|
60
|
+
list(): ExtensionCronEntry[];
|
|
61
|
+
pause(id: string): void;
|
|
62
|
+
resume(id: string): void;
|
|
63
|
+
}
|
|
64
|
+
/** The Fastify instance with the core decorations an extension may rely on. */
|
|
65
|
+
export type ExtensionApp = FastifyInstance & {
|
|
66
|
+
cron: ExtensionCronManager;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* What `register()` receives. Every member is a capability the core exposes
|
|
70
|
+
* to extensions; members marked optional arrived in a later core release
|
|
71
|
+
* than the earliest an extension may run against, so an extension checks
|
|
72
|
+
* before using them.
|
|
73
|
+
*/
|
|
74
|
+
export interface ExtensionContext {
|
|
75
|
+
app: ExtensionApp;
|
|
76
|
+
database: Knex;
|
|
77
|
+
inngest: Inngest;
|
|
78
|
+
logger: FastifyInstance['log'];
|
|
79
|
+
/** Admin-editable extension settings — declared on the export. Values are
|
|
80
|
+
* parsed by the declared type (number/boolean), 30s cache. */
|
|
81
|
+
settings?: {
|
|
82
|
+
get(key: string): Promise<ExtensionSettingValue>;
|
|
83
|
+
getAll(): Promise<Record<string, ExtensionSettingValue>>;
|
|
84
|
+
};
|
|
85
|
+
/** Durable event outbox — publish inserts a pending row delivered by the
|
|
86
|
+
* sweep cron; `on` registers a delivery handler for this extension's
|
|
87
|
+
* events ('*' = every type). Delivery retries with exponential backoff. */
|
|
88
|
+
events: {
|
|
89
|
+
publish(eventType: string, payload?: unknown): Promise<number | null>;
|
|
90
|
+
on(eventType: string | '*', fn: ExtensionEventHandler): void;
|
|
91
|
+
};
|
|
92
|
+
/** Call a configured external API by name or numeric ID. Auth resolved
|
|
93
|
+
* automatically; every call lands in the API's call log. */
|
|
94
|
+
callExternalApi(nameOrId: string | number, options?: CallOptions): Promise<CallResult>;
|
|
95
|
+
/** Long-running SQL outside knex.raw's request timeout — one statement,
|
|
96
|
+
* its own timeout (default 20 minutes), rows back. Bind nothing: the
|
|
97
|
+
* batch is sent as text, so only interpolate values you built yourself. */
|
|
98
|
+
sql: {
|
|
99
|
+
runLong<T = Record<string, unknown>>(sql: string, opts?: {
|
|
100
|
+
timeoutMs?: number;
|
|
101
|
+
}): Promise<T[]>;
|
|
102
|
+
};
|
|
103
|
+
/** Write an audit entry to nivaro_activity for a mutation that bypasses
|
|
104
|
+
* the items service. The action is namespaced `<extId>:<action>`. Never
|
|
105
|
+
* throws. */
|
|
106
|
+
logActivity(entry: {
|
|
107
|
+
action: string;
|
|
108
|
+
user?: string | null;
|
|
109
|
+
collection?: string;
|
|
110
|
+
item?: string | number;
|
|
111
|
+
comment?: string;
|
|
112
|
+
/** person | machine | import | integration — default: machine with no user. */
|
|
113
|
+
origin?: 'person' | 'machine' | 'import' | 'integration';
|
|
114
|
+
}): Promise<number | null>;
|
|
115
|
+
/** Deliver a notification through the full channel stack — inbox row,
|
|
116
|
+
* live socket event, browser push, optional email — honouring the
|
|
117
|
+
* recipient's notification rules. The only way an extension may notify a
|
|
118
|
+
* person; a raw nivaro_notifications insert bypasses every preference.
|
|
119
|
+
* Never throws. */
|
|
120
|
+
notifyUser(userId: string, opts: NotifyUserOptions): Promise<void>;
|
|
121
|
+
/** Hook helpers scoped to this extension — hooks are tagged and can be
|
|
122
|
+
* disabled / removed with it. */
|
|
123
|
+
hooks: {
|
|
124
|
+
before(collection: string | '*', action: HookAction | '*', fn: ExtensionHookHandler): void;
|
|
125
|
+
after(collection: string | '*', action: HookAction | '*', fn: ExtensionHookHandler): void;
|
|
126
|
+
};
|
|
127
|
+
/** Cron helpers scoped to this extension — jobs pause and resume with it. */
|
|
128
|
+
cron: {
|
|
129
|
+
/** Register a recurring job. `id` is scoped to this extension. */
|
|
130
|
+
schedule(id: string, expression: string, fn: () => void | Promise<void>, opts?: ExtensionCronOpts): void;
|
|
131
|
+
/** Cancel a previously scheduled job. */
|
|
132
|
+
unschedule(id: string): void;
|
|
133
|
+
/** Attach a description / heavy / idempotent flag / gate after scheduling. */
|
|
134
|
+
annotate(id: string, meta: ExtensionCronMeta): void;
|
|
135
|
+
};
|
|
136
|
+
/** Bulk actions in the collection browser / queue selection bar. */
|
|
137
|
+
bulkActions: {
|
|
138
|
+
register(def: BulkActionDef): void;
|
|
139
|
+
};
|
|
140
|
+
/** Contextual buttons in the record form's toolbar. */
|
|
141
|
+
itemActions: {
|
|
142
|
+
register(def: ItemActionDef): void;
|
|
143
|
+
};
|
|
144
|
+
/** Custom notification delivery channels (SMS, Slack, Teams…). */
|
|
145
|
+
notificationChannels: {
|
|
146
|
+
register(def: NotificationChannelDef): void;
|
|
147
|
+
};
|
|
148
|
+
/** Extension-owned alert subscriptions on the profile's notification
|
|
149
|
+
* sources card. */
|
|
150
|
+
notificationSources: {
|
|
151
|
+
register(provider: NotificationSourceProvider): void;
|
|
152
|
+
};
|
|
153
|
+
/** Read-only entries in a record's Notes thread, and the machine markers
|
|
154
|
+
* the thread should drop. */
|
|
155
|
+
notes: {
|
|
156
|
+
registerSource(provider: RelatedNoteProvider): void;
|
|
157
|
+
registerMachineMarkers(set: MachineMarkerSet): void;
|
|
158
|
+
};
|
|
159
|
+
dashboardWidgets: {
|
|
160
|
+
register(def: DashboardWidgetDef): void;
|
|
161
|
+
};
|
|
162
|
+
/** Named file storage adapters (S3, Azure Blob…). */
|
|
163
|
+
storage: {
|
|
164
|
+
register(name: string, adapter: StorageAdapter): void;
|
|
165
|
+
/** Activate a registered adapter for all new uploads. */
|
|
166
|
+
setActive(name: string): void;
|
|
167
|
+
};
|
|
168
|
+
fieldTypes: {
|
|
169
|
+
register(def: FieldTypeDef): void;
|
|
170
|
+
};
|
|
171
|
+
collectionViews: {
|
|
172
|
+
register(def: CollectionViewDef): void;
|
|
173
|
+
};
|
|
174
|
+
importParsers: {
|
|
175
|
+
register(def: ImportParserDef): void;
|
|
176
|
+
};
|
|
177
|
+
/** Processors for staged imports whose file spans several collections;
|
|
178
|
+
* `key` is `<extension>:<name>` and is what an import definition names. */
|
|
179
|
+
importProcessors: {
|
|
180
|
+
register(def: ImportProcessorDef): void;
|
|
181
|
+
};
|
|
182
|
+
validators: {
|
|
183
|
+
register(def: ValidatorDef): void;
|
|
184
|
+
};
|
|
185
|
+
approvalBrief: {
|
|
186
|
+
/** One short line on the transition confirm's approval brief. */
|
|
187
|
+
registerLine(collection: string, fn: BriefLineProvider): void;
|
|
188
|
+
};
|
|
189
|
+
digest: {
|
|
190
|
+
/** A per-user section of the daily action digest email. */
|
|
191
|
+
registerSection(fn: DigestSectionProvider): void;
|
|
192
|
+
};
|
|
193
|
+
readiness: {
|
|
194
|
+
/** A scored check on the go-live readiness scorecard. */
|
|
195
|
+
registerCheck(check: ReadinessCheck): void;
|
|
196
|
+
};
|
|
197
|
+
/** Operational tasks — repairs, backfills, migrations — run from the admin
|
|
198
|
+
* console (dry run by default, one at a time, every run recorded). */
|
|
199
|
+
tasks: {
|
|
200
|
+
register(def: OpsTaskDef): void;
|
|
201
|
+
};
|
|
202
|
+
/** Checked-in rows of a configuration collection, applied by a task —
|
|
203
|
+
* never at boot — with a drift report as its dry run. */
|
|
204
|
+
seeds: {
|
|
205
|
+
register(def: ConfigSeedDef): void;
|
|
206
|
+
};
|
|
207
|
+
/** A versioned schema change the extension owns, run at load under the
|
|
208
|
+
* migration lock and recorded once per database (#826). */
|
|
209
|
+
schema: {
|
|
210
|
+
step(id: string, def: Omit<SchemaStepDef, 'id'>): void;
|
|
211
|
+
};
|
|
212
|
+
chain: {
|
|
213
|
+
/** Start a chain for one feed event (e.g. one shipment) and run fn inside it. */
|
|
214
|
+
begin<T>(root: {
|
|
215
|
+
source: string;
|
|
216
|
+
ref: string;
|
|
217
|
+
}, fn: () => Promise<T>): Promise<T>;
|
|
218
|
+
/** The chain currently open, or null. */
|
|
219
|
+
current(): {
|
|
220
|
+
chain_id: string;
|
|
221
|
+
parent: string | null;
|
|
222
|
+
} | null;
|
|
223
|
+
/** chain_id/chain_parent for an insert into a core table (probe-aware).
|
|
224
|
+
* A table outside ChainTable yields {}. */
|
|
225
|
+
fields(table: ChainTable | (string & {}), opts?: {
|
|
226
|
+
parent?: string | null;
|
|
227
|
+
}): Promise<Record<string, string | null>>;
|
|
228
|
+
};
|
|
229
|
+
integrations: {
|
|
230
|
+
/** Declare an outbound obligation kind. Core owns the ledger and the
|
|
231
|
+
* sweep; the extension owns the sends. */
|
|
232
|
+
registerObligationKind(def: ObligationKindDef): void;
|
|
233
|
+
/** Open an obligation for a decision point in the extension's own code.
|
|
234
|
+
* Null when no registered kind claims the context. */
|
|
235
|
+
openObligation(ctx: ObligationTriggerContext, opts: {
|
|
236
|
+
trigger: ObligationTrigger;
|
|
237
|
+
trigger_ref?: string | null;
|
|
238
|
+
due_at?: Date;
|
|
239
|
+
}): Promise<number | null>;
|
|
240
|
+
/** Close an obligation with its outcome. A null id is a no-op. */
|
|
241
|
+
resolveObligation(id: number | null, patch: ObligationResolvePatch): Promise<void>;
|
|
242
|
+
/** A signal on the Integrations console's Firefight list. */
|
|
243
|
+
registerSignal(def: IntegrationSignal): void;
|
|
244
|
+
/** An action a signal row may offer (kind 'extension', id = def.id). */
|
|
245
|
+
registerSignalAction(def: SignalActionHandler): void;
|
|
246
|
+
/** An event source on the Integrations console's Events feed. */
|
|
247
|
+
registerEventSource(def: EventSourceDef): void;
|
|
248
|
+
};
|
|
249
|
+
integrity: {
|
|
250
|
+
/** A Data Integrity check the sweep, the record banner and Fix run. */
|
|
251
|
+
registerCheck(check: IntegrityCheck): void;
|
|
252
|
+
};
|
|
253
|
+
links: {
|
|
254
|
+
/** The headless frontend's base URL + route map (Settings wins). */
|
|
255
|
+
register(reg: LinkRegistration): void;
|
|
256
|
+
};
|
|
257
|
+
mail: {
|
|
258
|
+
/** An email type for the admin mail harness. */
|
|
259
|
+
registerType(def: MailTypeDef): void;
|
|
260
|
+
/** Dry-run an active flow with a payload; what its mail op would send. */
|
|
261
|
+
renderViaFlow(flowName: string, payload: Record<string, unknown>): Promise<{
|
|
262
|
+
to: string;
|
|
263
|
+
subject: string;
|
|
264
|
+
html: string;
|
|
265
|
+
} | null>;
|
|
266
|
+
/** Render a named Liquid mail template (core or extension root). */
|
|
267
|
+
renderTemplate(name: string, data: Record<string, unknown>): Promise<string>;
|
|
268
|
+
};
|
|
269
|
+
flows: {
|
|
270
|
+
/** A custom operation type: parsed options, current flow data, context. */
|
|
271
|
+
registerOperation(op: FlowOpRegistration): void;
|
|
272
|
+
/** A custom trigger type; `flows.emit(type, payload)` fires it. */
|
|
273
|
+
registerTrigger(trigger: FlowTriggerRegistration): void;
|
|
274
|
+
/** Fire every active flow on this trigger type. Fire-and-forget. */
|
|
275
|
+
emit(triggerType: string, payload: Record<string, unknown>): void;
|
|
276
|
+
};
|
|
277
|
+
/** Tools the AI chat bot may call, run as the asking user. */
|
|
278
|
+
chatBot: {
|
|
279
|
+
registerTool(def: BotToolDef): void;
|
|
280
|
+
};
|
|
281
|
+
/** Auth middleware — use as Fastify `onRequest` / `preHandler` handlers. */
|
|
282
|
+
auth: {
|
|
283
|
+
authenticate: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
284
|
+
requireAuth: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
285
|
+
requireAdmin: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
286
|
+
};
|
|
287
|
+
/** Cloud-only context — populated when CLOUD_META_DB_URL is set. */
|
|
288
|
+
cloud?: {
|
|
289
|
+
/** Immutable tenant UUID for the current request; undefined outside one. */
|
|
290
|
+
getTenantId(): string | undefined;
|
|
291
|
+
/** Tenant slug for the current request; undefined outside one. */
|
|
292
|
+
getTenantSlug(): string | undefined;
|
|
293
|
+
/** Knex client connected to the Nivaro Cloud meta database. */
|
|
294
|
+
metaDb: Knex;
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
export interface ExtensionSettingDeclInput {
|
|
298
|
+
key: string;
|
|
299
|
+
label: string;
|
|
300
|
+
type?: 'string' | 'number' | 'boolean' | 'secret';
|
|
301
|
+
description?: string;
|
|
302
|
+
default?: string;
|
|
303
|
+
/** Legacy spelling of `type: 'secret'`. */
|
|
304
|
+
secret?: boolean;
|
|
305
|
+
/** What production is expected to hold; the readiness scorecard warns
|
|
306
|
+
* when the live value differs. */
|
|
307
|
+
production_expect?: string;
|
|
308
|
+
/** Refuse a value with a message (null = fine). */
|
|
309
|
+
validate?: (value: ExtensionSettingValue) => string | null | Promise<string | null>;
|
|
310
|
+
/** Applied the moment a value is saved (no restart, no cache wait). */
|
|
311
|
+
on_change?: (value: ExtensionSettingValue) => void | Promise<void>;
|
|
312
|
+
}
|
|
313
|
+
/** A declared setting as the admin API serves it (types normalised,
|
|
314
|
+
* handlers reduced to flags). */
|
|
315
|
+
export interface ExtensionSettingDecl {
|
|
316
|
+
key: string;
|
|
317
|
+
label: string;
|
|
318
|
+
type: 'string' | 'number' | 'boolean' | 'secret';
|
|
319
|
+
description?: string;
|
|
320
|
+
default?: string;
|
|
321
|
+
production_expect?: string;
|
|
322
|
+
has_validate?: boolean;
|
|
323
|
+
has_on_change?: boolean;
|
|
324
|
+
}
|
|
325
|
+
/** An environment variable the extension reads. A missing REQUIRED one fails
|
|
326
|
+
* /api/preflight and the readiness scorecard by name; values are never
|
|
327
|
+
* reported. */
|
|
328
|
+
export interface ExtensionEnvDecl {
|
|
329
|
+
name: string;
|
|
330
|
+
required?: boolean;
|
|
331
|
+
description?: string;
|
|
332
|
+
/** A credential: the registry says only whether it is set. */
|
|
333
|
+
secret?: boolean;
|
|
334
|
+
}
|
|
335
|
+
/** The default export of an extension's entry module. */
|
|
336
|
+
export interface ExtensionDefinition {
|
|
337
|
+
id: string;
|
|
338
|
+
register(ctx: ExtensionContext): void | Promise<void>;
|
|
339
|
+
/** Permission scopes: what this extension touches — a declared manifest
|
|
340
|
+
* shown before enabling, not an enforcement boundary. */
|
|
341
|
+
scopes?: string[];
|
|
342
|
+
/** Extension ids that must load FIRST. */
|
|
343
|
+
requires?: string[];
|
|
344
|
+
/** Command-palette entries served to the admin palette. */
|
|
345
|
+
palette?: Array<{
|
|
346
|
+
label: string;
|
|
347
|
+
path: string;
|
|
348
|
+
}>;
|
|
349
|
+
/** Admin-editable settings, stored in nivaro_extension_settings. */
|
|
350
|
+
settings?: ExtensionSettingDeclInput[];
|
|
351
|
+
/** Freeform declared capabilities ('routes', 'cron', 'hooks', …). */
|
|
352
|
+
capabilities?: string[];
|
|
353
|
+
/** Quick self-check surfaced on the Extensions page. */
|
|
354
|
+
healthCheck?(): Promise<{
|
|
355
|
+
ok: boolean;
|
|
356
|
+
note?: string;
|
|
357
|
+
}>;
|
|
358
|
+
/** The environment variables this extension reads. */
|
|
359
|
+
env?: ExtensionEnvDecl[];
|
|
360
|
+
}
|
|
361
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":""}
|
package/dist/flows.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { FastifyBaseLogger } from 'fastify';
|
|
2
|
+
export interface FlowData extends Record<string, unknown> {
|
|
3
|
+
}
|
|
4
|
+
export interface FlowTraceStep {
|
|
5
|
+
key: string;
|
|
6
|
+
name: string;
|
|
7
|
+
type: string;
|
|
8
|
+
status: 'resolve' | 'reject' | 'async';
|
|
9
|
+
preview?: unknown;
|
|
10
|
+
}
|
|
11
|
+
/** What a flow operation handler runs inside. */
|
|
12
|
+
export interface FlowExecutionContext {
|
|
13
|
+
flowId: string;
|
|
14
|
+
flowName: string;
|
|
15
|
+
trigger: string;
|
|
16
|
+
payload: Record<string, unknown>;
|
|
17
|
+
log: FastifyBaseLogger;
|
|
18
|
+
userId?: string;
|
|
19
|
+
/** Test mode: side-effect ops render but do not send — a preview lands in
|
|
20
|
+
* the trace instead. A custom op must honour it. */
|
|
21
|
+
dryRun?: boolean;
|
|
22
|
+
/** When provided, the executor appends one step per executed operation. */
|
|
23
|
+
trace?: FlowTraceStep[];
|
|
24
|
+
}
|
|
25
|
+
export type OpResult = {
|
|
26
|
+
status: 'resolve' | 'reject';
|
|
27
|
+
output: FlowData;
|
|
28
|
+
};
|
|
29
|
+
export type OpHandler = (opts: Record<string, unknown>, data: FlowData, ctx: FlowExecutionContext) => Promise<OpResult>;
|
|
30
|
+
export interface OpFieldSchema {
|
|
31
|
+
key: string;
|
|
32
|
+
label: string;
|
|
33
|
+
type: 'string' | 'number' | 'boolean' | 'select' | 'textarea' | 'json';
|
|
34
|
+
options?: Array<{
|
|
35
|
+
value: string;
|
|
36
|
+
label: string;
|
|
37
|
+
}>;
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
required?: boolean;
|
|
40
|
+
description?: string;
|
|
41
|
+
defaultValue?: unknown;
|
|
42
|
+
}
|
|
43
|
+
export interface FlowOpRegistration {
|
|
44
|
+
type: string;
|
|
45
|
+
label: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
/** hex, e.g. '#7c3aed' */
|
|
48
|
+
color?: string;
|
|
49
|
+
fields?: OpFieldSchema[];
|
|
50
|
+
handler: OpHandler;
|
|
51
|
+
}
|
|
52
|
+
export interface FlowTriggerRegistration {
|
|
53
|
+
type: string;
|
|
54
|
+
label: string;
|
|
55
|
+
description?: string;
|
|
56
|
+
fields?: OpFieldSchema[];
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=flows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../src/flows.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAEhD,MAAM,WAAW,QAAS,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAAG;AAE5D,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;IACtC,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,GAAG,EAAE,iBAAiB,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;yDACqD;IACrD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,aAAa,EAAE,CAAA;CACxB;AAED,MAAM,MAAM,QAAQ,GAAG;IAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAA;AACzE,MAAM,MAAM,SAAS,GAAG,CACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,oBAAoB,KACtB,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEtB,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAA;IACtE,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,0BAA0B;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,aAAa,EAAE,CAAA;IACxB,OAAO,EAAE,SAAS,CAAA;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,aAAa,EAAE,CAAA;CACzB"}
|
package/dist/flows.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flows.js","sourceRoot":"","sources":["../src/flows.ts"],"names":[],"mappings":""}
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FastifyRequest } from 'fastify';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
import type { ExtensionUser } from './user.js';
|
|
4
|
+
export type HookAction = 'create' | 'update' | 'delete' | 'read';
|
|
5
|
+
export type HookTiming = 'before' | 'after';
|
|
6
|
+
/** What a before/after hook receives. `previousData` is set on update and
|
|
7
|
+
* delete (captured before the write); `result` on after-hooks; `keys` holds
|
|
8
|
+
* the record id(s) the write concerned. */
|
|
9
|
+
export interface ExtensionHookContext {
|
|
10
|
+
collection: string;
|
|
11
|
+
action: HookAction;
|
|
12
|
+
keys?: Array<string | number>;
|
|
13
|
+
payload?: Record<string, unknown>;
|
|
14
|
+
result?: unknown;
|
|
15
|
+
previousData?: Record<string, unknown>;
|
|
16
|
+
/** Caller-supplied justification for this write (change-reason config). */
|
|
17
|
+
changeReason?: string;
|
|
18
|
+
/** True while a write is being rehearsed (`dry_run`): a hook may read and
|
|
19
|
+
* shape the payload, and must not write, send or notify. */
|
|
20
|
+
dryRun?: boolean;
|
|
21
|
+
user?: ExtensionUser;
|
|
22
|
+
database: Knex;
|
|
23
|
+
req?: FastifyRequest;
|
|
24
|
+
}
|
|
25
|
+
export type ExtensionHookHandler = (ctx: ExtensionHookContext) => void | Promise<void>;
|
|
26
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAChC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAE9C,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAA;AAChE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAA;AAE3C;;4CAE4C;AAC5C,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAA;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACtC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;iEAC6D;IAC7D,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,QAAQ,EAAE,IAAI,CAAA;IACd,GAAG,CAAC,EAAE,cAAc,CAAA;CACrB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA"}
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":""}
|