@noctcore/lint-meta-rules 0.3.0 → 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/README.md +22 -0
- package/dist/chunk-VFCX3QKZ.js +47 -0
- package/dist/prisma.cjs +442 -0
- package/dist/prisma.d.cts +174 -0
- package/dist/prisma.d.ts +174 -0
- package/dist/prisma.js +356 -0
- package/dist/resolved-config.cjs +144 -0
- package/dist/resolved-config.d.cts +46 -0
- package/dist/resolved-config.d.ts +46 -0
- package/dist/resolved-config.js +75 -0
- package/docs/rules/eslint-config-no-warn.md +71 -0
- package/docs/rules/prisma-method-surface.md +65 -0
- package/docs/rules/tenant-model-registry-parity.md +117 -0
- package/package.json +13 -2
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { IMetaRule } from '@noctcore/harness';
|
|
2
|
+
import { TenantRegistry } from '@noctcore/eslint-plugin-prisma';
|
|
3
|
+
export { TenantRegistry } from '@noctcore/eslint-plugin-prisma';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for {@link createPrismaMethodSurfaceRule}.
|
|
7
|
+
*
|
|
8
|
+
* Every path is relative to the repo root the harness runs in.
|
|
9
|
+
*/
|
|
10
|
+
interface PrismaMethodSurfaceOptions {
|
|
11
|
+
/** Rule id, for running more than one instance. Default `prisma-method-surface`. */
|
|
12
|
+
readonly id?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Globs of the GENERATED Prisma client files that declare the
|
|
15
|
+
* `<Model>Delegate` interfaces. Default: the `prisma-client` generator's
|
|
16
|
+
* per-model files under `generated/prisma/models/`, and the
|
|
17
|
+
* `prisma-client-js` generator's `node_modules/.prisma/client/index.d.ts`.
|
|
18
|
+
*/
|
|
19
|
+
readonly clientGlobs?: readonly string[];
|
|
20
|
+
/**
|
|
21
|
+
* The methods your rules police as WRITES. Default: `PRISMA_WRITE_METHODS`
|
|
22
|
+
* from `@noctcore/eslint-plugin-prisma`, which is what its rules read.
|
|
23
|
+
*/
|
|
24
|
+
readonly writeMethods?: readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* The methods your rules know to be READS. Default: `PRISMA_READ_METHODS`
|
|
27
|
+
* from `@noctcore/eslint-plugin-prisma`.
|
|
28
|
+
*/
|
|
29
|
+
readonly readMethods?: readonly string[];
|
|
30
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
31
|
+
readonly ciCritical?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** One `<Model>Delegate` interface: where it was read, and its query methods. */
|
|
34
|
+
interface DelegateSurface {
|
|
35
|
+
readonly file: string;
|
|
36
|
+
readonly model: string;
|
|
37
|
+
readonly methods: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Every `<Model>Delegate` interface declared in `source`, with its query methods.
|
|
41
|
+
*
|
|
42
|
+
* A delegate member is a generic method, `name<T ...>(...)`, declared at the
|
|
43
|
+
* interface's member indentation, one per line. That holds for both Prisma
|
|
44
|
+
* generators, so a line scan is enough and needs no TypeScript parse. Anything
|
|
45
|
+
* that is not a generic method (`fields`, the `[K: symbol]` brand) is not a
|
|
46
|
+
* query method and is dropped, as is anything `$`-prefixed.
|
|
47
|
+
*/
|
|
48
|
+
declare function parseDelegateSurfaces(source: string, file?: string): DelegateSurface[];
|
|
49
|
+
/**
|
|
50
|
+
* The model-delegate method surface the rules police must be the surface the
|
|
51
|
+
* generated Prisma client actually exposes.
|
|
52
|
+
*
|
|
53
|
+
* Rules that guard Prisma calls by method name read a hand-written list, and a
|
|
54
|
+
* list falls behind the client: Prisma adds a method, nobody edits the list, and
|
|
55
|
+
* the new method is invisible to every fence at once. This reads the GENERATED
|
|
56
|
+
* client's `<Model>Delegate` interfaces and asserts the configured reads and
|
|
57
|
+
* writes partition them exactly, so an upgrade that adds a method turns CI red
|
|
58
|
+
* instead of silently widening an unguarded surface.
|
|
59
|
+
*
|
|
60
|
+
* Fails closed: no generated client is a violation, never a vacuous pass, since
|
|
61
|
+
* a checkout that never ran `prisma generate` is exactly where drift hides.
|
|
62
|
+
*/
|
|
63
|
+
declare function createPrismaMethodSurfaceRule(options?: PrismaMethodSurfaceOptions): IMetaRule;
|
|
64
|
+
|
|
65
|
+
/** Where the runtime scope map is declared, when it is read from source. */
|
|
66
|
+
interface ScopedModelsSource {
|
|
67
|
+
/** File declaring the map, e.g. the tenant-scope client extension. */
|
|
68
|
+
readonly file: string;
|
|
69
|
+
/** Name of the `const` whose object-literal KEYS are the scoped delegate accessors. */
|
|
70
|
+
readonly exportName: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The STATIC side: the lint config whose tenant rules must receive the registry.
|
|
74
|
+
* Read from the RESOLVED config, so it asserts what the rules actually get.
|
|
75
|
+
*/
|
|
76
|
+
interface TenantRegistryEslintOptions {
|
|
77
|
+
/** Directory whose ESLint config is resolved. Default `.`. */
|
|
78
|
+
readonly cwd?: string;
|
|
79
|
+
/** File, relative to `cwd`, the config is resolved for; match your tenant rules' glob. Default `src/__lint_meta_probe__.ts`. */
|
|
80
|
+
readonly probe?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Rules whose `modelsOption` must equal the scoped models. Default the three
|
|
83
|
+
* `noctcore-prisma` tenant rules.
|
|
84
|
+
*/
|
|
85
|
+
readonly modelRules?: readonly string[];
|
|
86
|
+
/** The option those rules take the model list in. Default `tenantModels`. */
|
|
87
|
+
readonly modelsOption?: string;
|
|
88
|
+
/**
|
|
89
|
+
* The rule whose `handScopedOption` must equal `registry.handScopedModels`, or
|
|
90
|
+
* `null` to skip. Default `noctcore-prisma/tenant-scoped-tables-require-where`.
|
|
91
|
+
*/
|
|
92
|
+
readonly handScopedRule?: string | null;
|
|
93
|
+
/** The option that rule takes the hand-scope map in. Default `handScopedModels`. */
|
|
94
|
+
readonly handScopedOption?: string;
|
|
95
|
+
/** Path reported for config-side violations. Default: `cwd`. */
|
|
96
|
+
readonly configFile?: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Options for {@link createTenantModelRegistryParityRule}.
|
|
100
|
+
*
|
|
101
|
+
* Every path is relative to the repo root the harness runs in.
|
|
102
|
+
*/
|
|
103
|
+
interface TenantModelRegistryParityOptions {
|
|
104
|
+
/** Rule id, for running more than one instance. Default `tenant-model-registry-parity`. */
|
|
105
|
+
readonly id?: string;
|
|
106
|
+
/**
|
|
107
|
+
* The Prisma schema: a `.prisma` file, or a folder whose `.prisma` files
|
|
108
|
+
* (recursively) are read together. Default `prisma/schema.prisma`.
|
|
109
|
+
*/
|
|
110
|
+
readonly schemaPath?: string;
|
|
111
|
+
/**
|
|
112
|
+
* The columns that make a model tenant-bearing: a model carrying ALL of them
|
|
113
|
+
* must be scoped or exempt. Default `['tenantId']`.
|
|
114
|
+
*/
|
|
115
|
+
readonly tenantFields?: readonly string[];
|
|
116
|
+
/**
|
|
117
|
+
* The project's registry, injected as data (`TenantRegistry` from
|
|
118
|
+
* `@noctcore/eslint-plugin-prisma`). `scopedModels` is ignored when
|
|
119
|
+
* `scopedModelsSource` is set. Default: empty.
|
|
120
|
+
*/
|
|
121
|
+
readonly registry?: TenantRegistry;
|
|
122
|
+
/**
|
|
123
|
+
* Read the scoped models from the runtime source instead of
|
|
124
|
+
* `registry.scopedModels`: the keys of an object literal
|
|
125
|
+
* (`export const SCOPED = { invoice: [...], ... }`). Prefer this when the
|
|
126
|
+
* runtime map is the source of truth, so the check reads it rather than a copy.
|
|
127
|
+
*/
|
|
128
|
+
readonly scopedModelsSource?: ScopedModelsSource;
|
|
129
|
+
/**
|
|
130
|
+
* Require every unscoped-by-design model to name its hand-scope columns
|
|
131
|
+
* (`registry.handScopedModels`) or record why it cannot
|
|
132
|
+
* (`registry.handScopePending`). Default `true`.
|
|
133
|
+
*/
|
|
134
|
+
readonly requireHandScope?: boolean;
|
|
135
|
+
/** The static side, or `false` to check only the registry against the schema. */
|
|
136
|
+
readonly eslint?: TenantRegistryEslintOptions | false;
|
|
137
|
+
/**
|
|
138
|
+
* Path reported for registry-side violations (the hand-scope maps, and the
|
|
139
|
+
* schema reconciliation when there is no `scopedModelsSource`). Default:
|
|
140
|
+
* `scopedModelsSource.file`, else `schemaPath`.
|
|
141
|
+
*/
|
|
142
|
+
readonly registryFile?: string;
|
|
143
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
144
|
+
readonly ciCritical?: boolean;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Top-level keys of the object literal assigned to `exportName`, or `null` when
|
|
148
|
+
* the declaration cannot be located (reported, never read as an empty map).
|
|
149
|
+
*
|
|
150
|
+
* Reads a comment- and string-blanked copy while tracking bracket depth, so a
|
|
151
|
+
* nested object or an array holding a `:` is never mistaken for an entry.
|
|
152
|
+
* Quoted keys (`'invoice': [...]`) are blanked with the strings, so a map must
|
|
153
|
+
* use identifier keys, as a delegate-accessor map does.
|
|
154
|
+
*/
|
|
155
|
+
declare function parseObjectLiteralKeys(source: string, exportName: string): string[] | null;
|
|
156
|
+
/**
|
|
157
|
+
* The tenant-model registries must mirror each other AND the Prisma schema.
|
|
158
|
+
*
|
|
159
|
+
* A multi-tenant Prisma project keeps "which models are tenant-scoped?" in two
|
|
160
|
+
* hand-maintained places, the runtime scope map and the model list its tenant
|
|
161
|
+
* lint rules are configured with, and agreement between those two says nothing
|
|
162
|
+
* about a table neither has heard of. So the SCHEMA is the third input: every
|
|
163
|
+
* model carrying the tenant columns must be scoped or explicitly exempt with a
|
|
164
|
+
* reason, and a new tenant-bearing model cannot ship without the guardrails
|
|
165
|
+
* learning about it.
|
|
166
|
+
*
|
|
167
|
+
* The static side is read from the RESOLVED ESLint config, not by parsing the
|
|
168
|
+
* config file: moving the array, spreading a different preset, or handing one
|
|
169
|
+
* rule a hand-written list is all caught. Fails closed on an unreadable schema,
|
|
170
|
+
* runtime map or config. Needs the optional `eslint` peer unless `eslint: false`.
|
|
171
|
+
*/
|
|
172
|
+
declare function createTenantModelRegistryParityRule(options?: TenantModelRegistryParityOptions): IMetaRule;
|
|
173
|
+
|
|
174
|
+
export { type DelegateSurface, type PrismaMethodSurfaceOptions, type ScopedModelsSource, type TenantModelRegistryParityOptions, type TenantRegistryEslintOptions, createPrismaMethodSurfaceRule, createTenantModelRegistryParityRule, parseDelegateSurfaces, parseObjectLiteralKeys };
|
package/dist/prisma.d.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { IMetaRule } from '@noctcore/harness';
|
|
2
|
+
import { TenantRegistry } from '@noctcore/eslint-plugin-prisma';
|
|
3
|
+
export { TenantRegistry } from '@noctcore/eslint-plugin-prisma';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for {@link createPrismaMethodSurfaceRule}.
|
|
7
|
+
*
|
|
8
|
+
* Every path is relative to the repo root the harness runs in.
|
|
9
|
+
*/
|
|
10
|
+
interface PrismaMethodSurfaceOptions {
|
|
11
|
+
/** Rule id, for running more than one instance. Default `prisma-method-surface`. */
|
|
12
|
+
readonly id?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Globs of the GENERATED Prisma client files that declare the
|
|
15
|
+
* `<Model>Delegate` interfaces. Default: the `prisma-client` generator's
|
|
16
|
+
* per-model files under `generated/prisma/models/`, and the
|
|
17
|
+
* `prisma-client-js` generator's `node_modules/.prisma/client/index.d.ts`.
|
|
18
|
+
*/
|
|
19
|
+
readonly clientGlobs?: readonly string[];
|
|
20
|
+
/**
|
|
21
|
+
* The methods your rules police as WRITES. Default: `PRISMA_WRITE_METHODS`
|
|
22
|
+
* from `@noctcore/eslint-plugin-prisma`, which is what its rules read.
|
|
23
|
+
*/
|
|
24
|
+
readonly writeMethods?: readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* The methods your rules know to be READS. Default: `PRISMA_READ_METHODS`
|
|
27
|
+
* from `@noctcore/eslint-plugin-prisma`.
|
|
28
|
+
*/
|
|
29
|
+
readonly readMethods?: readonly string[];
|
|
30
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
31
|
+
readonly ciCritical?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** One `<Model>Delegate` interface: where it was read, and its query methods. */
|
|
34
|
+
interface DelegateSurface {
|
|
35
|
+
readonly file: string;
|
|
36
|
+
readonly model: string;
|
|
37
|
+
readonly methods: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Every `<Model>Delegate` interface declared in `source`, with its query methods.
|
|
41
|
+
*
|
|
42
|
+
* A delegate member is a generic method, `name<T ...>(...)`, declared at the
|
|
43
|
+
* interface's member indentation, one per line. That holds for both Prisma
|
|
44
|
+
* generators, so a line scan is enough and needs no TypeScript parse. Anything
|
|
45
|
+
* that is not a generic method (`fields`, the `[K: symbol]` brand) is not a
|
|
46
|
+
* query method and is dropped, as is anything `$`-prefixed.
|
|
47
|
+
*/
|
|
48
|
+
declare function parseDelegateSurfaces(source: string, file?: string): DelegateSurface[];
|
|
49
|
+
/**
|
|
50
|
+
* The model-delegate method surface the rules police must be the surface the
|
|
51
|
+
* generated Prisma client actually exposes.
|
|
52
|
+
*
|
|
53
|
+
* Rules that guard Prisma calls by method name read a hand-written list, and a
|
|
54
|
+
* list falls behind the client: Prisma adds a method, nobody edits the list, and
|
|
55
|
+
* the new method is invisible to every fence at once. This reads the GENERATED
|
|
56
|
+
* client's `<Model>Delegate` interfaces and asserts the configured reads and
|
|
57
|
+
* writes partition them exactly, so an upgrade that adds a method turns CI red
|
|
58
|
+
* instead of silently widening an unguarded surface.
|
|
59
|
+
*
|
|
60
|
+
* Fails closed: no generated client is a violation, never a vacuous pass, since
|
|
61
|
+
* a checkout that never ran `prisma generate` is exactly where drift hides.
|
|
62
|
+
*/
|
|
63
|
+
declare function createPrismaMethodSurfaceRule(options?: PrismaMethodSurfaceOptions): IMetaRule;
|
|
64
|
+
|
|
65
|
+
/** Where the runtime scope map is declared, when it is read from source. */
|
|
66
|
+
interface ScopedModelsSource {
|
|
67
|
+
/** File declaring the map, e.g. the tenant-scope client extension. */
|
|
68
|
+
readonly file: string;
|
|
69
|
+
/** Name of the `const` whose object-literal KEYS are the scoped delegate accessors. */
|
|
70
|
+
readonly exportName: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The STATIC side: the lint config whose tenant rules must receive the registry.
|
|
74
|
+
* Read from the RESOLVED config, so it asserts what the rules actually get.
|
|
75
|
+
*/
|
|
76
|
+
interface TenantRegistryEslintOptions {
|
|
77
|
+
/** Directory whose ESLint config is resolved. Default `.`. */
|
|
78
|
+
readonly cwd?: string;
|
|
79
|
+
/** File, relative to `cwd`, the config is resolved for; match your tenant rules' glob. Default `src/__lint_meta_probe__.ts`. */
|
|
80
|
+
readonly probe?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Rules whose `modelsOption` must equal the scoped models. Default the three
|
|
83
|
+
* `noctcore-prisma` tenant rules.
|
|
84
|
+
*/
|
|
85
|
+
readonly modelRules?: readonly string[];
|
|
86
|
+
/** The option those rules take the model list in. Default `tenantModels`. */
|
|
87
|
+
readonly modelsOption?: string;
|
|
88
|
+
/**
|
|
89
|
+
* The rule whose `handScopedOption` must equal `registry.handScopedModels`, or
|
|
90
|
+
* `null` to skip. Default `noctcore-prisma/tenant-scoped-tables-require-where`.
|
|
91
|
+
*/
|
|
92
|
+
readonly handScopedRule?: string | null;
|
|
93
|
+
/** The option that rule takes the hand-scope map in. Default `handScopedModels`. */
|
|
94
|
+
readonly handScopedOption?: string;
|
|
95
|
+
/** Path reported for config-side violations. Default: `cwd`. */
|
|
96
|
+
readonly configFile?: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Options for {@link createTenantModelRegistryParityRule}.
|
|
100
|
+
*
|
|
101
|
+
* Every path is relative to the repo root the harness runs in.
|
|
102
|
+
*/
|
|
103
|
+
interface TenantModelRegistryParityOptions {
|
|
104
|
+
/** Rule id, for running more than one instance. Default `tenant-model-registry-parity`. */
|
|
105
|
+
readonly id?: string;
|
|
106
|
+
/**
|
|
107
|
+
* The Prisma schema: a `.prisma` file, or a folder whose `.prisma` files
|
|
108
|
+
* (recursively) are read together. Default `prisma/schema.prisma`.
|
|
109
|
+
*/
|
|
110
|
+
readonly schemaPath?: string;
|
|
111
|
+
/**
|
|
112
|
+
* The columns that make a model tenant-bearing: a model carrying ALL of them
|
|
113
|
+
* must be scoped or exempt. Default `['tenantId']`.
|
|
114
|
+
*/
|
|
115
|
+
readonly tenantFields?: readonly string[];
|
|
116
|
+
/**
|
|
117
|
+
* The project's registry, injected as data (`TenantRegistry` from
|
|
118
|
+
* `@noctcore/eslint-plugin-prisma`). `scopedModels` is ignored when
|
|
119
|
+
* `scopedModelsSource` is set. Default: empty.
|
|
120
|
+
*/
|
|
121
|
+
readonly registry?: TenantRegistry;
|
|
122
|
+
/**
|
|
123
|
+
* Read the scoped models from the runtime source instead of
|
|
124
|
+
* `registry.scopedModels`: the keys of an object literal
|
|
125
|
+
* (`export const SCOPED = { invoice: [...], ... }`). Prefer this when the
|
|
126
|
+
* runtime map is the source of truth, so the check reads it rather than a copy.
|
|
127
|
+
*/
|
|
128
|
+
readonly scopedModelsSource?: ScopedModelsSource;
|
|
129
|
+
/**
|
|
130
|
+
* Require every unscoped-by-design model to name its hand-scope columns
|
|
131
|
+
* (`registry.handScopedModels`) or record why it cannot
|
|
132
|
+
* (`registry.handScopePending`). Default `true`.
|
|
133
|
+
*/
|
|
134
|
+
readonly requireHandScope?: boolean;
|
|
135
|
+
/** The static side, or `false` to check only the registry against the schema. */
|
|
136
|
+
readonly eslint?: TenantRegistryEslintOptions | false;
|
|
137
|
+
/**
|
|
138
|
+
* Path reported for registry-side violations (the hand-scope maps, and the
|
|
139
|
+
* schema reconciliation when there is no `scopedModelsSource`). Default:
|
|
140
|
+
* `scopedModelsSource.file`, else `schemaPath`.
|
|
141
|
+
*/
|
|
142
|
+
readonly registryFile?: string;
|
|
143
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
144
|
+
readonly ciCritical?: boolean;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Top-level keys of the object literal assigned to `exportName`, or `null` when
|
|
148
|
+
* the declaration cannot be located (reported, never read as an empty map).
|
|
149
|
+
*
|
|
150
|
+
* Reads a comment- and string-blanked copy while tracking bracket depth, so a
|
|
151
|
+
* nested object or an array holding a `:` is never mistaken for an entry.
|
|
152
|
+
* Quoted keys (`'invoice': [...]`) are blanked with the strings, so a map must
|
|
153
|
+
* use identifier keys, as a delegate-accessor map does.
|
|
154
|
+
*/
|
|
155
|
+
declare function parseObjectLiteralKeys(source: string, exportName: string): string[] | null;
|
|
156
|
+
/**
|
|
157
|
+
* The tenant-model registries must mirror each other AND the Prisma schema.
|
|
158
|
+
*
|
|
159
|
+
* A multi-tenant Prisma project keeps "which models are tenant-scoped?" in two
|
|
160
|
+
* hand-maintained places, the runtime scope map and the model list its tenant
|
|
161
|
+
* lint rules are configured with, and agreement between those two says nothing
|
|
162
|
+
* about a table neither has heard of. So the SCHEMA is the third input: every
|
|
163
|
+
* model carrying the tenant columns must be scoped or explicitly exempt with a
|
|
164
|
+
* reason, and a new tenant-bearing model cannot ship without the guardrails
|
|
165
|
+
* learning about it.
|
|
166
|
+
*
|
|
167
|
+
* The static side is read from the RESOLVED ESLint config, not by parsing the
|
|
168
|
+
* config file: moving the array, spreading a different preset, or handing one
|
|
169
|
+
* rule a hand-written list is all caught. Fails closed on an unreadable schema,
|
|
170
|
+
* runtime map or config. Needs the optional `eslint` peer unless `eslint: false`.
|
|
171
|
+
*/
|
|
172
|
+
declare function createTenantModelRegistryParityRule(options?: TenantModelRegistryParityOptions): IMetaRule;
|
|
173
|
+
|
|
174
|
+
export { type DelegateSurface, type PrismaMethodSurfaceOptions, type ScopedModelsSource, type TenantModelRegistryParityOptions, type TenantRegistryEslintOptions, createPrismaMethodSurfaceRule, createTenantModelRegistryParityRule, parseDelegateSurfaces, parseObjectLiteralKeys };
|