@forumone/throughline-core 0.2.2 → 0.4.0
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/CHANGELOG.md +105 -0
- package/README.md +39 -8
- package/dist/auth/authenticator.d.ts +4 -0
- package/dist/auth/authenticator.d.ts.map +1 -1
- package/dist/auth/authenticator.js +11 -0
- package/dist/auth/authenticator.js.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp/audit-context.d.ts +39 -0
- package/dist/mcp/audit-context.d.ts.map +1 -0
- package/dist/mcp/audit-context.js +36 -0
- package/dist/mcp/audit-context.js.map +1 -0
- package/dist/mcp/handler.d.ts.map +1 -1
- package/dist/mcp/handler.js +27 -1
- package/dist/mcp/handler.js.map +1 -1
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/payload-mcp.d.ts +66 -0
- package/dist/mcp/payload-mcp.d.ts.map +1 -0
- package/dist/mcp/payload-mcp.js +63 -0
- package/dist/mcp/payload-mcp.js.map +1 -0
- package/dist/utils/content-hash.d.ts +31 -0
- package/dist/utils/content-hash.d.ts.map +1 -0
- package/dist/utils/content-hash.js +94 -0
- package/dist/utils/content-hash.js.map +1 -0
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -2
- package/dist/utils/index.js.map +1 -1
- package/package.json +3 -7
- package/dist/env/index.d.ts +0 -51
- package/dist/env/index.d.ts.map +0 -1
- package/dist/env/index.js +0 -59
- package/dist/env/index.js.map +0 -1
- package/dist/utils/diff.d.ts +0 -13
- package/dist/utils/diff.d.ts.map +0 -1
- package/dist/utils/diff.js +0 -31
- package/dist/utils/diff.js.map +0 -1
- package/dist/utils/id.d.ts +0 -6
- package/dist/utils/id.d.ts.map +0 -1
- package/dist/utils/id.js +0 -13
- package/dist/utils/id.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,110 @@
|
|
|
1
1
|
# @forumone/throughline-core
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 40839b5: Stop publishing code nothing imports
|
|
8
|
+
|
|
9
|
+
`@forumone/throughline-core` loses three things no package in the suite, and no consumer, has ever called:
|
|
10
|
+
- **`./env`** — `ENV_VARS`, `validateBaseEnv`, `requireEnv`, `optionalEnv`, and the subpath export that served them. The idea was that plugins would read `process.env` through shared constants instead of hard-coded strings; every plugin hard-codes the string, including the ones in this repo. A convention with no adherents is not a convention.
|
|
11
|
+
- **`shallowDiff`** — written for the audit writer's `diff` field, never wired to it. The writer still takes a caller-supplied diff, and Payload's own version diffing is the better answer if one is ever wanted.
|
|
12
|
+
- **`generateId`** — an id generator in a framework where Payload assigns the ids.
|
|
13
|
+
|
|
14
|
+
`@forumone/throughline-plugin-contract` stops shipping `examplePlugin`. It is documentation of a shape, and it now lives in the playground, which is where a shape gets demonstrated — the published package was carrying 74 lines of example for every consumer that installs it.
|
|
15
|
+
|
|
16
|
+
Removing exports from a published package, hence minor rather than patch. Nothing in this repository, and nothing in the suite's only consumer, imports any of it.
|
|
17
|
+
|
|
18
|
+
- 9f39ace: Enforce API-key scopes, which until now were only a label
|
|
19
|
+
|
|
20
|
+
The API-keys collection has always had a required `scopes` field, the README has always told you to mint keys with `--scopes publishing.execute`, and the scheduled-publish factory documents that its key "must carry `publishing.execute` scope". Nothing read the field. Every key could do whatever its linked user could, whatever it said on the label.
|
|
21
|
+
|
|
22
|
+
A tool may now declare `requiredScope`, and the handler holds callers to it: the tool is hidden from `tools/list` and refused on a direct call unless the key names that scope. Hidden as well as refused, because an agent shown a tool it will be turned away from will try it, fail, and report the tool as broken when what is narrow is the key.
|
|
23
|
+
|
|
24
|
+
The consequential tools are annotated — `publish`, `unpublish`, `schedule_publish`, `rollback` (`publishing.execute`); `request_approval` (`approvals.request`); `respond_to_approval` (`approvals.decide`); the three form writers (`forms.manage`); `trigger_sync` and `test_integration` (`integrations.trigger`). Reads are left unscoped, which is the right default for a read.
|
|
25
|
+
|
|
26
|
+
**This narrows existing keys.** A key minted with one scope could previously call every tool on every server and now cannot. That is the point, but it will change what an existing MCP client can do — check the scopes on your keys before upgrading. A key carrying no scopes at all passes nothing scoped: absent is read as none, not as everything.
|
|
27
|
+
|
|
28
|
+
- f138b3d: One audit actor shape for every tool, and stop recording agents as people
|
|
29
|
+
|
|
30
|
+
Ten tools built the audit actor by hand and four of them disagreed. Three were only untidy — a dropped `userName`, conditional spreads, an assumption that `ctx.user` is non-null. The fourth was wrong: the component tools wrote `type: 'user'` unconditionally, so a call made with an API key and no linked user was recorded as a person. An audit log that cannot tell an agent from an editor is not an audit log.
|
|
31
|
+
|
|
32
|
+
`auditContext(ctx, meta)` is now exported from core and used at all eight tool call sites. `type` follows the rule the publishing service already used — a call carrying a user is that user's, one without is the system's — and `apiKeyName` rides along either way, because a key acting for a linked user is still worth naming.
|
|
33
|
+
|
|
34
|
+
It also passes `sessionId` through for the first time. The column has been on the audit collection since it was written and nothing ever filled it; it is what lets somebody reading the log group one conversation's writes instead of reading them one at a time.
|
|
35
|
+
|
|
36
|
+
- 6fac789: Add `toPayloadMcpTool`, so Throughline's tools can be served by Payload's own MCP plugin
|
|
37
|
+
|
|
38
|
+
Payload ships `@payloadcms/plugin-mcp`, exact-pinned to the Payload version, built on the official MCP SDK: streamable HTTP, sessions, per-key per-tool capability checkboxes, and generic CRUD tools derived from the field configs. Against that, `createMcpHandler` here is a 146-line JSON-RPC subset speaking `tools/list` and `tools/call`, mounted six times over.
|
|
39
|
+
|
|
40
|
+
The transport was never the product. The tools are. This adapter is what makes moving between the two a configuration change rather than a rewrite of every tool: it translates the input schema (`withMeta`'s `z.object` to the raw shape the plugin registers), the context (a `PayloadRequest` to an `McpToolContext`), and the result (a tool's own object to MCP content blocks).
|
|
41
|
+
|
|
42
|
+
Nothing is wired to it. It is the outcome of a spike, and the servers move over one at a time.
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- Updated dependencies [40839b5]
|
|
47
|
+
- Updated dependencies [9f39ace]
|
|
48
|
+
- @forumone/throughline-plugin-contract@0.3.0
|
|
49
|
+
|
|
50
|
+
## 0.3.0
|
|
51
|
+
|
|
52
|
+
### Minor Changes
|
|
53
|
+
|
|
54
|
+
- d20f909: Bind an approval to the document's content rather than to its `updatedAt`.
|
|
55
|
+
|
|
56
|
+
`request_approval` stored `String(document['updatedAt'] ?? …)` as `targetVersion`,
|
|
57
|
+
and publishing's approval step recomputed the same expression at publish time. So
|
|
58
|
+
an approval was tied to a timestamp that moves on **every** save. An editor fixing
|
|
59
|
+
a typo between an approver opening the request and clicking approve invalidated the
|
|
60
|
+
approval — and the approver spent that time reading a version that no longer
|
|
61
|
+
existed.
|
|
62
|
+
|
|
63
|
+
Requiring re-approval after an edit is a defensible rule. Inheriting it from
|
|
64
|
+
whichever timestamp field happened to be nearby is not, and it is why **autosave
|
|
65
|
+
could not be turned on** anywhere the approvals plugin is installed: autosave moves
|
|
66
|
+
`updatedAt` every couple of seconds of typing, so a pending approval would be
|
|
67
|
+
invalidated continuously.
|
|
68
|
+
|
|
69
|
+
Both sides now call `documentContentHash(document)`, new in
|
|
70
|
+
`@forumone/throughline-core`. It hashes the document with the metadata that moves
|
|
71
|
+
without the content moving stripped at every level — `id`, `createdAt`,
|
|
72
|
+
`updatedAt`, `_status`, `__v`, `_id`, `globalType` — over keys in sorted order,
|
|
73
|
+
since blocks come back out of JSONB in no promised order. Array order is preserved,
|
|
74
|
+
because that is the order of the blocks on the page. `{ exclude }` adds
|
|
75
|
+
app-specific bookkeeping fields to the strip list.
|
|
76
|
+
|
|
77
|
+
The rule is now the one that was wanted all along: a save that changed nothing
|
|
78
|
+
keeps a granted approval, a save that changed something invalidates it, and an edit
|
|
79
|
+
that is reverted brings the approval back. That last one is why this is a content
|
|
80
|
+
hash rather than a version id — a version id moves whether or not the content did.
|
|
81
|
+
|
|
82
|
+
The two sides only agree because they load the document identically, with
|
|
83
|
+
`payload.findByID({ collection, id, draft: true })` at the config's default depth.
|
|
84
|
+
A populated relationship and a bare relationship id are different values and no
|
|
85
|
+
normalising makes them one, so a caller hashing a document fetched at some other
|
|
86
|
+
depth gets a hash that matches nothing. That is stated on the function.
|
|
87
|
+
|
|
88
|
+
**Approvals pending at upgrade must be re-requested.** Their `targetVersion` holds
|
|
89
|
+
an ISO timestamp; the publish step now computes a hash, so nothing matches and
|
|
90
|
+
those documents report `approval-required` until a fresh request is granted. No
|
|
91
|
+
migration is offered, because the old value cannot be converted — the content it
|
|
92
|
+
was granted against is not recoverable from a timestamp. Grant a moment for
|
|
93
|
+
in-flight requests to clear before upgrading, or expect approvers to be asked once
|
|
94
|
+
more.
|
|
95
|
+
|
|
96
|
+
Also exports `isDraftWrite` from `@forumone/throughline-publishing`. It is the
|
|
97
|
+
predicate the plugin's own trust boundary uses to tell a "Save draft" apart from
|
|
98
|
+
an unpublish, and it is unavailable to host code that needs the same answer: an
|
|
99
|
+
`afterChange` hook cannot work it out, because Payload sets `data._status =
|
|
100
|
+
'draft'` on any `draft: true` update before the hooks run and `previousDoc` is the
|
|
101
|
+
latest _version_ rather than the live document. With autosave on, a host hook that
|
|
102
|
+
drops a cache or sends a notification fires every few seconds of typing unless it
|
|
103
|
+
asks this first.
|
|
104
|
+
|
|
105
|
+
Minor rather than patch on all three: `documentContentHash` and `isDraftWrite` are
|
|
106
|
+
new public API, and the stored meaning of `targetVersion` changes.
|
|
107
|
+
|
|
3
108
|
## 0.2.2
|
|
4
109
|
|
|
5
110
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -10,9 +10,8 @@ The shared plumbing every Throughline server package depends on. Drop it into a
|
|
|
10
10
|
| Auth | `./auth` | `createApiKeysCollection`, `createBearerTokenAuthenticator`, `generateApiKey`, `sha256Hex` |
|
|
11
11
|
| Events | `./events` | `createInngestClient`, `CoreEvents`, `FrameworkEvents` (module-augmentation seam) |
|
|
12
12
|
| MCP | `./mcp` | `createMcpHandler`, `McpMetaSchema`, `withMeta` |
|
|
13
|
-
| Env | `./env` | `ENV_VARS`, `validateBaseEnv`, `requireEnv`, `optionalEnv` |
|
|
14
13
|
| Logger | (main) | `defaultLogger`, `createNamedLogger` |
|
|
15
|
-
| Utils | (main) | `
|
|
14
|
+
| Utils | (main) | `documentContentHash` |
|
|
16
15
|
|
|
17
16
|
The main entry re-exports everything; the subpath exports keep bundles smaller for consumers who only need one slice.
|
|
18
17
|
|
|
@@ -88,6 +87,34 @@ const handleMcp = createMcpHandler({
|
|
|
88
87
|
export const POST = (req: Request) => handleMcp(req)
|
|
89
88
|
```
|
|
90
89
|
|
|
90
|
+
### Scopes
|
|
91
|
+
|
|
92
|
+
A key carries `scopes`, and a tool may declare the one it needs:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const publishTool: McpToolDefinition = {
|
|
96
|
+
name: 'publish',
|
|
97
|
+
requiredScope: 'publishing.execute',
|
|
98
|
+
// …
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
A tool that declares no `requiredScope` is callable by any authenticated key, which is the right default for a read. A tool that declares one is **hidden from `tools/list`** and refused on a direct call unless the key names that scope — hidden as well as refused, because an agent shown a tool it will be turned away from will try it, fail, and report the tool as broken when what is narrow is the key.
|
|
103
|
+
|
|
104
|
+
A key carrying no scopes at all passes nothing scoped. Absent is read as none, not as everything.
|
|
105
|
+
|
|
106
|
+
The consequential tools in this suite and the scopes they require:
|
|
107
|
+
|
|
108
|
+
| Scope | Tools |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `publishing.execute` | `publish`, `unpublish`, `schedule_publish`, `rollback` |
|
|
111
|
+
| `approvals.request` | `request_approval` |
|
|
112
|
+
| `approvals.decide` | `respond_to_approval` |
|
|
113
|
+
| `forms.manage` | `create_form`, `update_form_fields`, `update_form_destinations` |
|
|
114
|
+
| `integrations.trigger` | `trigger_sync`, `test_integration` |
|
|
115
|
+
|
|
116
|
+
Everything else — the component tools, the audit queries, the read side of publishing and approvals — needs only a valid key.
|
|
117
|
+
|
|
91
118
|
## Events
|
|
92
119
|
|
|
93
120
|
`CoreEvents` enumerates the events the framework fires today. Server packages add their own via TypeScript module augmentation:
|
|
@@ -104,18 +131,22 @@ declare module '@forumone/throughline-core/events' {
|
|
|
104
131
|
|
|
105
132
|
After augmentation, `inngest.send({ name: 'approval/decided', data: { ... } })` is type-checked everywhere.
|
|
106
133
|
|
|
107
|
-
## Env vars
|
|
108
134
|
|
|
109
|
-
|
|
135
|
+
## Document content hashing
|
|
110
136
|
|
|
111
|
-
|
|
112
|
-
import { ENV_VARS, requireEnv, validateBaseEnv } from '@forumone/throughline-core'
|
|
137
|
+
`documentContentHash(document)` reduces a Payload document to a hash of the part an editor authored, ignoring the metadata that moves without the content moving — `id`, `createdAt`, `updatedAt`, `_status`, `__v`, `_id`, `globalType`, stripped at every level of the document. Object key order does not affect the result, because blocks come back out of JSONB in no promised order; array order does, because that is the order of the blocks on the page.
|
|
113
138
|
|
|
114
|
-
|
|
139
|
+
```ts
|
|
140
|
+
import { documentContentHash } from '@forumone/throughline-core'
|
|
115
141
|
|
|
116
|
-
const
|
|
142
|
+
const version = await documentContentHash(page)
|
|
143
|
+
const withExtras = await documentContentHash(page, { exclude: ['syncedAt'] })
|
|
117
144
|
```
|
|
118
145
|
|
|
146
|
+
It exists so that approvals can bind to *what an approver read* rather than to when it was last saved. Approvals writes it as `targetVersion`; publishing recomputes it at publish time. Two consequences worth stating: a save that changed nothing keeps a granted approval, and an edit that is reverted brings one back.
|
|
147
|
+
|
|
148
|
+
**Two callers only agree if they hash a document loaded the same way.** Both of the above use `payload.findByID({ collection, id, draft: true })` at the config's default depth. A populated relationship and a bare relationship id are different values, and normalising cannot turn one into the other — so a third caller fetching at a different depth would produce a hash that matches nothing.
|
|
149
|
+
|
|
119
150
|
## Why all of this lives in one package
|
|
120
151
|
|
|
121
152
|
Server packages (Component, Publishing, Approvals, Audit Query, Forms, Integrations) all depend on the same audit log, the same authentication pattern, and the same event taxonomy. Splitting these across packages would create circular dependencies — every server package would need the audit writer, and an audit-only package would need to know about every server. Consolidating the plumbing here keeps the dependency graph one-way: core → server packages → client app.
|
|
@@ -10,6 +10,10 @@ export interface BearerTokenAuthenticatorOptions {
|
|
|
10
10
|
* against the API-keys collection. Returns the linked user plus key metadata
|
|
11
11
|
* on success, `null` on any failure (no token, unknown token, disabled key,
|
|
12
12
|
* key without linked user, expired key).
|
|
13
|
+
*
|
|
14
|
+
* The key's `scopes` come back with it. They are what the handler holds a
|
|
15
|
+
* scoped tool against; until they did, the field was a label on a key that
|
|
16
|
+
* could do everything its linked user could.
|
|
13
17
|
*/
|
|
14
18
|
export declare function createBearerTokenAuthenticator(options: BearerTokenAuthenticatorOptions): McpAuthenticator;
|
|
15
19
|
//# sourceMappingURL=authenticator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticator.d.ts","sourceRoot":"","sources":["../../src/auth/authenticator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAGV,gBAAgB,EACjB,MAAM,uCAAuC,CAAA;AAG9C,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,OAAO,CAAA;IAChB,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED
|
|
1
|
+
{"version":3,"file":"authenticator.d.ts","sourceRoot":"","sources":["../../src/auth/authenticator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAGV,gBAAgB,EACjB,MAAM,uCAAuC,CAAA;AAG9C,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,OAAO,CAAA;IAChB,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,+BAA+B,GACvC,gBAAgB,CAyClB"}
|
|
@@ -4,6 +4,10 @@ import { DEFAULT_API_KEYS_SLUG, sha256Hex } from './api-keys.js';
|
|
|
4
4
|
* against the API-keys collection. Returns the linked user plus key metadata
|
|
5
5
|
* on success, `null` on any failure (no token, unknown token, disabled key,
|
|
6
6
|
* key without linked user, expired key).
|
|
7
|
+
*
|
|
8
|
+
* The key's `scopes` come back with it. They are what the handler holds a
|
|
9
|
+
* scoped tool against; until they did, the field was a label on a key that
|
|
10
|
+
* could do everything its linked user could.
|
|
7
11
|
*/
|
|
8
12
|
export function createBearerTokenAuthenticator(options) {
|
|
9
13
|
const { payload, collectionSlug = DEFAULT_API_KEYS_SLUG } = options;
|
|
@@ -38,6 +42,7 @@ export function createBearerTokenAuthenticator(options) {
|
|
|
38
42
|
user,
|
|
39
43
|
apiKeyName: String(apiKey['name'] ?? ''),
|
|
40
44
|
apiKeyId: String(apiKey['id']),
|
|
45
|
+
scopes: toScopes(apiKey['scopes']),
|
|
41
46
|
};
|
|
42
47
|
},
|
|
43
48
|
};
|
|
@@ -60,4 +65,10 @@ function toAuthenticatedUser(raw) {
|
|
|
60
65
|
groups: raw['groups'] ?? [],
|
|
61
66
|
};
|
|
62
67
|
}
|
|
68
|
+
/** The stored `scopes` as a string list, whatever shape the row came back in. */
|
|
69
|
+
function toScopes(raw) {
|
|
70
|
+
if (!Array.isArray(raw))
|
|
71
|
+
return [];
|
|
72
|
+
return raw.filter((scope) => typeof scope === 'string');
|
|
73
|
+
}
|
|
63
74
|
//# sourceMappingURL=authenticator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticator.js","sourceRoot":"","sources":["../../src/auth/authenticator.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAQhE
|
|
1
|
+
{"version":3,"file":"authenticator.js","sourceRoot":"","sources":["../../src/auth/authenticator.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,13 +2,12 @@ export { AUDIT_ACTIONS, AUDIT_MCP_SERVERS, DEFAULT_AUDIT_SLUG, auditPlugin, crea
|
|
|
2
2
|
export type { AuditAction, AuditActor, AuditCollectionOptions, AuditEventInput, AuditMcpServer, AuditPluginOptions, AuditWriter, AuditWriterOptions, } from './audit/index.js';
|
|
3
3
|
export { DEFAULT_API_KEYS_SLUG, createApiKeysCollection, createBearerTokenAuthenticator, generateApiKey, sha256Hex, } from './auth/index.js';
|
|
4
4
|
export type { ApiKeysCollectionOptions, BearerTokenAuthenticatorOptions } from './auth/index.js';
|
|
5
|
-
export { ENV_VARS, validateBaseEnv, requireEnv, optionalEnv } from './env/index.js';
|
|
6
|
-
export type { EnvVarName, BaseEnv } from './env/index.js';
|
|
7
5
|
export { createInngestClient } from './events/index.js';
|
|
8
6
|
export type { CoreEvents, FrameworkEvents, InngestClientOptions } from './events/index.js';
|
|
9
|
-
export { McpMetaSchema, createMcpHandler, withMeta } from './mcp/index.js';
|
|
10
|
-
export type { McpHandlerOptions, McpMeta } from './mcp/index.js';
|
|
7
|
+
export { McpMetaSchema, auditContext, createMcpHandler, toPayloadMcpTool, toPayloadMcpTools, withMeta, } from './mcp/index.js';
|
|
8
|
+
export type { AuditContextFields, McpHandlerOptions, McpMeta, PayloadMcpRequest, PayloadMcpTool, ToPayloadMcpToolOptions, } from './mcp/index.js';
|
|
11
9
|
export { defaultLogger, createNamedLogger } from './logger/index.js';
|
|
12
|
-
export {
|
|
10
|
+
export { documentContentHash } from './utils/index.js';
|
|
11
|
+
export type { DocumentContentHashOptions } from './utils/index.js';
|
|
13
12
|
export type { AuthenticatedUser, BaseCorePluginOptions, CorePlugin, Logger, McpAuthResult, McpAuthenticator, McpToolContext, McpToolDefinition, PluginRegistry, PluginRegistryEntry, } from '@forumone/throughline-plugin-contract';
|
|
14
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,GACf,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,WAAW,EACX,UAAU,EACV,sBAAsB,EACtB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,8BAA8B,EAC9B,cAAc,EACd,SAAS,GACV,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,wBAAwB,EAAE,+BAA+B,EAAE,MAAM,iBAAiB,CAAA;AAEhG,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,GACf,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,WAAW,EACX,UAAU,EACV,sBAAsB,EACtB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,8BAA8B,EAC9B,cAAc,EACd,SAAS,GACV,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,wBAAwB,EAAE,+BAA+B,EAAE,MAAM,iBAAiB,CAAA;AAEhG,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAE1F,OAAO,EACL,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,GACT,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,OAAO,EACP,iBAAiB,EACjB,cAAc,EACd,uBAAuB,GACxB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAEpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtD,YAAY,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAA;AAIlE,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,EACV,MAAM,EACN,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,mBAAmB,GACpB,MAAM,uCAAuC,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
// surface here mirrors the package's `./<subpath>` exports.
|
|
3
3
|
export { AUDIT_ACTIONS, AUDIT_MCP_SERVERS, DEFAULT_AUDIT_SLUG, auditPlugin, createAuditCollection, createAuditWriter, getAuditWriter, } from './audit/index.js';
|
|
4
4
|
export { DEFAULT_API_KEYS_SLUG, createApiKeysCollection, createBearerTokenAuthenticator, generateApiKey, sha256Hex, } from './auth/index.js';
|
|
5
|
-
export { ENV_VARS, validateBaseEnv, requireEnv, optionalEnv } from './env/index.js';
|
|
6
5
|
export { createInngestClient } from './events/index.js';
|
|
7
|
-
export { McpMetaSchema, createMcpHandler, withMeta } from './mcp/index.js';
|
|
6
|
+
export { McpMetaSchema, auditContext, createMcpHandler, toPayloadMcpTool, toPayloadMcpTools, withMeta, } from './mcp/index.js';
|
|
8
7
|
export { defaultLogger, createNamedLogger } from './logger/index.js';
|
|
9
|
-
export {
|
|
8
|
+
export { documentContentHash } from './utils/index.js';
|
|
10
9
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,4DAA4D;AAE5D,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,GACf,MAAM,kBAAkB,CAAA;AAYzB,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,8BAA8B,EAC9B,cAAc,EACd,SAAS,GACV,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,4DAA4D;AAE5D,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,GACf,MAAM,kBAAkB,CAAA;AAYzB,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,8BAA8B,EAC9B,cAAc,EACd,SAAS,GACV,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAGvD,OAAO,EACL,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,GACT,MAAM,gBAAgB,CAAA;AAUvB,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAEpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { McpToolContext } from '@forumone/throughline-plugin-contract';
|
|
2
|
+
import type { AuditActor } from '../audit/writer.js';
|
|
3
|
+
import type { McpMeta } from './meta.js';
|
|
4
|
+
/**
|
|
5
|
+
* The audit fields every tool derives from its request rather than from its
|
|
6
|
+
* arguments: who called, and whatever narrative context the client attached.
|
|
7
|
+
*
|
|
8
|
+
* Spread into an `AuditEventInput` alongside the fields the tool itself knows —
|
|
9
|
+
* `action`, `mcpServer`, `mcpTool`, the target.
|
|
10
|
+
*/
|
|
11
|
+
export interface AuditContextFields {
|
|
12
|
+
actor: AuditActor;
|
|
13
|
+
prompt?: string | undefined;
|
|
14
|
+
reasoning?: string | undefined;
|
|
15
|
+
changesSummary?: string | undefined;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* One answer to "who did this", for every tool in every server.
|
|
19
|
+
*
|
|
20
|
+
* Ten tools built this block by hand and four of them disagreed. Three variants
|
|
21
|
+
* were only untidy — a dropped `userName`, conditional spreads, an assumption
|
|
22
|
+
* that `ctx.user` is non-null. The fourth was wrong: the component tools wrote
|
|
23
|
+
* `type: 'user'` unconditionally, so a call made with an API key and no linked
|
|
24
|
+
* user was recorded as a person. An audit log that cannot tell an agent from an
|
|
25
|
+
* editor is not an audit log, and this is the log the MCP pilot will be judged
|
|
26
|
+
* on.
|
|
27
|
+
*
|
|
28
|
+
* `type` follows the rule `publishing`'s service already used: a call carrying a
|
|
29
|
+
* user is that user's, and one without is the system's. `apiKeyName` rides along
|
|
30
|
+
* either way, because a key acting on behalf of a linked user is still worth
|
|
31
|
+
* naming.
|
|
32
|
+
*
|
|
33
|
+
* `sessionId` is passed through for the first time. The column has existed on
|
|
34
|
+
* the audit collection since it was written and nothing ever filled it; it is
|
|
35
|
+
* what lets somebody reading the log group a whole conversation's writes rather
|
|
36
|
+
* than reading them one at a time.
|
|
37
|
+
*/
|
|
38
|
+
export declare function auditContext(ctx: McpToolContext, meta?: McpMeta): AuditContextFields;
|
|
39
|
+
//# sourceMappingURL=audit-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-context.d.ts","sourceRoot":"","sources":["../../src/mcp/audit-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AAC3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAIpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,UAAU,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACpC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAapF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One answer to "who did this", for every tool in every server.
|
|
3
|
+
*
|
|
4
|
+
* Ten tools built this block by hand and four of them disagreed. Three variants
|
|
5
|
+
* were only untidy — a dropped `userName`, conditional spreads, an assumption
|
|
6
|
+
* that `ctx.user` is non-null. The fourth was wrong: the component tools wrote
|
|
7
|
+
* `type: 'user'` unconditionally, so a call made with an API key and no linked
|
|
8
|
+
* user was recorded as a person. An audit log that cannot tell an agent from an
|
|
9
|
+
* editor is not an audit log, and this is the log the MCP pilot will be judged
|
|
10
|
+
* on.
|
|
11
|
+
*
|
|
12
|
+
* `type` follows the rule `publishing`'s service already used: a call carrying a
|
|
13
|
+
* user is that user's, and one without is the system's. `apiKeyName` rides along
|
|
14
|
+
* either way, because a key acting on behalf of a linked user is still worth
|
|
15
|
+
* naming.
|
|
16
|
+
*
|
|
17
|
+
* `sessionId` is passed through for the first time. The column has existed on
|
|
18
|
+
* the audit collection since it was written and nothing ever filled it; it is
|
|
19
|
+
* what lets somebody reading the log group a whole conversation's writes rather
|
|
20
|
+
* than reading them one at a time.
|
|
21
|
+
*/
|
|
22
|
+
export function auditContext(ctx, meta) {
|
|
23
|
+
return {
|
|
24
|
+
actor: {
|
|
25
|
+
type: ctx.user ? 'user' : 'system',
|
|
26
|
+
userId: ctx.user?.id,
|
|
27
|
+
userName: ctx.user?.name,
|
|
28
|
+
apiKeyName: ctx.apiKeyName,
|
|
29
|
+
sessionId: ctx.sessionId,
|
|
30
|
+
},
|
|
31
|
+
prompt: meta?.userPrompt,
|
|
32
|
+
reasoning: meta?.reasoning,
|
|
33
|
+
changesSummary: meta?.changesSummary,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=audit-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-context.js","sourceRoot":"","sources":["../../src/mcp/audit-context.ts"],"names":[],"mappings":"AAqBA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,YAAY,CAAC,GAAmB,EAAE,IAAc;IAC9D,OAAO;QACL,KAAK,EAAE;YACL,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;YAClC,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE;YACpB,QAAQ,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI;YACxB,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,SAAS,EAAE,GAAG,CAAC,SAAS;SACzB;QACD,MAAM,EAAE,IAAI,EAAE,UAAU;QACxB,SAAS,EAAE,IAAI,EAAE,SAAS;QAC1B,cAAc,EAAE,IAAI,EAAE,cAAc;KACrC,CAAA;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/mcp/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAGtC,OAAO,KAAK,EACV,MAAM,EACN,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,uCAAuC,CAAA;AAI9C,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAA;IAClB,kEAAkE;IAClE,KAAK,EAAE,iBAAiB,EAAE,CAAA;IAC1B,mEAAmE;IACnE,aAAa,CAAC,EAAE,gBAAgB,CAAA;IAChC,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/mcp/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAGtC,OAAO,KAAK,EACV,MAAM,EACN,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,uCAAuC,CAAA;AAI9C,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAA;IAClB,kEAAkE;IAClE,KAAK,EAAE,iBAAiB,EAAE,CAAA;IAC1B,mEAAmE;IACnE,aAAa,CAAC,EAAE,gBAAgB,CAAA;IAChC,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA0GpG"}
|
package/dist/mcp/handler.js
CHANGED
|
@@ -30,8 +30,17 @@ export function createMcpHandler(options) {
|
|
|
30
30
|
const id = rpc.id ?? null;
|
|
31
31
|
try {
|
|
32
32
|
if (rpc.method === 'tools/list') {
|
|
33
|
+
/*
|
|
34
|
+
Only what this key may actually call.
|
|
35
|
+
|
|
36
|
+
An agent shown a tool it will be refused for will try it, be refused,
|
|
37
|
+
and try something else — and the transcript reads like the tool is
|
|
38
|
+
broken rather than like the key is narrow. Hiding it is the difference
|
|
39
|
+
between "you cannot publish" and "publishing is unavailable", and only
|
|
40
|
+
one of those is true.
|
|
41
|
+
*/
|
|
33
42
|
return jsonRpcResult(id, {
|
|
34
|
-
tools: options.tools.map((tool) => ({
|
|
43
|
+
tools: options.tools.filter(permits(auth.scopes)).map((tool) => ({
|
|
35
44
|
name: tool.name,
|
|
36
45
|
description: tool.description,
|
|
37
46
|
inputSchema: zodToJsonSchema(tool.inputSchema, { target: 'jsonSchema7' }),
|
|
@@ -47,6 +56,12 @@ export function createMcpHandler(options) {
|
|
|
47
56
|
if (!tool) {
|
|
48
57
|
return jsonRpcError(id, JSON_RPC_METHOD_NOT_FOUND, `Unknown tool: ${params.data.name}`);
|
|
49
58
|
}
|
|
59
|
+
// Named rather than merely refused: a caller that cannot see why it was
|
|
60
|
+
// turned away has no way to ask for the right key.
|
|
61
|
+
if (!permits(auth.scopes)(tool)) {
|
|
62
|
+
logger.warn(`${tag} refused ${tool.name}: key "${auth.apiKeyName}" lacks scope ${String(tool.requiredScope)}`);
|
|
63
|
+
return jsonRpcError(id, JSON_RPC_INVALID_REQUEST, `This API key does not carry the "${String(tool.requiredScope)}" scope, which ${tool.name} requires.`);
|
|
64
|
+
}
|
|
50
65
|
const inputResult = tool.inputSchema.safeParse(params.data.arguments ?? {});
|
|
51
66
|
if (!inputResult.success) {
|
|
52
67
|
return jsonRpcError(id, JSON_RPC_INVALID_PARAMS, `Invalid arguments: ${inputResult.error.message}`);
|
|
@@ -106,4 +121,15 @@ function jsonRpcResult(id, result) {
|
|
|
106
121
|
function jsonRpcError(id, code, message) {
|
|
107
122
|
return jsonResponse({ jsonrpc: '2.0', id, error: { code, message } });
|
|
108
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Whether a key holding `scopes` may call a given tool.
|
|
126
|
+
*
|
|
127
|
+
* A tool declaring no `requiredScope` is callable by any authenticated key —
|
|
128
|
+
* the right default for a read. A tool that declares one needs it named on the
|
|
129
|
+
* key, and a key carrying no scopes at all passes nothing.
|
|
130
|
+
*/
|
|
131
|
+
function permits(scopes) {
|
|
132
|
+
const held = new Set(scopes ?? []);
|
|
133
|
+
return (tool) => tool.requiredScope === undefined || held.has(tool.requiredScope);
|
|
134
|
+
}
|
|
109
135
|
//# sourceMappingURL=handler.js.map
|
package/dist/mcp/handler.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/mcp/handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAMpD,OAAO,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAA;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAclD;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAA0B;IACzD,MAAM,aAAa,GACjB,OAAO,CAAC,aAAa,IAAI,8BAA8B,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IACvF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IAClE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,aAAa,CAAA;IAC9C,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,UAAU,GAAG,CAAA;IAErC,OAAO,KAAK,UAAU,SAAS,CAAC,OAAgB;QAC9C,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;QACtD,IAAI,CAAC,IAAI;YAAE,OAAO,YAAY,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAA;QAE9D,IAAI,IAAa,CAAA;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAA;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,YAAY,CAAC,IAAI,EAAE,oBAAoB,EAAE,aAAa,CAAC,CAAA;QAChE,CAAC;QAED,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QACnD,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO,YAAY,CAAC,IAAI,EAAE,wBAAwB,EAAE,iBAAiB,CAAC,CAAA;QAE3F,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAA;QACvB,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,IAAI,IAAI,CAAA;QAEzB,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;gBAChC,OAAO,aAAa,CAAC,EAAE,EAAE;oBACvB,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/mcp/handler.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -2,4 +2,8 @@ export { createMcpHandler } from './handler.js';
|
|
|
2
2
|
export type { McpHandlerOptions } from './handler.js';
|
|
3
3
|
export { McpMetaSchema, withMeta } from './meta.js';
|
|
4
4
|
export type { McpMeta } from './meta.js';
|
|
5
|
+
export { auditContext } from './audit-context.js';
|
|
6
|
+
export type { AuditContextFields } from './audit-context.js';
|
|
7
|
+
export { toPayloadMcpTool, toPayloadMcpTools } from './payload-mcp.js';
|
|
8
|
+
export type { PayloadMcpRequest, PayloadMcpTool, ToPayloadMcpToolOptions, } from './payload-mcp.js';
|
|
5
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/mcp/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAErD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACnD,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAErD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACnD,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAE5D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACtE,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAA"}
|
package/dist/mcp/index.js
CHANGED
package/dist/mcp/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAG/C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAG/C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGjD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Logger, McpToolDefinition } from '@forumone/throughline-plugin-contract';
|
|
3
|
+
/** The tool shape `plugin-mcp` accepts under its `mcp.tools` option. */
|
|
4
|
+
export interface PayloadMcpTool {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
parameters: z.ZodRawShape;
|
|
8
|
+
handler: (args: Record<string, unknown>, req: PayloadMcpRequest, extra: unknown) => Promise<{
|
|
9
|
+
content: Array<{
|
|
10
|
+
type: 'text';
|
|
11
|
+
text: string;
|
|
12
|
+
}>;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The half of `PayloadRequest` this adapter reads.
|
|
17
|
+
*
|
|
18
|
+
* Declared structurally rather than imported so `core` keeps `payload` a peer
|
|
19
|
+
* it does not name in a type position — and because `payloadAPI` is a module
|
|
20
|
+
* augmentation `plugin-mcp` adds, which is only present when that package is
|
|
21
|
+
* installed.
|
|
22
|
+
*/
|
|
23
|
+
export interface PayloadMcpRequest {
|
|
24
|
+
user?: {
|
|
25
|
+
id?: unknown;
|
|
26
|
+
email?: unknown;
|
|
27
|
+
name?: unknown;
|
|
28
|
+
roles?: unknown;
|
|
29
|
+
groups?: unknown;
|
|
30
|
+
} | null;
|
|
31
|
+
payloadAPI?: string;
|
|
32
|
+
payload?: {
|
|
33
|
+
logger?: Logger;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface ToPayloadMcpToolOptions {
|
|
37
|
+
/**
|
|
38
|
+
* What to record as the calling key's name.
|
|
39
|
+
*
|
|
40
|
+
* `plugin-mcp` authenticates against its own key collection and puts the user
|
|
41
|
+
* on the request, not the key — so unlike this package's handler, the key's
|
|
42
|
+
* own name is not recoverable from the request. Pass one if the audit trail
|
|
43
|
+
* should name something more useful than the strategy.
|
|
44
|
+
*/
|
|
45
|
+
apiKeyName?: string;
|
|
46
|
+
logger?: Logger;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Wraps one Throughline tool so Payload's MCP plugin can serve it.
|
|
50
|
+
*
|
|
51
|
+
* Three things are being translated, and none of them is the tool's logic:
|
|
52
|
+
*
|
|
53
|
+
* - **The schema.** `plugin-mcp` wants the raw shape; `withMeta` produces a
|
|
54
|
+
* `z.object` around it. `.shape` is the whole conversion, which is why tools
|
|
55
|
+
* must build their input with `withMeta` or `z.object` rather than an
|
|
56
|
+
* arbitrary `ZodType`.
|
|
57
|
+
* - **The context.** Payload hands the handler a request. Throughline's tools
|
|
58
|
+
* take a `McpToolContext`, so one is built from it.
|
|
59
|
+
* - **The result.** Payload wants MCP content blocks; Throughline's tools
|
|
60
|
+
* return their own objects, exactly as they do through this package's own
|
|
61
|
+
* handler, which does the same wrapping a layer up.
|
|
62
|
+
*/
|
|
63
|
+
export declare function toPayloadMcpTool(tool: McpToolDefinition, options?: ToPayloadMcpToolOptions): PayloadMcpTool;
|
|
64
|
+
/** Every tool in a server, in one call. */
|
|
65
|
+
export declare function toPayloadMcpTools(tools: McpToolDefinition[], options?: ToPayloadMcpToolOptions): PayloadMcpTool[];
|
|
66
|
+
//# sourceMappingURL=payload-mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-mcp.d.ts","sourceRoot":"","sources":["../../src/mcp/payload-mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,MAAM,EAAkB,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAsBtG,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,CAAC,CAAC,WAAW,CAAA;IACzB,OAAO,EAAE,CACP,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,OAAO,KACX,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAA;CACjE;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAA;IAClG,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,iBAAiB,EACvB,OAAO,GAAE,uBAA4B,GACpC,cAAc,CAYhB;AAED,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,iBAAiB,EAAE,EAC1B,OAAO,GAAE,uBAA4B,GACpC,cAAc,EAAE,CAElB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defaultLogger } from '../logger/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Wraps one Throughline tool so Payload's MCP plugin can serve it.
|
|
5
|
+
*
|
|
6
|
+
* Three things are being translated, and none of them is the tool's logic:
|
|
7
|
+
*
|
|
8
|
+
* - **The schema.** `plugin-mcp` wants the raw shape; `withMeta` produces a
|
|
9
|
+
* `z.object` around it. `.shape` is the whole conversion, which is why tools
|
|
10
|
+
* must build their input with `withMeta` or `z.object` rather than an
|
|
11
|
+
* arbitrary `ZodType`.
|
|
12
|
+
* - **The context.** Payload hands the handler a request. Throughline's tools
|
|
13
|
+
* take a `McpToolContext`, so one is built from it.
|
|
14
|
+
* - **The result.** Payload wants MCP content blocks; Throughline's tools
|
|
15
|
+
* return their own objects, exactly as they do through this package's own
|
|
16
|
+
* handler, which does the same wrapping a layer up.
|
|
17
|
+
*/
|
|
18
|
+
export function toPayloadMcpTool(tool, options = {}) {
|
|
19
|
+
const shape = shapeOf(tool);
|
|
20
|
+
return {
|
|
21
|
+
name: tool.name,
|
|
22
|
+
description: tool.description,
|
|
23
|
+
parameters: shape,
|
|
24
|
+
handler: async (args, req) => {
|
|
25
|
+
const result = await tool.handler(args, contextFrom(req, options));
|
|
26
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Every tool in a server, in one call. */
|
|
31
|
+
export function toPayloadMcpTools(tools, options = {}) {
|
|
32
|
+
return tools.map(tool => toPayloadMcpTool(tool, options));
|
|
33
|
+
}
|
|
34
|
+
function shapeOf(tool) {
|
|
35
|
+
const schema = tool.inputSchema;
|
|
36
|
+
if (schema instanceof z.ZodObject)
|
|
37
|
+
return schema.shape;
|
|
38
|
+
throw new Error(`Tool "${tool.name}" has an input schema that is not a z.object, so it has no shape to hand ` +
|
|
39
|
+
`Payload's MCP plugin. Build tool inputs with \`withMeta({ … })\` or \`z.object({ … })\`.`);
|
|
40
|
+
}
|
|
41
|
+
function contextFrom(req, options) {
|
|
42
|
+
const user = req.user
|
|
43
|
+
? {
|
|
44
|
+
id: String(req.user.id ?? ''),
|
|
45
|
+
email: String(req.user.email ?? ''),
|
|
46
|
+
name: String(req.user.name ?? req.user.email ?? ''),
|
|
47
|
+
roles: Array.isArray(req.user.roles) ? req.user.roles : [],
|
|
48
|
+
groups: Array.isArray(req.user.groups) ? req.user.groups : [],
|
|
49
|
+
}
|
|
50
|
+
: null;
|
|
51
|
+
return {
|
|
52
|
+
user,
|
|
53
|
+
/*
|
|
54
|
+
The strategy name is the honest fallback. `plugin-mcp` resolves a key to its
|
|
55
|
+
linked user and does not carry the key document forward, so "which key" is
|
|
56
|
+
not a question the request can answer — and an audit row saying
|
|
57
|
+
`mcp-api-key` is better than one asserting a name nothing checked.
|
|
58
|
+
*/
|
|
59
|
+
apiKeyName: options.apiKeyName ?? (req.payloadAPI === 'MCP' ? 'mcp-api-key' : ''),
|
|
60
|
+
logger: options.logger ?? req.payload?.logger ?? defaultLogger,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=payload-mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-mcp.js","sourceRoot":"","sources":["../../src/mcp/payload-mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AA4DlD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAuB,EACvB,UAAmC,EAAE;IAErC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE3B,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,UAAU,EAAE,KAAK;QACjB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;YAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAA;YAClE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;QAC/E,CAAC;KACF,CAAA;AACH,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,iBAAiB,CAC/B,KAA0B,EAC1B,UAAmC,EAAE;IAErC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;AAC3D,CAAC;AAED,SAAS,OAAO,CAAC,IAAuB;IACtC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAA;IAC/B,IAAI,MAAM,YAAY,CAAC,CAAC,SAAS;QAAE,OAAO,MAAM,CAAC,KAAsB,CAAA;IACvE,MAAM,IAAI,KAAK,CACb,SAAS,IAAI,CAAC,IAAI,2EAA2E;QAC3F,0FAA0F,CAC7F,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAsB,EAAE,OAAgC;IAC3E,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI;QACnB,CAAC,CAAC;YACE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;YAC7B,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACnC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACnD,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,IAAI,CAAC,KAAkB,CAAC,CAAC,CAAC,EAAE;YACxE,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,IAAI,CAAC,MAAmB,CAAC,CAAC,CAAC,EAAE;SAC5E;QACH,CAAC,CAAC,IAAI,CAAA;IAER,OAAO;QACL,IAAI;QACJ;;;;;UAKE;QACF,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,CAAC,GAAG,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,MAAM,IAAI,aAAa;KAC/D,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface DocumentContentHashOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Extra field names to treat as volatile, stripped at every level.
|
|
4
|
+
* For app-specific bookkeeping written on save — a sync timestamp, a
|
|
5
|
+
* cached count — that no approver is looking at.
|
|
6
|
+
*/
|
|
7
|
+
exclude?: string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Hashes the *content* of a document, ignoring the metadata that moves on
|
|
11
|
+
* every save.
|
|
12
|
+
*
|
|
13
|
+
* This is the binding an approval is tied to. Two documents hash the same
|
|
14
|
+
* when an approver would read the same thing, which means:
|
|
15
|
+
*
|
|
16
|
+
* - a save that changed nothing leaves a granted approval standing, and
|
|
17
|
+
* - a save that changed something invalidates it.
|
|
18
|
+
*
|
|
19
|
+
* That is the rule stated deliberately, rather than inherited from
|
|
20
|
+
* whichever timestamp field happened to be nearby.
|
|
21
|
+
*
|
|
22
|
+
* **Both sides must hash a document fetched the same way.** The approvals
|
|
23
|
+
* request tool and publishing's approval step both use
|
|
24
|
+
* `payload.findByID({ collection, id, draft: true })` at the config's
|
|
25
|
+
* default depth, so they see the same shape. A caller that hashes a
|
|
26
|
+
* document fetched at some other depth gets a hash that matches nothing:
|
|
27
|
+
* a populated relationship and a bare relationship id are not the same
|
|
28
|
+
* value, and no amount of normalising makes them one.
|
|
29
|
+
*/
|
|
30
|
+
export declare function documentContentHash(document: Record<string, unknown>, options?: DocumentContentHashOptions): Promise<string>;
|
|
31
|
+
//# sourceMappingURL=content-hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-hash.d.ts","sourceRoot":"","sources":["../../src/utils/content-hash.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,MAAM,CAAC,CAIjB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { sha256Hex } from '../auth/api-keys.js';
|
|
2
|
+
/**
|
|
3
|
+
* Fields that move without the content moving. Stripped at every level of
|
|
4
|
+
* the document before hashing.
|
|
5
|
+
*
|
|
6
|
+
* `updatedAt` is the reason this module exists: an approval bound to it is
|
|
7
|
+
* invalidated by any save at all, including one that changed nothing, and
|
|
8
|
+
* including every tick of autosave. `createdAt`, `_status`, `__v`,
|
|
9
|
+
* `globalType` and `_id` are storage and publishing machinery rather than
|
|
10
|
+
* anything an approver read.
|
|
11
|
+
*
|
|
12
|
+
* The document's own `id` is stripped separately, at the top level only —
|
|
13
|
+
* see `normalize`.
|
|
14
|
+
*/
|
|
15
|
+
const VOLATILE_FIELDS = new Set(['createdAt', 'updatedAt', '_status', '__v', '_id', 'globalType']);
|
|
16
|
+
/**
|
|
17
|
+
* Hashes the *content* of a document, ignoring the metadata that moves on
|
|
18
|
+
* every save.
|
|
19
|
+
*
|
|
20
|
+
* This is the binding an approval is tied to. Two documents hash the same
|
|
21
|
+
* when an approver would read the same thing, which means:
|
|
22
|
+
*
|
|
23
|
+
* - a save that changed nothing leaves a granted approval standing, and
|
|
24
|
+
* - a save that changed something invalidates it.
|
|
25
|
+
*
|
|
26
|
+
* That is the rule stated deliberately, rather than inherited from
|
|
27
|
+
* whichever timestamp field happened to be nearby.
|
|
28
|
+
*
|
|
29
|
+
* **Both sides must hash a document fetched the same way.** The approvals
|
|
30
|
+
* request tool and publishing's approval step both use
|
|
31
|
+
* `payload.findByID({ collection, id, draft: true })` at the config's
|
|
32
|
+
* default depth, so they see the same shape. A caller that hashes a
|
|
33
|
+
* document fetched at some other depth gets a hash that matches nothing:
|
|
34
|
+
* a populated relationship and a bare relationship id are not the same
|
|
35
|
+
* value, and no amount of normalising makes them one.
|
|
36
|
+
*/
|
|
37
|
+
export async function documentContentHash(document, options = {}) {
|
|
38
|
+
const volatile = new Set(VOLATILE_FIELDS);
|
|
39
|
+
for (const field of options.exclude ?? [])
|
|
40
|
+
volatile.add(field);
|
|
41
|
+
return sha256Hex(stableStringify(normalize(document, volatile, true)));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Strips volatile fields and normalises values whose representation can
|
|
45
|
+
* vary without their meaning varying.
|
|
46
|
+
*
|
|
47
|
+
* `id` is dropped only at the top level, where it is the document's own id
|
|
48
|
+
* and constant for the life of the document. Nested `id`s are kept, because
|
|
49
|
+
* that is where a populated relationship carries which document it points
|
|
50
|
+
* at — dropping those would make a page that swapped one image for another
|
|
51
|
+
* with the same alt text hash identically to the one an approver read.
|
|
52
|
+
*/
|
|
53
|
+
function normalize(value, volatile, topLevel = false) {
|
|
54
|
+
if (value instanceof Date)
|
|
55
|
+
return value.toISOString();
|
|
56
|
+
if (Array.isArray(value))
|
|
57
|
+
return value.map((entry) => normalize(entry, volatile));
|
|
58
|
+
if (value === null || typeof value !== 'object')
|
|
59
|
+
return value;
|
|
60
|
+
const result = {};
|
|
61
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
62
|
+
if (volatile.has(key))
|
|
63
|
+
continue;
|
|
64
|
+
if (topLevel && key === 'id')
|
|
65
|
+
continue;
|
|
66
|
+
// A field the caller never set and one set to `undefined` are the same
|
|
67
|
+
// document. JSON.stringify already drops these; doing it here keeps the
|
|
68
|
+
// sorted key list honest.
|
|
69
|
+
if (entry === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
result[key] = normalize(entry, volatile);
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* `JSON.stringify` with object keys in sorted order, so that two documents
|
|
77
|
+
* differing only in key order hash the same. Payload reads blocks back out
|
|
78
|
+
* of JSONB, and neither Postgres nor the JSON parser promises to hand them
|
|
79
|
+
* back in the order they went in.
|
|
80
|
+
*
|
|
81
|
+
* Array order is meaningful — it is the order of the blocks on the page —
|
|
82
|
+
* and is preserved.
|
|
83
|
+
*/
|
|
84
|
+
function stableStringify(value) {
|
|
85
|
+
if (Array.isArray(value)) {
|
|
86
|
+
return `[${value.map(stableStringify).join(',')}]`;
|
|
87
|
+
}
|
|
88
|
+
if (value !== null && typeof value === 'object') {
|
|
89
|
+
const entries = Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
90
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(',')}}`;
|
|
91
|
+
}
|
|
92
|
+
return JSON.stringify(value) ?? 'null';
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=content-hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-hash.js","sourceRoot":"","sources":["../../src/utils/content-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAE/C;;;;;;;;;;;;GAYG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAA;AAWlG;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,QAAiC,EACjC,UAAsC,EAAE;IAExC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAA;IACzC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,IAAI,EAAE;QAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAC9D,OAAO,SAAS,CAAC,eAAe,CAAC,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;AACxE,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,SAAS,CAAC,KAAc,EAAE,QAAqB,EAAE,QAAQ,GAAG,KAAK;IACxE,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,KAAK,CAAC,WAAW,EAAE,CAAA;IACrD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAA;IACjF,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAE7D,MAAM,MAAM,GAA4B,EAAE,CAAA;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;QAC5E,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAQ;QAC/B,IAAI,QAAQ,IAAI,GAAG,KAAK,IAAI;YAAE,SAAQ;QACtC,uEAAuE;QACvE,wEAAwE;QACxE,0BAA0B;QAC1B,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IAC1C,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;IACpD,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CACjF,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAC3B,CAAA;QACD,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;IAC/F,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAA;AACxC,CAAC"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { documentContentHash } from './content-hash.js';
|
|
2
|
+
export type { DocumentContentHashOptions } from './content-hash.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,YAAY,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAA"}
|
package/dist/utils/index.js
CHANGED
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forumone/throughline-core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Core plumbing for Throughline: audit log, MCP authentication and handler, event taxonomy + Inngest client factory,
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Core plumbing for Throughline: audit log, MCP authentication and handler, event taxonomy + Inngest client factory, logger, shared utilities.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -25,10 +25,6 @@
|
|
|
25
25
|
"./mcp": {
|
|
26
26
|
"types": "./dist/mcp/index.d.ts",
|
|
27
27
|
"default": "./dist/mcp/index.js"
|
|
28
|
-
},
|
|
29
|
-
"./env": {
|
|
30
|
-
"types": "./dist/env/index.d.ts",
|
|
31
|
-
"default": "./dist/env/index.js"
|
|
32
28
|
}
|
|
33
29
|
},
|
|
34
30
|
"files": [
|
|
@@ -62,7 +58,7 @@
|
|
|
62
58
|
"dependencies": {
|
|
63
59
|
"zod": "^3.23.0",
|
|
64
60
|
"zod-to-json-schema": "^3.25.2",
|
|
65
|
-
"@forumone/throughline-plugin-contract": "0.
|
|
61
|
+
"@forumone/throughline-plugin-contract": "0.3.0"
|
|
66
62
|
},
|
|
67
63
|
"devDependencies": {
|
|
68
64
|
"@types/node": "^20.17.0",
|
package/dist/env/index.d.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
/**
|
|
3
|
-
* Canonical env-var names used across Throughline packages. Plugins read
|
|
4
|
-
* `process.env[ENV_VARS.X]` rather than hard-coding strings so renames stay
|
|
5
|
-
* coordinated.
|
|
6
|
-
*/
|
|
7
|
-
export declare const ENV_VARS: {
|
|
8
|
-
readonly PAYLOAD_SECRET: "PAYLOAD_SECRET";
|
|
9
|
-
readonly DATABASE_URI: "DATABASE_URI";
|
|
10
|
-
readonly NEXT_PUBLIC_SERVER_URL: "NEXT_PUBLIC_SERVER_URL";
|
|
11
|
-
readonly INNGEST_EVENT_KEY: "INNGEST_EVENT_KEY";
|
|
12
|
-
readonly INNGEST_SIGNING_KEY: "INNGEST_SIGNING_KEY";
|
|
13
|
-
readonly RESEND_API_KEY: "RESEND_API_KEY";
|
|
14
|
-
readonly EMAIL_FROM_ADDRESS: "EMAIL_FROM_ADDRESS";
|
|
15
|
-
readonly EMAIL_FROM_NAME: "EMAIL_FROM_NAME";
|
|
16
|
-
readonly EMAIL_REPLY_TO: "EMAIL_REPLY_TO";
|
|
17
|
-
readonly APPROVAL_TOKEN_SECRET: "APPROVAL_TOKEN_SECRET";
|
|
18
|
-
readonly COMPONENT_SERVER_API_KEY: "COMPONENT_SERVER_API_KEY";
|
|
19
|
-
readonly PUBLISHING_SERVER_API_KEY: "PUBLISHING_SERVER_API_KEY";
|
|
20
|
-
readonly APPROVALS_SERVER_API_KEY: "APPROVALS_SERVER_API_KEY";
|
|
21
|
-
readonly AUDIT_SERVER_API_KEY: "AUDIT_SERVER_API_KEY";
|
|
22
|
-
readonly FORMS_SERVER_API_KEY: "FORMS_SERVER_API_KEY";
|
|
23
|
-
readonly INTEGRATIONS_SERVER_API_KEY: "INTEGRATIONS_SERVER_API_KEY";
|
|
24
|
-
};
|
|
25
|
-
export type EnvVarName = (typeof ENV_VARS)[keyof typeof ENV_VARS];
|
|
26
|
-
declare const BaseEnvSchema: z.ZodObject<{
|
|
27
|
-
PAYLOAD_SECRET: z.ZodString;
|
|
28
|
-
DATABASE_URI: z.ZodString;
|
|
29
|
-
NEXT_PUBLIC_SERVER_URL: z.ZodString;
|
|
30
|
-
}, "strip", z.ZodTypeAny, {
|
|
31
|
-
PAYLOAD_SECRET: string;
|
|
32
|
-
DATABASE_URI: string;
|
|
33
|
-
NEXT_PUBLIC_SERVER_URL: string;
|
|
34
|
-
}, {
|
|
35
|
-
PAYLOAD_SECRET: string;
|
|
36
|
-
DATABASE_URI: string;
|
|
37
|
-
NEXT_PUBLIC_SERVER_URL: string;
|
|
38
|
-
}>;
|
|
39
|
-
export type BaseEnv = z.infer<typeof BaseEnvSchema>;
|
|
40
|
-
/**
|
|
41
|
-
* Validates the base env vars every Throughline deployment requires.
|
|
42
|
-
* Plugins extend with their own checks. Throws on validation failure with
|
|
43
|
-
* a multi-line, path-qualified message.
|
|
44
|
-
*/
|
|
45
|
-
export declare function validateBaseEnv(env?: NodeJS.ProcessEnv): BaseEnv;
|
|
46
|
-
/** Returns an env var, throwing with a clear error if missing or empty. */
|
|
47
|
-
export declare function requireEnv(name: string, message?: string): string;
|
|
48
|
-
/** Returns an optional env var or a fallback. */
|
|
49
|
-
export declare function optionalEnv(name: string, fallback?: string): string | undefined;
|
|
50
|
-
export {};
|
|
51
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/env/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/env/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;GAIG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;CAiBX,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAA;AAEjE,QAAA,MAAM,aAAa;;;;;;;;;;;;EAMjB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAA;AAEnD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAS7E;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAMjE;AAED,iDAAiD;AACjD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE/E"}
|
package/dist/env/index.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
/**
|
|
3
|
-
* Canonical env-var names used across Throughline packages. Plugins read
|
|
4
|
-
* `process.env[ENV_VARS.X]` rather than hard-coding strings so renames stay
|
|
5
|
-
* coordinated.
|
|
6
|
-
*/
|
|
7
|
-
export const ENV_VARS = {
|
|
8
|
-
PAYLOAD_SECRET: 'PAYLOAD_SECRET',
|
|
9
|
-
DATABASE_URI: 'DATABASE_URI',
|
|
10
|
-
NEXT_PUBLIC_SERVER_URL: 'NEXT_PUBLIC_SERVER_URL',
|
|
11
|
-
INNGEST_EVENT_KEY: 'INNGEST_EVENT_KEY',
|
|
12
|
-
INNGEST_SIGNING_KEY: 'INNGEST_SIGNING_KEY',
|
|
13
|
-
RESEND_API_KEY: 'RESEND_API_KEY',
|
|
14
|
-
EMAIL_FROM_ADDRESS: 'EMAIL_FROM_ADDRESS',
|
|
15
|
-
EMAIL_FROM_NAME: 'EMAIL_FROM_NAME',
|
|
16
|
-
EMAIL_REPLY_TO: 'EMAIL_REPLY_TO',
|
|
17
|
-
APPROVAL_TOKEN_SECRET: 'APPROVAL_TOKEN_SECRET',
|
|
18
|
-
COMPONENT_SERVER_API_KEY: 'COMPONENT_SERVER_API_KEY',
|
|
19
|
-
PUBLISHING_SERVER_API_KEY: 'PUBLISHING_SERVER_API_KEY',
|
|
20
|
-
APPROVALS_SERVER_API_KEY: 'APPROVALS_SERVER_API_KEY',
|
|
21
|
-
AUDIT_SERVER_API_KEY: 'AUDIT_SERVER_API_KEY',
|
|
22
|
-
FORMS_SERVER_API_KEY: 'FORMS_SERVER_API_KEY',
|
|
23
|
-
INTEGRATIONS_SERVER_API_KEY: 'INTEGRATIONS_SERVER_API_KEY',
|
|
24
|
-
};
|
|
25
|
-
const BaseEnvSchema = z.object({
|
|
26
|
-
[ENV_VARS.PAYLOAD_SECRET]: z
|
|
27
|
-
.string()
|
|
28
|
-
.min(32, `${ENV_VARS.PAYLOAD_SECRET} must be at least 32 characters`),
|
|
29
|
-
[ENV_VARS.DATABASE_URI]: z.string().min(1, `${ENV_VARS.DATABASE_URI} must be set`),
|
|
30
|
-
[ENV_VARS.NEXT_PUBLIC_SERVER_URL]: z.string().url(),
|
|
31
|
-
});
|
|
32
|
-
/**
|
|
33
|
-
* Validates the base env vars every Throughline deployment requires.
|
|
34
|
-
* Plugins extend with their own checks. Throws on validation failure with
|
|
35
|
-
* a multi-line, path-qualified message.
|
|
36
|
-
*/
|
|
37
|
-
export function validateBaseEnv(env = process.env) {
|
|
38
|
-
const result = BaseEnvSchema.safeParse(env);
|
|
39
|
-
if (!result.success) {
|
|
40
|
-
const issues = result.error.issues
|
|
41
|
-
.map((i) => ` - ${String(i.path[0] ?? '(root)')}: ${i.message}`)
|
|
42
|
-
.join('\n');
|
|
43
|
-
throw new Error(`Invalid environment:\n${issues}`);
|
|
44
|
-
}
|
|
45
|
-
return result.data;
|
|
46
|
-
}
|
|
47
|
-
/** Returns an env var, throwing with a clear error if missing or empty. */
|
|
48
|
-
export function requireEnv(name, message) {
|
|
49
|
-
const value = process.env[name];
|
|
50
|
-
if (!value) {
|
|
51
|
-
throw new Error(message ?? `Environment variable ${name} is required but not set`);
|
|
52
|
-
}
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
|
-
/** Returns an optional env var or a fallback. */
|
|
56
|
-
export function optionalEnv(name, fallback) {
|
|
57
|
-
return process.env[name] ?? fallback;
|
|
58
|
-
}
|
|
59
|
-
//# sourceMappingURL=index.js.map
|
package/dist/env/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/env/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,cAAc,EAAE,gBAAgB;IAChC,YAAY,EAAE,cAAc;IAC5B,sBAAsB,EAAE,wBAAwB;IAChD,iBAAiB,EAAE,mBAAmB;IACtC,mBAAmB,EAAE,qBAAqB;IAC1C,cAAc,EAAE,gBAAgB;IAChC,kBAAkB,EAAE,oBAAoB;IACxC,eAAe,EAAE,iBAAiB;IAClC,cAAc,EAAE,gBAAgB;IAChC,qBAAqB,EAAE,uBAAuB;IAC9C,wBAAwB,EAAE,0BAA0B;IACpD,yBAAyB,EAAE,2BAA2B;IACtD,wBAAwB,EAAE,0BAA0B;IACpD,oBAAoB,EAAE,sBAAsB;IAC5C,oBAAoB,EAAE,sBAAsB;IAC5C,2BAA2B,EAAE,6BAA6B;CAClD,CAAA;AAIV,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;SACzB,MAAM,EAAE;SACR,GAAG,CAAC,EAAE,EAAE,GAAG,QAAQ,CAAC,cAAc,iCAAiC,CAAC;IACvE,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,YAAY,cAAc,CAAC;IAClF,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;CACpD,CAAC,CAAA;AAIF;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IAClE,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;IAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aAChE,IAAI,CAAC,IAAI,CAAC,CAAA;QACb,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,EAAE,CAAC,CAAA;IACpD,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAA;AACpB,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,OAAgB;IACvD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,wBAAwB,IAAI,0BAA0B,CAAC,CAAA;IACpF,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,QAAiB;IACzD,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAA;AACtC,CAAC"}
|
package/dist/utils/diff.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Computes a shallow diff between two records. Returns an entry per
|
|
3
|
-
* field whose value changed, with `before` and `after` values.
|
|
4
|
-
*
|
|
5
|
-
* Field equality is determined by JSON-string equality for objects and
|
|
6
|
-
* arrays, and `===` for primitives. That is sufficient for audit-log
|
|
7
|
-
* payloads where structures are JSON-shaped to begin with.
|
|
8
|
-
*/
|
|
9
|
-
export declare function shallowDiff<T extends Record<string, unknown>>(before: T, after: T): Record<string, {
|
|
10
|
-
before: unknown;
|
|
11
|
-
after: unknown;
|
|
12
|
-
}>;
|
|
13
|
-
//# sourceMappingURL=diff.d.ts.map
|
package/dist/utils/diff.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/utils/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,CAAC,GACP,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,CASrD"}
|
package/dist/utils/diff.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Computes a shallow diff between two records. Returns an entry per
|
|
3
|
-
* field whose value changed, with `before` and `after` values.
|
|
4
|
-
*
|
|
5
|
-
* Field equality is determined by JSON-string equality for objects and
|
|
6
|
-
* arrays, and `===` for primitives. That is sufficient for audit-log
|
|
7
|
-
* payloads where structures are JSON-shaped to begin with.
|
|
8
|
-
*/
|
|
9
|
-
export function shallowDiff(before, after) {
|
|
10
|
-
const diff = {};
|
|
11
|
-
const keys = new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
12
|
-
for (const key of keys) {
|
|
13
|
-
if (!isEqual(before[key], after[key])) {
|
|
14
|
-
diff[key] = { before: before[key], after: after[key] };
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return diff;
|
|
18
|
-
}
|
|
19
|
-
function isEqual(a, b) {
|
|
20
|
-
if (Object.is(a, b))
|
|
21
|
-
return true;
|
|
22
|
-
if (a === null || b === null || a === undefined || b === undefined)
|
|
23
|
-
return false;
|
|
24
|
-
if (typeof a !== typeof b)
|
|
25
|
-
return false;
|
|
26
|
-
if (typeof a === 'object') {
|
|
27
|
-
return JSON.stringify(a) === JSON.stringify(b);
|
|
28
|
-
}
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
//# sourceMappingURL=diff.js.map
|
package/dist/utils/diff.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/utils/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,MAAS,EACT,KAAQ;IAER,MAAM,IAAI,GAAwD,EAAE,CAAA;IACpE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACrE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAA;QACxD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,OAAO,CAAC,CAAU,EAAE,CAAU;IACrC,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAChC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAChF,IAAI,OAAO,CAAC,KAAK,OAAO,CAAC;QAAE,OAAO,KAAK,CAAA;IACvC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;IAChD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
|
package/dist/utils/id.d.ts
DELETED
package/dist/utils/id.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAOlD"}
|
package/dist/utils/id.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates a short, URL-safe, hex-encoded random ID. Optionally prefixed
|
|
3
|
-
* with a token category (e.g. `evt_a1b2c3...`).
|
|
4
|
-
*/
|
|
5
|
-
export function generateId(prefix) {
|
|
6
|
-
const bytes = new Uint8Array(12);
|
|
7
|
-
crypto.getRandomValues(bytes);
|
|
8
|
-
const id = Array.from(bytes)
|
|
9
|
-
.map((b) => b.toString(16).padStart(2, '0'))
|
|
10
|
-
.join('');
|
|
11
|
-
return prefix ? `${prefix}_${id}` : id;
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=id.js.map
|
package/dist/utils/id.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,MAAe;IACxC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAA;IAChC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;IAC7B,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;SACzB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAA;IACX,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;AACxC,CAAC"}
|