@starci/eslint-canon-fe 9.4.7 → 9.5.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/fe-stack.d.ts +3 -3
  3. package/dist/fe-stack.js +3 -9
  4. package/dist/index.js +0 -2
  5. package/dist/lib/scope.d.ts +8 -0
  6. package/dist/lib/scope.js +10 -0
  7. package/dist/lib/why.js +2 -2
  8. package/dist/shape-slot.d.ts +2 -2
  9. package/dist/transport.d.ts +1 -1
  10. package/dist/transport.js +29 -7
  11. package/package.json +1 -1
  12. package/runtime/knowledge/hfs/canon-pins.yaml +54 -24
  13. package/runtime/knowledge/hfs/fe-stack.yaml +4 -11
  14. package/runtime/knowledge/hfs/slots.yaml +185 -174
  15. package/runtime/scripts/hfs/architecture/canon-clones.mjs +1 -1
  16. package/runtime/scripts/hfs/architecture/{supabase-be.mjs → identity.mjs} +45 -15
  17. package/runtime/scripts/hfs/architecture/index.mjs +2 -3
  18. package/runtime/scripts/hfs/architecture/{supabase-ast.mjs → jwt-ast.mjs} +2 -103
  19. package/runtime/scripts/hfs/architecture/register-once.mjs +1 -1
  20. package/runtime/scripts/hfs/architecture/schema-owner.mjs +29 -4
  21. package/runtime/scripts/hfs/architecture/server-action.mjs +3 -4
  22. package/runtime/scripts/hfs/architecture/side-boundary.mjs +1 -1
  23. package/runtime/scripts/hfs/architecture/{supabase-keys.mjs → sql-keys.mjs} +1 -1
  24. package/runtime/scripts/hfs/architecture/sql-owner.mjs +25 -27
  25. package/runtime/scripts/hfs/architecture/transport-owner.mjs +7 -3
  26. package/runtime/scripts/hfs/declaration-shape.mjs +15 -18
  27. package/runtime/scripts/hfs/manifest-shape.mjs +4 -4
  28. package/runtime/scripts/hfs/rule-params-shape.mjs +4 -1
  29. package/runtime/scripts/hfs/rules/database-constants.mjs +8 -10
  30. package/runtime/scripts/hfs/rules/database-migrations.mjs +3 -3
  31. package/runtime/scripts/hfs/slot-app-view.mjs +1 -1
  32. package/runtime/scripts/hfs/slots.mjs +8 -2
  33. package/runtime/scripts/lib/secret-patterns.mjs +2 -2
  34. package/dist/callback-shape.d.ts +0 -7
  35. package/dist/callback-shape.js +0 -396
  36. package/dist/lib/pure-owner-helper.d.ts +0 -10
  37. package/dist/lib/pure-owner-helper.js +0 -156
  38. package/runtime/scripts/hfs/architecture/supabase-tables.mjs +0 -50
  39. package/runtime/scripts/hfs/architecture/supabase.mjs +0 -226
@@ -2,27 +2,23 @@
2
2
  // against a loaded manifest remain in slots.mjs; this module mirrors modules/schemas/hfs-repo.schema.yaml only.
3
3
  import { isPlainObject } from '../../engine/plain-object.mjs';
4
4
  import { APP_KIND, connectionShapeProblems, NAME, PACKAGE_NAME, RUNTIME_KIND, SLOT_ID } from './manifest-shape.mjs';
5
+ import { dbFunctionsOk } from './rule-params-shape.mjs';
5
6
  import { kindShapeProblems, patternShapeProblems } from './declaration-slots.mjs';
6
7
  import { byCodeUnit } from '../lib/list.mjs';
7
8
 
8
- /** Shape problems of the optional hfs.json `supabase` block whose auth posture DB_CONFIG_POLICY checks. */
9
- const isSupabaseStringList = (value) => Array.isArray(value) && value.every((entry) => typeof entry === 'string' && entry.trim());
9
+ /** Shape problems of the optional hfs.json `db` block of a lite app: the environment variable naming its database, its image, forced RLS and its application role. */
10
+ const isStringList = (value) => Array.isArray(value) && value.every((entry) => typeof entry === 'string' && entry.trim());
10
11
 
11
- function supabaseValueProblems(block) {
12
- const bad = [];
13
- if (block.enableSignup !== undefined && typeof block.enableSignup !== 'boolean') bad.push('supabase.enableSignup must be true or false');
14
- if (block.jwtExpiry !== undefined && !(Number.isInteger(block.jwtExpiry) && block.jwtExpiry > 0 && block.jwtExpiry <= 3600)) bad.push('supabase.jwtExpiry must be an integer of seconds, 3600 at most');
15
- if (block.siteUrl !== undefined && (typeof block.siteUrl !== 'string' || !block.siteUrl.trim())) bad.push('supabase.siteUrl must be a URL string');
16
- for (const key of ['redirectUrls', 'forceRls']) if (block[key] !== undefined && !isSupabaseStringList(block[key])) bad.push(`supabase.${key} must be a list of strings`);
17
- return bad;
18
- }
19
-
20
- function supabaseBlockProblems(block) {
12
+ function dbBlockProblems(block) {
21
13
  if (block === undefined) return [];
22
- if (!isPlainObject(block)) return ['supabase must be an object'];
14
+ if (!isPlainObject(block)) return ['db must be an object'];
23
15
  const bad = [];
24
- for (const key of Object.keys(block)) if (!['enableSignup', 'jwtExpiry', 'siteUrl', 'redirectUrls', 'forceRls'].includes(key)) bad.push(`supabase has unknown key ${key}`);
25
- bad.push(...supabaseValueProblems(block));
16
+ for (const key of Object.keys(block)) if (!['urlEnv', 'image', 'forceRls', 'forceRlsExemptions', 'appRole'].includes(key)) bad.push(`db has unknown key ${key}`);
17
+ if (block.urlEnv !== undefined && (typeof block.urlEnv !== 'string' || !/^[A-Z][A-Z0-9_]*$/.test(block.urlEnv))) bad.push('db.urlEnv must name an UPPER_CASE environment variable');
18
+ if (block.image !== undefined && (typeof block.image !== 'string' || !/@sha256:[0-9a-f]{64}$/.test(block.image))) bad.push('db.image must be a digest-pinned image reference');
19
+ if (block.forceRls !== undefined && !isStringList(block.forceRls)) bad.push('db.forceRls must be a list of strings');
20
+ if (block.appRole !== undefined && (typeof block.appRole !== 'string' || !/^[a-z][a-z0-9_]*$/.test(block.appRole))) bad.push('db.appRole must name a role');
21
+ if (block.forceRlsExemptions !== undefined && !Array.isArray(block.forceRlsExemptions)) bad.push('db.forceRlsExemptions must be a list of { table, reason }');
26
22
  return bad;
27
23
  }
28
24
 
@@ -95,7 +91,8 @@ function declarationSideProblems(side, value, names) {
95
91
  const bad = [];
96
92
  const at = `sides.${side}`;
97
93
  if (!isPlainObject(value)) return [`${at} must be an object`];
98
- for (const key of Object.keys(value)) if (!['apps', 'optionalSlots', 'patterns', 'kinds', 'connections', 'reads', 'extraPackages', 'grammar', 'backing', 'queueOutbox'].includes(key)) bad.push(`${at} has unknown key ${key}`);
94
+ for (const key of Object.keys(value)) if (!['apps', 'optionalSlots', 'patterns', 'kinds', 'connections', 'reads', 'extraPackages', 'grammar', 'backing', 'queueOutbox', 'dbFunctions'].includes(key)) bad.push(`${at} has unknown key ${key}`);
95
+ if (value.dbFunctions !== undefined && (side !== 'be' || !dbFunctionsOk(value.dbFunctions))) bad.push(`${at}.dbFunctions belongs to the be side and lists {name, reason} database functions with unique names`);
99
96
  bad.push(...appShapeProblems(value.apps, at, side, names));
100
97
  if (isBadUniqueList(value.optionalSlots, (slot) => SLOT_ID.test(String(slot)))) bad.push(`${at}.optionalSlots must be a unique list of slot ids`);
101
98
  if (value.backing !== undefined && side !== 'be') bad.push('backing belongs to the be side');
@@ -127,13 +124,13 @@ function observeBlockProblems(observe) {
127
124
  return bad;
128
125
  }
129
126
 
130
- const DECLARATION_KEYS = new Set(['hfs', 'kind', 'project', 'edition', 'supabase', 'sides', 'browser', 'lexicon', 'observe']);
127
+ const DECLARATION_KEYS = new Set(['hfs', 'kind', 'project', 'edition', 'db', 'sides', 'browser', 'lexicon', 'observe']);
131
128
  const RUNTIME_DECLARATION_KEYS = new Set(['hfs', 'kind', 'project']);
132
129
 
133
130
  function productDeclarationProblems(declaration, profiles) {
134
131
  const bad = [];
135
132
  for (const key of Object.keys(declaration)) if (!DECLARATION_KEYS.has(key)) bad.push(`unknown key ${key}`);
136
- bad.push(...supabaseBlockProblems(declaration.supabase), ...lexiconBlockProblems(declaration.lexicon), ...observeBlockProblems(declaration.observe));
133
+ bad.push(...dbBlockProblems(declaration.db), ...lexiconBlockProblems(declaration.lexicon), ...observeBlockProblems(declaration.observe));
137
134
  if (declaration.browser !== undefined && declaration.browser !== true) bad.push('browser is `true` when the app has a browser journey (slot app.browser), and is left out otherwise');
138
135
  if (declaration.kind !== APP_KIND) bad.push(`kind must be ${APP_KIND} (a product is one app repository with a be and an fe side) or ${RUNTIME_KIND} (the StarCi runtime repository)`);
139
136
  if (!isPlainObject(declaration.sides) || Object.keys(declaration.sides).sort(byCodeUnit).join() !== profiles.join()) { bad.push('sides must declare exactly be and fe'); return bad; }
@@ -15,12 +15,12 @@ const ENV_PREFIX =/^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/;
15
15
  const CONTEXT_ISOLATIONS = Object.freeze(["database", "schema"]);
16
16
  /** The app kinds that may own a context: the services, never the migrate or cli apps that only run migrations. */
17
17
  const CONTEXT_OWNER_KINDS = Object.freeze(["api", "worker"]);
18
- /** The database engines a connection may run on: a self-described Postgres, or the Postgres of a Supabase project. */
19
- const CONNECTION_PROVIDERS = Object.freeze(["postgres", "supabase"]);
18
+ /** The database engine a connection runs on. */
19
+ const CONNECTION_PROVIDERS = Object.freeze(["postgres"]);
20
20
  /** The editions an app repository may declare in hfs.json (`full` when absent): the filter slots.mjs applies. */
21
21
  export const EDITIONS = Object.freeze(["full", "lite"]);
22
- /** What decides where schema changes live (ruleParams.<side>.schemaAuthority): TypeORM migrations under persistence/, or the Supabase migration tree. */
23
- export const SCHEMA_AUTHORITIES = Object.freeze(["typeorm", "supabase"]);
22
+ /** What decides where schema changes live (ruleParams.<side>.schemaAuthority): TypeORM migrations under persistence/, or the SQL migration tree db/migrations. */
23
+ export const SCHEMA_AUTHORITIES = Object.freeze(["typeorm", "sql"]);
24
24
  /**
25
25
  * The shape problems of the `connections` of one side: a list of {name, envPrefix, owner, isolation}, the owner an api or worker app
26
26
  * of the same side (a connection is a bounded context and one service owns it), isolation `database` or `schema`.
@@ -62,6 +62,9 @@ function beSpecificProblems(be, manifest) {
62
62
  return bad;
63
63
  }
64
64
 
65
+ /** True for an absent list or a list of {name, reason} database functions, names unique. */
66
+ export const dbFunctionsOk = (list) => list === undefined || (Array.isArray(list) && list.every((entry) => isPlainObject(entry) && Object.keys(entry).sort().join() === 'name,reason' && /^[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)?$/.test(entry.name) && typeof entry.reason === 'string' && entry.reason.trim().length > 0) && new Set(list.map((entry) => entry.name)).size === list.length);
67
+
65
68
  /** The shape problems of `m.ruleParams`. */
66
69
  export function ruleParamsProblems(m) {
67
70
  const bad = [];
@@ -71,7 +74,7 @@ export function ruleParamsProblems(m) {
71
74
  // common holds the parameters both sides share; a side may restate one of them (a valid value) as an override, nothing else.
72
75
  // Edition lite: a side's optional `lite` map overrides keys of the same side (never `lite` itself) when the app declares edition lite.
73
76
  if (!sideOk(rp.common, []) || !fileLinesOk(rp.common.fileLines) || !blockOk(rp.common.duplicateBlock)) bad.push('ruleParams.common needs fileLines {soft, hardGrowth} and duplicateBlock {lines >= 2, tokens >= 1}; optional functionLines and featureFiles must be positive integers');
74
- if (!sideOk(rp.be, BE_OWN, ['schemaAuthority', 'lite']) || (rp.be.schemaAuthority !== undefined && !SCHEMA_AUTHORITIES.includes(rp.be.schemaAuthority)) || !liteOk(rp.be.lite, [...BE_OWN, ...SHARED_KEYS, 'schemaAuthority'])) bad.push('ruleParams.be needs infraOwners, eventBus, specDoubles, paramNames, suffixes, bannedSuffixes, contractShape {helper}, unitRoles, logicRoles, thinRoles, patternScenarios, kindPatterns, addKinds and backing (shared parameters live in ruleParams.common, a side may override them); optional: schemaAuthority (' + SCHEMA_AUTHORITIES.join(' | ') + ') and lite (overrides of the same keys)'); else bad.push(...unitRolesProblems(rp.be), ...roleListProblems(rp.be));
77
+ if (!sideOk(rp.be, BE_OWN, ['schemaAuthority', 'lite', 'dbFunctions']) || (rp.be.schemaAuthority !== undefined && !SCHEMA_AUTHORITIES.includes(rp.be.schemaAuthority)) || !dbFunctionsOk(rp.be.dbFunctions) || !liteOk(rp.be.lite, [...BE_OWN, ...SHARED_KEYS, 'schemaAuthority', 'dbFunctions'])) bad.push('ruleParams.be needs infraOwners, eventBus, specDoubles, paramNames, suffixes, bannedSuffixes, contractShape {helper}, unitRoles, logicRoles, thinRoles, patternScenarios, kindPatterns, addKinds and backing (shared parameters live in ruleParams.common, a side may override them); optional: schemaAuthority (' + SCHEMA_AUTHORITIES.join(' | ') + '), dbFunctions ([{name, reason}]) and lite (overrides of the same keys)'); else bad.push(...unitRolesProblems(rp.be), ...roleListProblems(rp.be));
75
78
  bad.push(...beSpecificProblems(rp.be, m));
76
79
  if (rp.fe !== undefined && (!sideOk(rp.fe, [], ['lite']) || !liteOk(rp.fe.lite, SHARED_KEYS))) bad.push('ruleParams.fe may only restate the shared parameters of ruleParams.common, and carry a lite override of them');
77
80
  if (!dbParamsOk(rp.db)) bad.push('ruleParams.db may contain only extensions (a list of unique non-empty extension names) and queueOutbox ({table: <schema>.<table>, columns: [unique column names including queue]})');
@@ -1,26 +1,24 @@
1
- // Single source of database-rule finding codes and owned Supabase paths.
1
+ // Single source of database-rule finding codes and the owned db/ paths of a lite app (slot app.db).
2
2
  export const DB_MIGRATION_SHAPE = 'DB_MIGRATION_SHAPE';
3
3
  export const DB_RLS_REQUIRED = 'DB_RLS_REQUIRED';
4
4
  export const DB_DYNAMIC_DDL = 'DB_DYNAMIC_DDL';
5
5
  export const DB_POLICY_SHAPE = 'DB_POLICY_SHAPE';
6
6
  export const DB_DEFINER_SAFE = 'DB_DEFINER_SAFE';
7
7
  export const DB_STORAGE_POLICY = 'DB_STORAGE_POLICY';
8
- export const DB_CONFIG_POLICY = 'DB_CONFIG_POLICY';
9
- export const DB_TYPES_DRIFT = 'DB_TYPES_DRIFT';
8
+ export const LITE_DB_MIGRATION = 'LITE_DB_MIGRATION';
10
9
  export const DB_BASELINE_SHAPE = 'DB_BASELINE_SHAPE';
11
10
  export const DB_ARCHIVE_MUTATED = 'DB_ARCHIVE_MUTATED';
12
11
  export const DB_PGMQ_DECLARED = 'DB_PGMQ_DECLARED';
13
12
  export const DB_CRON_OWNED = 'DB_CRON_OWNED';
14
13
  export const DB_EXTENSION_ALLOWED = 'DB_EXTENSION_ALLOWED';
15
- export const DB_API_EXPOSED = 'DB_API_EXPOSED';
16
14
  export const DB_MIGRATION_DESTRUCTIVE = 'DB_MIGRATION_DESTRUCTIVE';
17
15
  export const HFS_SEED_PII = 'HFS_SEED_PII';
18
16
  export const DB_VAULT_WRITE = 'DB_VAULT_WRITE';
19
17
 
20
- export const MIGRATIONS_DIR = 'supabase/migrations';
21
- /** The migrations retired at a baseline cutover (slot app.supabase.migrations.archive): never applied, never judged by L02-L08. */
22
- export const MIGRATIONS_ARCHIVE_DIR = `${MIGRATIONS_DIR}/_archive`;
23
- /** True for a migration the Supabase CLI applies: a path under supabase/migrations/ outside the archive. */
18
+ export const DB_DIR = 'db';
19
+ export const MIGRATIONS_DIR = `${DB_DIR}/migrations`;
20
+ /** The migrations retired at a baseline cutover (slot app.db.migrations.archive): never applied, never judged by L02-L08. */
21
+ export const MIGRATIONS_ARCHIVE_DIR = `${MIGRATIONS_DIR}/archive`;
22
+ /** True for a migration `starci app db migrate` applies: a path under db/migrations/ outside the archive. */
24
23
  export const isActiveMigration = (file) => file.startsWith(`${MIGRATIONS_DIR}/`) && !file.startsWith(`${MIGRATIONS_ARCHIVE_DIR}/`);
25
- export const CONFIG_FILE = 'supabase/config.toml';
26
- export const TYPES_FILE = 'supabase/types/database.types.ts';
24
+ export const SEED_FILE = `${DB_DIR}/seed.sql`;
@@ -74,7 +74,7 @@ function localMigrationFindings(local) {
74
74
  const stamps = new Map();
75
75
  for (const entry of local) {
76
76
  if (!entry.match) {
77
- findings.push(found(DB_MIGRATION_SHAPE, entry.file, `${entry.file} is not named <ts14>_<kebab>.sql; a migration is created by \`npm run db:new\` (supabase migration new), never renamed by hand`, { expected: '<ts14>_<kebab>.sql' }));
77
+ findings.push(found(DB_MIGRATION_SHAPE, entry.file, `${entry.file} is not named <ts14>_<kebab>.sql; a migration is created by \`npm run db:new\` (starci app db new), never renamed by hand`, { expected: '<ts14>_<kebab>.sql' }));
78
78
  continue;
79
79
  }
80
80
  const stamp = migrationStamp(entry.match[1]);
@@ -143,8 +143,8 @@ function removedMigrationFindings(local, retired, baseNames, baseSha) {
143
143
  }
144
144
 
145
145
  /**
146
- * L02 findings for names, stamps, ordering and immutability of supabase/migrations/*.sql. `archived` are the files of
147
- * supabase/migrations/_archive/ (app.supabase.migrations.archive): never judged, and a migration moved there at a baseline
146
+ * L02 findings for names, stamps, ordering and immutability of db/migrations/*.sql. `archived` are the files of
147
+ * db/migrations/archive/ (app.db.migrations.archive): never judged, and a migration moved there at a baseline
148
148
  * cutover is retired from the ordering chain, not removed. New files append after HEAD's active migrations; timestamps
149
149
  * on archived migrations, remote branches and the wall clock do not constrain a replacement baseline.
150
150
  */
@@ -49,7 +49,7 @@ function sideCrossing(repo, fromSide, toSide, toPath) {
49
49
  function importAllowedApp({ repo, root, sides }, fromPath, toPath) {
50
50
  const from = splitSide(fromPath);
51
51
  const to = splitSide(toPath);
52
- // A side file may read an app-root path (supabase/types/) only through a declared read, exactly as it reads the other side.
52
+ // A side file may read an app-root path (db/types/) only through a declared read, exactly as it reads the other side.
53
53
  if (from && from.side !== to?.side) return sideCrossing(repo, from.side, to?.side ?? null, toPath);
54
54
  if (from && to) return sides[from.side].importAllowed(from.rest, to.rest);
55
55
  return root.importAllowed(fromPath, toPath);
@@ -120,6 +120,7 @@ function sideDeclaration(manifest, declaration, name, edition, providers) {
120
120
  connections: Object.freeze((s.connections ?? []).map(connectionView)),
121
121
  providers,
122
122
  reads: Object.freeze([...(s.reads ?? [])]),
123
+ dbFunctions: Object.freeze((s.dbFunctions ?? []).map((entry) => Object.freeze({ ...entry }))),
123
124
  ...(s.grammar === undefined ? {} : { grammar: s.grammar }),
124
125
  ...(s.extraPackages === undefined ? {} : { extraPackages: Object.freeze(s.extraPackages.map(entry => Object.freeze({ ...entry }))) }),
125
126
  manifestVersion: manifest.version,
@@ -221,7 +222,8 @@ function createScopeResolver(manifest, repo) {
221
222
  trackingOf,
222
223
  /** True only for a path a slot owns and requires to be committed. */
223
224
  isTracked: (p) => trackingOf(p) === 'tracked',
224
- ruleParams: () => ruleParams(manifest, profile, edition),
225
+ // The app's own database functions (hfs.json sides.be.dbFunctions) join the canon's list (R334).
226
+ ruleParams: () => withDeclaredDbFunctions(ruleParams(manifest, profile, edition), repo.dbFunctions),
225
227
  allowedImports: (tier) => manifest.tiers[profile]?.[tier]?.mayImport ?? null,
226
228
  });
227
229
  }
@@ -235,13 +237,17 @@ function createScopeResolver(manifest, repo) {
235
237
  */
236
238
  export function createSlotResolver(manifest, repo) {
237
239
  if (repo.profile !== APP_SCOPE) return createScopeResolver(manifest, repo);
238
- // The root declares no connection of its own, yet a provider slot of the app profile (app.supabase*) is enabled by a
240
+ // The root declares no connection of its own, yet a provider slot of the app profile is enabled by a
239
241
  // side's connection with that provider: the root scope reads the union of the sides' connections.
240
242
  const root = createScopeResolver(manifest, { ...repo, connections: SIDES.flatMap((side) => repo.sides[side].connections) });
241
243
  const sides = Object.fromEntries(PROFILES.map((side) => [side, createScopeResolver(manifest, repo.sides[side])]));
242
244
  return appResolver(repo, root, sides);
243
245
  }
244
246
 
247
+ /** The rule parameters with the app's declared database functions appended to `dbFunctions` (be only), frozen. */
248
+ const withDeclaredDbFunctions = (params, declared = []) => (params.dbFunctions === undefined && !declared.length ? params
249
+ : Object.freeze({ ...params, dbFunctions: Object.freeze([...(params.dbFunctions ?? []), ...declared]) }));
250
+
245
251
  /** The rule parameters of one profile (be: infraOwners, suffixes, bannedSuffixes and the rest over the shared ruleParams.common fileLines and duplicateBlock; fe: common alone or with its overrides; runtime: ruleParams.runtime of a runtime manifest), as a frozen deep copy. Under `edition` lite the `lite` overrides of ruleParams.<profile> merge over the base (the `lite` key itself is never returned). */
246
252
  export function ruleParams(manifest, profile, edition = 'full') {
247
253
  const profiles = manifestKind(manifest) === RUNTIME_KIND ? [RUNTIME_KIND] : PROFILES;
@@ -100,7 +100,7 @@ const SECRET_NAME_TEST = NAME_WORD(SECRET_WORD, String.raw`(?:[_\-.]|[A-Z]|[^A-Z
100
100
  const BENIGN_TAIL_TEST = NAME_WORD(BENIGN_WORD, '$');
101
101
 
102
102
  /** True when a bare variable `name` holds secret material as an identifier segment (DB_PASSWORD, dbPassword,
103
- * SUPABASE_SERVICE_ROLE_KEY) and does not end in a config suffix (PASSWORD_MIN_LENGTH, SECRET_FILE,
103
+ * STORAGE_SECRET_ACCESS_KEY) and does not end in a config suffix (PASSWORD_MIN_LENGTH, SECRET_FILE,
104
104
  * tokenBudget). The same name judgement the `assigned-secret` patterns make, for callers that read keys. */
105
105
  export const secretNameLike = (name) => SECRET_NAME_TEST.test(name) && !BENIGN_TAIL_TEST.test(name);
106
106
 
@@ -117,7 +117,7 @@ export const SECRET_PATTERNS = [
117
117
  { name: 'jwt', re: /\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/ },
118
118
  { name: 'age-secret-key', re: /\bAGE-SECRET-KEY-1[0-9A-Z]{20,}\b/ },
119
119
  { name: 'password-connection-string', re: /\b(?:postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|redis):\/\/[^:\s/@]+:[^@\s/${}]{8,}@/iu },
120
- // A value written `env(NAME)` (the Supabase config.toml reference form) names a variable, not a secret.
120
+ // A value written `env(NAME)` (a config reference form) names a variable, not a secret.
121
121
  // A keyword-assigned value that names itself a stand-in (a test stub's `accessToken: "fixture-..."`) is no
122
122
  // candidate; only this heuristic takes the exemption, never a provider-shaped token above.
123
123
  // A spec file's keyword-assigned values are test inputs (a spec's sign-in
@@ -1,7 +0,0 @@
1
- import { type Recommended, type Rules } from "./lib/rule.js";
2
- /** A callback stays a PKCE exchange and redirect, with every side effect owned by the auth helper. */
3
- export declare const callbackShape: import("./lib/rule.js").Rule<"shape", []>;
4
- /** The rule R287 contributes to the front-end plugin. */
5
- export declare const rules: Rules;
6
- /** Every callback shape violation is an error. */
7
- export declare const recommended: Recommended;