@usebetterdev/plugin 0.5.0 → 0.7.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/dist/config-loader.d.ts +23 -0
- package/dist/config-loader.d.ts.map +1 -0
- package/dist/config-types.d.ts +2 -0
- package/dist/config-types.d.ts.map +1 -0
- package/dist/config-validation.d.ts +19 -0
- package/dist/config-validation.d.ts.map +1 -0
- package/dist/config.cjs +142 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +110 -0
- package/dist/config.js.map +1 -0
- package/dist/define-config.d.ts +15 -0
- package/dist/define-config.d.ts.map +1 -0
- package/dist/demo/auth-events.d.ts +34 -0
- package/dist/demo/auth-events.d.ts.map +1 -0
- package/dist/demo/webhook-dispatcher.d.ts +74 -0
- package/dist/demo/webhook-dispatcher.d.ts.map +1 -0
- package/dist/hook-registry.d.ts +68 -0
- package/dist/hook-registry.d.ts.map +1 -0
- package/dist/index.cjs +257 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +242 -0
- package/dist/index.js.map +1 -1
- package/dist/resolve-plugins.d.ts +36 -0
- package/dist/resolve-plugins.d.ts.map +1 -0
- package/dist/schema.d.ts +20 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/types.d.ts +1 -92
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts +11 -0
- package/dist/validation.d.ts.map +1 -0
- package/package.json +12 -2
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { BetterConfig } from "./config-types.js";
|
|
2
|
+
/** Thrown when no `better.config.*` file is found in the working directory. */
|
|
3
|
+
export declare class BetterConfigNotFoundError extends Error {
|
|
4
|
+
readonly name = "BetterConfigNotFoundError";
|
|
5
|
+
readonly cwd: string;
|
|
6
|
+
constructor(cwd: string);
|
|
7
|
+
}
|
|
8
|
+
export interface LoadBetterConfigOptions {
|
|
9
|
+
/** Working directory to search for the config file. Defaults to `process.cwd()`. */
|
|
10
|
+
cwd?: string;
|
|
11
|
+
/** Explicit path to a config file, bypassing discovery. Maps to c12's `configFile` option. */
|
|
12
|
+
configPath?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Loads a `better.config.{ts,js,mjs}` file using c12.
|
|
16
|
+
* Throws {@link BetterConfigNotFoundError} when no config file is found.
|
|
17
|
+
*
|
|
18
|
+
* The returned config is **unvalidated**. Pass it through
|
|
19
|
+
* {@link requireTenantConfig} or {@link requireAuditConfig} to validate
|
|
20
|
+
* product-specific sections.
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadBetterConfig(options?: LoadBetterConfigOptions): Promise<BetterConfig>;
|
|
23
|
+
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../src/config-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,+EAA+E;AAC/E,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,SAAkB,IAAI,+BAA+B;IACrD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;gBAET,GAAG,EAAE,MAAM;CAgBxB;AAED,MAAM,WAAW,uBAAuB;IACtC,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8FAA8F;IAC9F,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,YAAY,CAAC,CAsBvB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export type { TelemetryConfig, PathResolverConfig, SubdomainResolverConfig, StaticJwtResolverConfig, StaticTenantResolverConfig, BetterTenantStaticConfig, RetentionPolicy, BetterAuditStaticConfig, StaticMagicLinkConfig, StaticConsoleSessionConfig, BetterConsoleStaticConfig, BetterConfig, } from "@usebetterdev/contract/config-types";
|
|
2
|
+
//# sourceMappingURL=config-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-types.d.ts","sourceRoot":"","sources":["../src/config-types.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,EACvB,0BAA0B,EAC1B,wBAAwB,EACxB,eAAe,EACf,uBAAuB,EACvB,qBAAqB,EACrB,0BAA0B,EAC1B,yBAAyB,EACzB,YAAY,GACb,MAAM,qCAAqC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BetterConfig, BetterTenantStaticConfig, BetterAuditStaticConfig } from "./config-types.js";
|
|
2
|
+
/** Thrown when a config section fails validation. */
|
|
3
|
+
export declare class ConfigValidationError extends Error {
|
|
4
|
+
readonly name = "ConfigValidationError";
|
|
5
|
+
readonly field: string;
|
|
6
|
+
constructor(field: string, message: string);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Validates that `config.tenant` exists and its `tenantTables` is a non-empty
|
|
10
|
+
* array of non-empty strings. Returns the narrowed tenant config.
|
|
11
|
+
*/
|
|
12
|
+
export declare function requireTenantConfig(config: BetterConfig): BetterTenantStaticConfig;
|
|
13
|
+
/**
|
|
14
|
+
* Validates that `config.audit` exists and its `auditTables` is a non-empty
|
|
15
|
+
* array of non-empty strings. When `retention` is present, validates `days` is
|
|
16
|
+
* a positive integer and `tables` (if present) is a non-empty string array.
|
|
17
|
+
*/
|
|
18
|
+
export declare function requireAuditConfig(config: BetterConfig): BetterAuditStaticConfig;
|
|
19
|
+
//# sourceMappingURL=config-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-validation.d.ts","sourceRoot":"","sources":["../src/config-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,qDAAqD;AACrD,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAkB,IAAI,2BAA2B;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAI3C;AA4BD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,GACnB,wBAAwB,CAM1B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,GACnB,uBAAuB,CAoBzB"}
|
package/dist/config.cjs
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/config.ts
|
|
21
|
+
var config_exports = {};
|
|
22
|
+
__export(config_exports, {
|
|
23
|
+
BetterConfigNotFoundError: () => BetterConfigNotFoundError,
|
|
24
|
+
ConfigValidationError: () => ConfigValidationError,
|
|
25
|
+
defineBetterConfig: () => defineBetterConfig,
|
|
26
|
+
loadBetterConfig: () => loadBetterConfig,
|
|
27
|
+
requireAuditConfig: () => requireAuditConfig,
|
|
28
|
+
requireTenantConfig: () => requireTenantConfig
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(config_exports);
|
|
31
|
+
|
|
32
|
+
// src/define-config.ts
|
|
33
|
+
function defineBetterConfig(config) {
|
|
34
|
+
return config;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/config-loader.ts
|
|
38
|
+
var import_c12 = require("c12");
|
|
39
|
+
var BetterConfigNotFoundError = class extends Error {
|
|
40
|
+
name = "BetterConfigNotFoundError";
|
|
41
|
+
cwd;
|
|
42
|
+
constructor(cwd) {
|
|
43
|
+
super(
|
|
44
|
+
[
|
|
45
|
+
`No better.config.{ts,js,mjs} file found in ${cwd}.`,
|
|
46
|
+
"",
|
|
47
|
+
"Create one with:",
|
|
48
|
+
"",
|
|
49
|
+
' import { defineBetterConfig } from "@usebetterdev/plugin/config";',
|
|
50
|
+
"",
|
|
51
|
+
" export default defineBetterConfig({",
|
|
52
|
+
' tenant: { tenantTables: ["organizations"] },',
|
|
53
|
+
" });"
|
|
54
|
+
].join("\n")
|
|
55
|
+
);
|
|
56
|
+
this.cwd = cwd;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
async function loadBetterConfig(options) {
|
|
60
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
61
|
+
const configFile = options?.configPath;
|
|
62
|
+
const result = await (0, import_c12.loadConfig)({
|
|
63
|
+
name: "better",
|
|
64
|
+
cwd,
|
|
65
|
+
...configFile !== void 0 ? { configFile } : {},
|
|
66
|
+
rcFile: false,
|
|
67
|
+
globalRc: false,
|
|
68
|
+
packageJson: false,
|
|
69
|
+
dotenv: false
|
|
70
|
+
});
|
|
71
|
+
const layers = result.layers ?? [];
|
|
72
|
+
if (layers.length === 0) {
|
|
73
|
+
throw new BetterConfigNotFoundError(cwd);
|
|
74
|
+
}
|
|
75
|
+
return result.config ?? {};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/config-validation.ts
|
|
79
|
+
var ConfigValidationError = class extends Error {
|
|
80
|
+
name = "ConfigValidationError";
|
|
81
|
+
field;
|
|
82
|
+
constructor(field, message) {
|
|
83
|
+
super(`Config error: "${field}" ${message}`);
|
|
84
|
+
this.field = field;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
function isNonEmptyString(value) {
|
|
88
|
+
return typeof value === "string" && value.length > 0;
|
|
89
|
+
}
|
|
90
|
+
function validateStringArray(items, fieldName, sectionName) {
|
|
91
|
+
const qualifiedName = `${sectionName}.${fieldName}`;
|
|
92
|
+
if (!Array.isArray(items)) {
|
|
93
|
+
throw new ConfigValidationError(qualifiedName, "must be an array");
|
|
94
|
+
}
|
|
95
|
+
if (items.length === 0) {
|
|
96
|
+
throw new ConfigValidationError(qualifiedName, "must not be empty");
|
|
97
|
+
}
|
|
98
|
+
for (const item of items) {
|
|
99
|
+
if (!isNonEmptyString(item)) {
|
|
100
|
+
throw new ConfigValidationError(
|
|
101
|
+
qualifiedName,
|
|
102
|
+
"entries must be non-empty strings"
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function requireTenantConfig(config) {
|
|
108
|
+
if (config.tenant === void 0) {
|
|
109
|
+
throw new ConfigValidationError("tenant", "section is required");
|
|
110
|
+
}
|
|
111
|
+
validateStringArray(config.tenant.tenantTables, "tenantTables", "tenant");
|
|
112
|
+
return config.tenant;
|
|
113
|
+
}
|
|
114
|
+
function requireAuditConfig(config) {
|
|
115
|
+
if (config.audit === void 0) {
|
|
116
|
+
throw new ConfigValidationError("audit", "section is required");
|
|
117
|
+
}
|
|
118
|
+
validateStringArray(config.audit.auditTables, "auditTables", "audit");
|
|
119
|
+
if (config.audit.retention !== void 0) {
|
|
120
|
+
const { days, tables } = config.audit.retention;
|
|
121
|
+
if (!Number.isInteger(days) || days <= 0) {
|
|
122
|
+
throw new ConfigValidationError(
|
|
123
|
+
"audit.retention.days",
|
|
124
|
+
"must be a positive integer"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
if (tables !== void 0) {
|
|
128
|
+
validateStringArray(tables, "retention.tables", "audit");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return config.audit;
|
|
132
|
+
}
|
|
133
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
134
|
+
0 && (module.exports = {
|
|
135
|
+
BetterConfigNotFoundError,
|
|
136
|
+
ConfigValidationError,
|
|
137
|
+
defineBetterConfig,
|
|
138
|
+
loadBetterConfig,
|
|
139
|
+
requireAuditConfig,
|
|
140
|
+
requireTenantConfig
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=config.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/define-config.ts","../src/config-loader.ts","../src/config-validation.ts"],"sourcesContent":["// ── Config helpers ───────────────────────────────────────────────────────────\n\nexport { defineBetterConfig } from \"./define-config.js\";\n\nexport {\n loadBetterConfig,\n BetterConfigNotFoundError,\n} from \"./config-loader.js\";\nexport type { LoadBetterConfigOptions } from \"./config-loader.js\";\n\nexport {\n requireTenantConfig,\n requireAuditConfig,\n ConfigValidationError,\n} from \"./config-validation.js\";\n\n// ── Re-export config types ──────────────────────────────────────────────────\n\nexport type {\n BetterConfig,\n BetterTenantStaticConfig,\n BetterAuditStaticConfig,\n BetterConsoleStaticConfig,\n TelemetryConfig,\n PathResolverConfig,\n SubdomainResolverConfig,\n StaticJwtResolverConfig,\n StaticTenantResolverConfig,\n RetentionPolicy,\n StaticMagicLinkConfig,\n StaticConsoleSessionConfig,\n} from \"./config-types.js\";\n","import type { BetterConfig } from \"./config-types.js\";\n\n/**\n * Identity helper that provides TypeScript autocomplete for `better.config.ts`.\n *\n * ```ts\n * // better.config.ts\n * import { defineBetterConfig } from \"@usebetterdev/plugin/config\";\n *\n * export default defineBetterConfig({\n * tenant: { tenantTables: [\"organizations\"] },\n * });\n * ```\n */\nexport function defineBetterConfig(config: BetterConfig): BetterConfig {\n return config;\n}\n","import { loadConfig } from \"c12\";\nimport type { BetterConfig } from \"./config-types.js\";\n\n/** Thrown when no `better.config.*` file is found in the working directory. */\nexport class BetterConfigNotFoundError extends Error {\n override readonly name = \"BetterConfigNotFoundError\";\n readonly cwd: string;\n\n constructor(cwd: string) {\n super(\n [\n `No better.config.{ts,js,mjs} file found in ${cwd}.`,\n \"\",\n \"Create one with:\",\n \"\",\n ' import { defineBetterConfig } from \"@usebetterdev/plugin/config\";',\n \"\",\n \" export default defineBetterConfig({\",\n ' tenant: { tenantTables: [\"organizations\"] },',\n \" });\",\n ].join(\"\\n\"),\n );\n this.cwd = cwd;\n }\n}\n\nexport interface LoadBetterConfigOptions {\n /** Working directory to search for the config file. Defaults to `process.cwd()`. */\n cwd?: string;\n /** Explicit path to a config file, bypassing discovery. Maps to c12's `configFile` option. */\n configPath?: string;\n}\n\n/**\n * Loads a `better.config.{ts,js,mjs}` file using c12.\n * Throws {@link BetterConfigNotFoundError} when no config file is found.\n *\n * The returned config is **unvalidated**. Pass it through\n * {@link requireTenantConfig} or {@link requireAuditConfig} to validate\n * product-specific sections.\n */\nexport async function loadBetterConfig(\n options?: LoadBetterConfigOptions,\n): Promise<BetterConfig> {\n const cwd = options?.cwd ?? process.cwd();\n\n // configPath maps to c12's configFile — spread conditionally to satisfy exactOptionalPropertyTypes\n const configFile = options?.configPath;\n\n const result = await loadConfig<BetterConfig>({\n name: \"better\",\n cwd,\n ...(configFile !== undefined ? { configFile } : {}),\n rcFile: false,\n globalRc: false,\n packageJson: false,\n dotenv: false,\n });\n\n const layers = result.layers ?? [];\n if (layers.length === 0) {\n throw new BetterConfigNotFoundError(cwd);\n }\n\n return result.config ?? {};\n}\n","import type {\n BetterConfig,\n BetterTenantStaticConfig,\n BetterAuditStaticConfig,\n} from \"./config-types.js\";\n\n/** Thrown when a config section fails validation. */\nexport class ConfigValidationError extends Error {\n override readonly name = \"ConfigValidationError\";\n readonly field: string;\n\n constructor(field: string, message: string) {\n super(`Config error: \"${field}\" ${message}`);\n this.field = field;\n }\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction validateStringArray(\n items: unknown,\n fieldName: string,\n sectionName: string,\n): void {\n const qualifiedName = `${sectionName}.${fieldName}`;\n if (!Array.isArray(items)) {\n throw new ConfigValidationError(qualifiedName, \"must be an array\");\n }\n if (items.length === 0) {\n throw new ConfigValidationError(qualifiedName, \"must not be empty\");\n }\n for (const item of items) {\n if (!isNonEmptyString(item)) {\n throw new ConfigValidationError(\n qualifiedName,\n \"entries must be non-empty strings\",\n );\n }\n }\n}\n\n/**\n * Validates that `config.tenant` exists and its `tenantTables` is a non-empty\n * array of non-empty strings. Returns the narrowed tenant config.\n */\nexport function requireTenantConfig(\n config: BetterConfig,\n): BetterTenantStaticConfig {\n if (config.tenant === undefined) {\n throw new ConfigValidationError(\"tenant\", \"section is required\");\n }\n validateStringArray(config.tenant.tenantTables, \"tenantTables\", \"tenant\");\n return config.tenant;\n}\n\n/**\n * Validates that `config.audit` exists and its `auditTables` is a non-empty\n * array of non-empty strings. When `retention` is present, validates `days` is\n * a positive integer and `tables` (if present) is a non-empty string array.\n */\nexport function requireAuditConfig(\n config: BetterConfig,\n): BetterAuditStaticConfig {\n if (config.audit === undefined) {\n throw new ConfigValidationError(\"audit\", \"section is required\");\n }\n validateStringArray(config.audit.auditTables, \"auditTables\", \"audit\");\n\n if (config.audit.retention !== undefined) {\n const { days, tables } = config.audit.retention;\n if (!Number.isInteger(days) || days <= 0) {\n throw new ConfigValidationError(\n \"audit.retention.days\",\n \"must be a positive integer\",\n );\n }\n if (tables !== undefined) {\n validateStringArray(tables, \"retention.tables\", \"audit\");\n }\n }\n\n return config.audit;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACcO,SAAS,mBAAmB,QAAoC;AACrE,SAAO;AACT;;;AChBA,iBAA2B;AAIpB,IAAM,4BAAN,cAAwC,MAAM;AAAA,EACjC,OAAO;AAAA,EAChB;AAAA,EAET,YAAY,KAAa;AACvB;AAAA,MACE;AAAA,QACE,8CAA8C,GAAG;AAAA,QACjD;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AACA,SAAK,MAAM;AAAA,EACb;AACF;AAiBA,eAAsB,iBACpB,SACuB;AACvB,QAAM,MAAM,SAAS,OAAO,QAAQ,IAAI;AAGxC,QAAM,aAAa,SAAS;AAE5B,QAAM,SAAS,UAAM,uBAAyB;AAAA,IAC5C,MAAM;AAAA,IACN;AAAA,IACA,GAAI,eAAe,SAAY,EAAE,WAAW,IAAI,CAAC;AAAA,IACjD,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,aAAa;AAAA,IACb,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,SAAS,OAAO,UAAU,CAAC;AACjC,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,IAAI,0BAA0B,GAAG;AAAA,EACzC;AAEA,SAAO,OAAO,UAAU,CAAC;AAC3B;;;AC1DO,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAC7B,OAAO;AAAA,EAChB;AAAA,EAET,YAAY,OAAe,SAAiB;AAC1C,UAAM,kBAAkB,KAAK,KAAK,OAAO,EAAE;AAC3C,SAAK,QAAQ;AAAA,EACf;AACF;AAEA,SAAS,iBAAiB,OAAiC;AACzD,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS;AACrD;AAEA,SAAS,oBACP,OACA,WACA,aACM;AACN,QAAM,gBAAgB,GAAG,WAAW,IAAI,SAAS;AACjD,MAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzB,UAAM,IAAI,sBAAsB,eAAe,kBAAkB;AAAA,EACnE;AACA,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,sBAAsB,eAAe,mBAAmB;AAAA,EACpE;AACA,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,iBAAiB,IAAI,GAAG;AAC3B,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAMO,SAAS,oBACd,QAC0B;AAC1B,MAAI,OAAO,WAAW,QAAW;AAC/B,UAAM,IAAI,sBAAsB,UAAU,qBAAqB;AAAA,EACjE;AACA,sBAAoB,OAAO,OAAO,cAAc,gBAAgB,QAAQ;AACxE,SAAO,OAAO;AAChB;AAOO,SAAS,mBACd,QACyB;AACzB,MAAI,OAAO,UAAU,QAAW;AAC9B,UAAM,IAAI,sBAAsB,SAAS,qBAAqB;AAAA,EAChE;AACA,sBAAoB,OAAO,MAAM,aAAa,eAAe,OAAO;AAEpE,MAAI,OAAO,MAAM,cAAc,QAAW;AACxC,UAAM,EAAE,MAAM,OAAO,IAAI,OAAO,MAAM;AACtC,QAAI,CAAC,OAAO,UAAU,IAAI,KAAK,QAAQ,GAAG;AACxC,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,WAAW,QAAW;AACxB,0BAAoB,QAAQ,oBAAoB,OAAO;AAAA,IACzD;AAAA,EACF;AAEA,SAAO,OAAO;AAChB;","names":[]}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { defineBetterConfig } from "./define-config.js";
|
|
2
|
+
export { loadBetterConfig, BetterConfigNotFoundError, } from "./config-loader.js";
|
|
3
|
+
export type { LoadBetterConfigOptions } from "./config-loader.js";
|
|
4
|
+
export { requireTenantConfig, requireAuditConfig, ConfigValidationError, } from "./config-validation.js";
|
|
5
|
+
export type { BetterConfig, BetterTenantStaticConfig, BetterAuditStaticConfig, BetterConsoleStaticConfig, TelemetryConfig, PathResolverConfig, SubdomainResolverConfig, StaticJwtResolverConfig, StaticTenantResolverConfig, RetentionPolicy, StaticMagicLinkConfig, StaticConsoleSessionConfig, } from "./config-types.js";
|
|
6
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EACL,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAIhC,YAAY,EACV,YAAY,EACZ,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,EACvB,0BAA0B,EAC1B,eAAe,EACf,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,mBAAmB,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// src/define-config.ts
|
|
2
|
+
function defineBetterConfig(config) {
|
|
3
|
+
return config;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/config-loader.ts
|
|
7
|
+
import { loadConfig } from "c12";
|
|
8
|
+
var BetterConfigNotFoundError = class extends Error {
|
|
9
|
+
name = "BetterConfigNotFoundError";
|
|
10
|
+
cwd;
|
|
11
|
+
constructor(cwd) {
|
|
12
|
+
super(
|
|
13
|
+
[
|
|
14
|
+
`No better.config.{ts,js,mjs} file found in ${cwd}.`,
|
|
15
|
+
"",
|
|
16
|
+
"Create one with:",
|
|
17
|
+
"",
|
|
18
|
+
' import { defineBetterConfig } from "@usebetterdev/plugin/config";',
|
|
19
|
+
"",
|
|
20
|
+
" export default defineBetterConfig({",
|
|
21
|
+
' tenant: { tenantTables: ["organizations"] },',
|
|
22
|
+
" });"
|
|
23
|
+
].join("\n")
|
|
24
|
+
);
|
|
25
|
+
this.cwd = cwd;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
async function loadBetterConfig(options) {
|
|
29
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
30
|
+
const configFile = options?.configPath;
|
|
31
|
+
const result = await loadConfig({
|
|
32
|
+
name: "better",
|
|
33
|
+
cwd,
|
|
34
|
+
...configFile !== void 0 ? { configFile } : {},
|
|
35
|
+
rcFile: false,
|
|
36
|
+
globalRc: false,
|
|
37
|
+
packageJson: false,
|
|
38
|
+
dotenv: false
|
|
39
|
+
});
|
|
40
|
+
const layers = result.layers ?? [];
|
|
41
|
+
if (layers.length === 0) {
|
|
42
|
+
throw new BetterConfigNotFoundError(cwd);
|
|
43
|
+
}
|
|
44
|
+
return result.config ?? {};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/config-validation.ts
|
|
48
|
+
var ConfigValidationError = class extends Error {
|
|
49
|
+
name = "ConfigValidationError";
|
|
50
|
+
field;
|
|
51
|
+
constructor(field, message) {
|
|
52
|
+
super(`Config error: "${field}" ${message}`);
|
|
53
|
+
this.field = field;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
function isNonEmptyString(value) {
|
|
57
|
+
return typeof value === "string" && value.length > 0;
|
|
58
|
+
}
|
|
59
|
+
function validateStringArray(items, fieldName, sectionName) {
|
|
60
|
+
const qualifiedName = `${sectionName}.${fieldName}`;
|
|
61
|
+
if (!Array.isArray(items)) {
|
|
62
|
+
throw new ConfigValidationError(qualifiedName, "must be an array");
|
|
63
|
+
}
|
|
64
|
+
if (items.length === 0) {
|
|
65
|
+
throw new ConfigValidationError(qualifiedName, "must not be empty");
|
|
66
|
+
}
|
|
67
|
+
for (const item of items) {
|
|
68
|
+
if (!isNonEmptyString(item)) {
|
|
69
|
+
throw new ConfigValidationError(
|
|
70
|
+
qualifiedName,
|
|
71
|
+
"entries must be non-empty strings"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function requireTenantConfig(config) {
|
|
77
|
+
if (config.tenant === void 0) {
|
|
78
|
+
throw new ConfigValidationError("tenant", "section is required");
|
|
79
|
+
}
|
|
80
|
+
validateStringArray(config.tenant.tenantTables, "tenantTables", "tenant");
|
|
81
|
+
return config.tenant;
|
|
82
|
+
}
|
|
83
|
+
function requireAuditConfig(config) {
|
|
84
|
+
if (config.audit === void 0) {
|
|
85
|
+
throw new ConfigValidationError("audit", "section is required");
|
|
86
|
+
}
|
|
87
|
+
validateStringArray(config.audit.auditTables, "auditTables", "audit");
|
|
88
|
+
if (config.audit.retention !== void 0) {
|
|
89
|
+
const { days, tables } = config.audit.retention;
|
|
90
|
+
if (!Number.isInteger(days) || days <= 0) {
|
|
91
|
+
throw new ConfigValidationError(
|
|
92
|
+
"audit.retention.days",
|
|
93
|
+
"must be a positive integer"
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
if (tables !== void 0) {
|
|
97
|
+
validateStringArray(tables, "retention.tables", "audit");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return config.audit;
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
BetterConfigNotFoundError,
|
|
104
|
+
ConfigValidationError,
|
|
105
|
+
defineBetterConfig,
|
|
106
|
+
loadBetterConfig,
|
|
107
|
+
requireAuditConfig,
|
|
108
|
+
requireTenantConfig
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/define-config.ts","../src/config-loader.ts","../src/config-validation.ts"],"sourcesContent":["import type { BetterConfig } from \"./config-types.js\";\n\n/**\n * Identity helper that provides TypeScript autocomplete for `better.config.ts`.\n *\n * ```ts\n * // better.config.ts\n * import { defineBetterConfig } from \"@usebetterdev/plugin/config\";\n *\n * export default defineBetterConfig({\n * tenant: { tenantTables: [\"organizations\"] },\n * });\n * ```\n */\nexport function defineBetterConfig(config: BetterConfig): BetterConfig {\n return config;\n}\n","import { loadConfig } from \"c12\";\nimport type { BetterConfig } from \"./config-types.js\";\n\n/** Thrown when no `better.config.*` file is found in the working directory. */\nexport class BetterConfigNotFoundError extends Error {\n override readonly name = \"BetterConfigNotFoundError\";\n readonly cwd: string;\n\n constructor(cwd: string) {\n super(\n [\n `No better.config.{ts,js,mjs} file found in ${cwd}.`,\n \"\",\n \"Create one with:\",\n \"\",\n ' import { defineBetterConfig } from \"@usebetterdev/plugin/config\";',\n \"\",\n \" export default defineBetterConfig({\",\n ' tenant: { tenantTables: [\"organizations\"] },',\n \" });\",\n ].join(\"\\n\"),\n );\n this.cwd = cwd;\n }\n}\n\nexport interface LoadBetterConfigOptions {\n /** Working directory to search for the config file. Defaults to `process.cwd()`. */\n cwd?: string;\n /** Explicit path to a config file, bypassing discovery. Maps to c12's `configFile` option. */\n configPath?: string;\n}\n\n/**\n * Loads a `better.config.{ts,js,mjs}` file using c12.\n * Throws {@link BetterConfigNotFoundError} when no config file is found.\n *\n * The returned config is **unvalidated**. Pass it through\n * {@link requireTenantConfig} or {@link requireAuditConfig} to validate\n * product-specific sections.\n */\nexport async function loadBetterConfig(\n options?: LoadBetterConfigOptions,\n): Promise<BetterConfig> {\n const cwd = options?.cwd ?? process.cwd();\n\n // configPath maps to c12's configFile — spread conditionally to satisfy exactOptionalPropertyTypes\n const configFile = options?.configPath;\n\n const result = await loadConfig<BetterConfig>({\n name: \"better\",\n cwd,\n ...(configFile !== undefined ? { configFile } : {}),\n rcFile: false,\n globalRc: false,\n packageJson: false,\n dotenv: false,\n });\n\n const layers = result.layers ?? [];\n if (layers.length === 0) {\n throw new BetterConfigNotFoundError(cwd);\n }\n\n return result.config ?? {};\n}\n","import type {\n BetterConfig,\n BetterTenantStaticConfig,\n BetterAuditStaticConfig,\n} from \"./config-types.js\";\n\n/** Thrown when a config section fails validation. */\nexport class ConfigValidationError extends Error {\n override readonly name = \"ConfigValidationError\";\n readonly field: string;\n\n constructor(field: string, message: string) {\n super(`Config error: \"${field}\" ${message}`);\n this.field = field;\n }\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction validateStringArray(\n items: unknown,\n fieldName: string,\n sectionName: string,\n): void {\n const qualifiedName = `${sectionName}.${fieldName}`;\n if (!Array.isArray(items)) {\n throw new ConfigValidationError(qualifiedName, \"must be an array\");\n }\n if (items.length === 0) {\n throw new ConfigValidationError(qualifiedName, \"must not be empty\");\n }\n for (const item of items) {\n if (!isNonEmptyString(item)) {\n throw new ConfigValidationError(\n qualifiedName,\n \"entries must be non-empty strings\",\n );\n }\n }\n}\n\n/**\n * Validates that `config.tenant` exists and its `tenantTables` is a non-empty\n * array of non-empty strings. Returns the narrowed tenant config.\n */\nexport function requireTenantConfig(\n config: BetterConfig,\n): BetterTenantStaticConfig {\n if (config.tenant === undefined) {\n throw new ConfigValidationError(\"tenant\", \"section is required\");\n }\n validateStringArray(config.tenant.tenantTables, \"tenantTables\", \"tenant\");\n return config.tenant;\n}\n\n/**\n * Validates that `config.audit` exists and its `auditTables` is a non-empty\n * array of non-empty strings. When `retention` is present, validates `days` is\n * a positive integer and `tables` (if present) is a non-empty string array.\n */\nexport function requireAuditConfig(\n config: BetterConfig,\n): BetterAuditStaticConfig {\n if (config.audit === undefined) {\n throw new ConfigValidationError(\"audit\", \"section is required\");\n }\n validateStringArray(config.audit.auditTables, \"auditTables\", \"audit\");\n\n if (config.audit.retention !== undefined) {\n const { days, tables } = config.audit.retention;\n if (!Number.isInteger(days) || days <= 0) {\n throw new ConfigValidationError(\n \"audit.retention.days\",\n \"must be a positive integer\",\n );\n }\n if (tables !== undefined) {\n validateStringArray(tables, \"retention.tables\", \"audit\");\n }\n }\n\n return config.audit;\n}\n"],"mappings":";AAcO,SAAS,mBAAmB,QAAoC;AACrE,SAAO;AACT;;;AChBA,SAAS,kBAAkB;AAIpB,IAAM,4BAAN,cAAwC,MAAM;AAAA,EACjC,OAAO;AAAA,EAChB;AAAA,EAET,YAAY,KAAa;AACvB;AAAA,MACE;AAAA,QACE,8CAA8C,GAAG;AAAA,QACjD;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AACA,SAAK,MAAM;AAAA,EACb;AACF;AAiBA,eAAsB,iBACpB,SACuB;AACvB,QAAM,MAAM,SAAS,OAAO,QAAQ,IAAI;AAGxC,QAAM,aAAa,SAAS;AAE5B,QAAM,SAAS,MAAM,WAAyB;AAAA,IAC5C,MAAM;AAAA,IACN;AAAA,IACA,GAAI,eAAe,SAAY,EAAE,WAAW,IAAI,CAAC;AAAA,IACjD,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,aAAa;AAAA,IACb,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,SAAS,OAAO,UAAU,CAAC;AACjC,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,IAAI,0BAA0B,GAAG;AAAA,EACzC;AAEA,SAAO,OAAO,UAAU,CAAC;AAC3B;;;AC1DO,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAC7B,OAAO;AAAA,EAChB;AAAA,EAET,YAAY,OAAe,SAAiB;AAC1C,UAAM,kBAAkB,KAAK,KAAK,OAAO,EAAE;AAC3C,SAAK,QAAQ;AAAA,EACf;AACF;AAEA,SAAS,iBAAiB,OAAiC;AACzD,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS;AACrD;AAEA,SAAS,oBACP,OACA,WACA,aACM;AACN,QAAM,gBAAgB,GAAG,WAAW,IAAI,SAAS;AACjD,MAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzB,UAAM,IAAI,sBAAsB,eAAe,kBAAkB;AAAA,EACnE;AACA,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,sBAAsB,eAAe,mBAAmB;AAAA,EACpE;AACA,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,iBAAiB,IAAI,GAAG;AAC3B,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAMO,SAAS,oBACd,QAC0B;AAC1B,MAAI,OAAO,WAAW,QAAW;AAC/B,UAAM,IAAI,sBAAsB,UAAU,qBAAqB;AAAA,EACjE;AACA,sBAAoB,OAAO,OAAO,cAAc,gBAAgB,QAAQ;AACxE,SAAO,OAAO;AAChB;AAOO,SAAS,mBACd,QACyB;AACzB,MAAI,OAAO,UAAU,QAAW;AAC9B,UAAM,IAAI,sBAAsB,SAAS,qBAAqB;AAAA,EAChE;AACA,sBAAoB,OAAO,MAAM,aAAa,eAAe,OAAO;AAEpE,MAAI,OAAO,MAAM,cAAc,QAAW;AACxC,UAAM,EAAE,MAAM,OAAO,IAAI,OAAO,MAAM;AACtC,QAAI,CAAC,OAAO,UAAU,IAAI,KAAK,QAAQ,GAAG;AACxC,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,WAAW,QAAW;AACxB,0BAAoB,QAAQ,oBAAoB,OAAO;AAAA,IACzD;AAAA,EACF;AAEA,SAAO,OAAO;AAChB;","names":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BetterConfig } from "./config-types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Identity helper that provides TypeScript autocomplete for `better.config.ts`.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* // better.config.ts
|
|
7
|
+
* import { defineBetterConfig } from "@usebetterdev/plugin/config";
|
|
8
|
+
*
|
|
9
|
+
* export default defineBetterConfig({
|
|
10
|
+
* tenant: { tenantTables: ["organizations"] },
|
|
11
|
+
* });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare function defineBetterConfig(config: BetterConfig): BetterConfig;
|
|
15
|
+
//# sourceMappingURL=define-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-config.d.ts","sourceRoot":"","sources":["../src/define-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAErE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare const authEvents: () => {
|
|
2
|
+
id: string;
|
|
3
|
+
schema: {
|
|
4
|
+
tables: {
|
|
5
|
+
auth_events: {
|
|
6
|
+
fields: {
|
|
7
|
+
id: {
|
|
8
|
+
type: "string";
|
|
9
|
+
required: true;
|
|
10
|
+
unique: true;
|
|
11
|
+
};
|
|
12
|
+
event_type: {
|
|
13
|
+
type: "string";
|
|
14
|
+
required: true;
|
|
15
|
+
};
|
|
16
|
+
user_id: {
|
|
17
|
+
type: "string";
|
|
18
|
+
required: true;
|
|
19
|
+
index: true;
|
|
20
|
+
};
|
|
21
|
+
timestamp: {
|
|
22
|
+
type: "date";
|
|
23
|
+
required: true;
|
|
24
|
+
defaultValue: () => Date;
|
|
25
|
+
};
|
|
26
|
+
metadata: {
|
|
27
|
+
type: "json";
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=auth-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-events.d.ts","sourceRoot":"","sources":["../../src/demo/auth-events.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsBtB,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export type AuditHookContract = {
|
|
2
|
+
afterLog: {
|
|
3
|
+
log: Readonly<{
|
|
4
|
+
id: string;
|
|
5
|
+
tableName: string;
|
|
6
|
+
}>;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export interface WebhookDispatcherConfig {
|
|
10
|
+
/** Target URL to POST event payloads to. */
|
|
11
|
+
url: string;
|
|
12
|
+
/** Optional list of event types to forward. When omitted, all events are forwarded. */
|
|
13
|
+
events?: string[];
|
|
14
|
+
}
|
|
15
|
+
export declare const webhookDispatcher: (config: WebhookDispatcherConfig) => {
|
|
16
|
+
id: string;
|
|
17
|
+
hooks: {
|
|
18
|
+
afterLog: (_payload: {
|
|
19
|
+
log: Readonly<{
|
|
20
|
+
id: string;
|
|
21
|
+
tableName: string;
|
|
22
|
+
}>;
|
|
23
|
+
}) => Promise<void>;
|
|
24
|
+
};
|
|
25
|
+
schema: {
|
|
26
|
+
tables: {
|
|
27
|
+
webhook_deliveries: {
|
|
28
|
+
fields: {
|
|
29
|
+
id: {
|
|
30
|
+
type: "string";
|
|
31
|
+
required: true;
|
|
32
|
+
unique: true;
|
|
33
|
+
};
|
|
34
|
+
endpoint_url: {
|
|
35
|
+
type: "string";
|
|
36
|
+
required: true;
|
|
37
|
+
index: true;
|
|
38
|
+
};
|
|
39
|
+
event_type: {
|
|
40
|
+
type: "string";
|
|
41
|
+
required: true;
|
|
42
|
+
};
|
|
43
|
+
status: {
|
|
44
|
+
type: "string";
|
|
45
|
+
required: true;
|
|
46
|
+
};
|
|
47
|
+
http_status: {
|
|
48
|
+
type: "number";
|
|
49
|
+
};
|
|
50
|
+
attempt_count: {
|
|
51
|
+
type: "number";
|
|
52
|
+
required: true;
|
|
53
|
+
defaultValue: number;
|
|
54
|
+
};
|
|
55
|
+
last_attempt_at: {
|
|
56
|
+
type: "date";
|
|
57
|
+
};
|
|
58
|
+
created_at: {
|
|
59
|
+
type: "date";
|
|
60
|
+
required: true;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
extend: {
|
|
66
|
+
audit_log: {
|
|
67
|
+
webhook_delivered_at: {
|
|
68
|
+
type: "date";
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=webhook-dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-dispatcher.d.ts","sourceRoot":"","sources":["../../src/demo/webhook-dispatcher.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE;QAAE,GAAG,EAAE,QAAQ,CAAC;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;CAChE,CAAC;AAEF,MAAM,WAAW,uBAAuB;IACtC,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,eAAO,MAAM,iBAAiB,WAAY,uBAAuB;;;;iBAV9C,QAAQ,CAAC;gBAAE,EAAE,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6C7D,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { HookContract, HookHandlers } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Error captured when a plugin hook handler fails during `invokeAfter`.
|
|
4
|
+
*/
|
|
5
|
+
export interface PluginError {
|
|
6
|
+
pluginId: string;
|
|
7
|
+
hook: string;
|
|
8
|
+
error: unknown;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Error thrown by `invokeBefore` when a plugin hook handler fails.
|
|
12
|
+
* Wraps the original error with plugin and hook context so callers
|
|
13
|
+
* can identify which plugin caused the failure.
|
|
14
|
+
*/
|
|
15
|
+
export declare class PluginInvokeError extends Error {
|
|
16
|
+
readonly name = "PluginInvokeError";
|
|
17
|
+
readonly pluginId: string;
|
|
18
|
+
readonly hook: string;
|
|
19
|
+
constructor(pluginId: string, hook: string, cause: unknown);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Runtime registry that products use to register plugin hooks and invoke them
|
|
23
|
+
* at lifecycle points.
|
|
24
|
+
*
|
|
25
|
+
* Generic over THooks so products get type-safe hook names and payloads.
|
|
26
|
+
*/
|
|
27
|
+
export declare class HookRegistry<THooks extends HookContract> {
|
|
28
|
+
private hooks;
|
|
29
|
+
private registeredPlugins;
|
|
30
|
+
/**
|
|
31
|
+
* Register all hooks declared by a plugin. Skips entries whose value is not
|
|
32
|
+
* a function (defensive against partial/optional declarations).
|
|
33
|
+
*
|
|
34
|
+
* Throws if a plugin with the same ID has already been registered.
|
|
35
|
+
*/
|
|
36
|
+
register(pluginId: string, hooks: Partial<HookHandlers<THooks>>): void;
|
|
37
|
+
/**
|
|
38
|
+
* Sequential, fail-fast invocation — for "before" hooks.
|
|
39
|
+
* Runs handlers in registration order. Aborts on first throw and wraps
|
|
40
|
+
* the error in a {@link PluginInvokeError} so the caller knows which
|
|
41
|
+
* plugin failed. Later handlers are **not** called.
|
|
42
|
+
*
|
|
43
|
+
* Uses a snapshot of the handler list so that registrations during
|
|
44
|
+
* async execution do not affect the current invocation.
|
|
45
|
+
*/
|
|
46
|
+
invokeBefore<K extends keyof THooks & string>(hook: K, payload: THooks[K]): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Sequential, collect-errors invocation — for "after" hooks.
|
|
49
|
+
* Runs **all** handlers even when some fail. Failures are wrapped in
|
|
50
|
+
* {@link PluginError} and returned as an array. Returns an empty array
|
|
51
|
+
* when all handlers succeed or when no handlers are registered.
|
|
52
|
+
*
|
|
53
|
+
* Uses a snapshot of the handler list so that registrations during
|
|
54
|
+
* async execution do not affect the current invocation.
|
|
55
|
+
*/
|
|
56
|
+
invokeAfter<K extends keyof THooks & string>(hook: K, payload: THooks[K]): Promise<PluginError[]>;
|
|
57
|
+
/**
|
|
58
|
+
* Calls a stored handler with the given payload.
|
|
59
|
+
*
|
|
60
|
+
* Handlers are stored as `(payload: never) => …` for variance compatibility
|
|
61
|
+
* (see {@link RegisteredHook}). At runtime the payload always matches
|
|
62
|
+
* `THooks[K]` — guaranteed by the generic constraint on {@link register}.
|
|
63
|
+
* This single assertion is the only place where TypeScript's type system
|
|
64
|
+
* cannot statically verify the heterogeneous map access.
|
|
65
|
+
*/
|
|
66
|
+
private callHandler;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=hook-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-registry.d.ts","sourceRoot":"","sources":["../src/hook-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,SAAkB,IAAI,uBAAuB;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAQ3D;AAYD;;;;;GAKG;AACH,qBAAa,YAAY,CAAC,MAAM,SAAS,YAAY;IACnD,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,iBAAiB,CAAqB;IAE9C;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI;IAoBtE;;;;;;;;OAQG;IACG,YAAY,CAAC,CAAC,SAAS,MAAM,MAAM,GAAG,MAAM,EAChD,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,GACjB,OAAO,CAAC,IAAI,CAAC;IAehB;;;;;;;;OAQG;IACG,WAAW,CAAC,CAAC,SAAS,MAAM,MAAM,GAAG,MAAM,EAC/C,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,GACjB,OAAO,CAAC,WAAW,EAAE,CAAC;IAsBzB;;;;;;;;OAQG;IACH,OAAO,CAAC,WAAW;CAMpB"}
|