@ogygie/core 0.7.0 → 0.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ogygie/core",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Ogygie framework core: manifest/spec/error schemas, generic tenancy and gate tooling. Ships TypeScript sources — Ogygie products run tsx/vitest by construction (D1).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -64,9 +64,33 @@ export const HttpRouteSchema = z.object({
64
64
  * `shared` is a literal `true`: a table is either shared (and says so) or
65
65
  * tenant-scoped (and stays a plain string) — no `shared: false` third state.
66
66
  */
67
+ /**
68
+ * WHICH hole in the tenancy rail, because the two do not carry the same risk
69
+ * (D8):
70
+ * - `reference` — universal knowledge loaded by an extraction script, READ
71
+ * ONLY for the API. What an ABSENT key means, which is also what
72
+ * `shared: true` historically meant — read it through `sharedKindOf()`
73
+ * rather than defaulting in the schema, so the manifest a project wrote is
74
+ * the manifest an agent reads.
75
+ * - `ownerless` — data the API itself produces, owned by nobody because it is
76
+ * born before any account. Its feature WRITES it, and since no tenant guards
77
+ * it, an unguessable secret must: its rows are read by token, never by an
78
+ * enumerable id. No gate can check that, so the feature's spec must say
79
+ * where the secret lives.
80
+ *
81
+ * A security fact belongs on the map, not in a comment — hence a declared key
82
+ * rather than a convention.
83
+ */
84
+ export const SharedTableKindSchema = z.enum(["reference", "ownerless"]);
85
+ export type SharedTableKind = z.infer<typeof SharedTableKindSchema>;
86
+
67
87
  export const DbTableDeclarationSchema = z.union([
68
88
  z.string(),
69
- z.object({ name: z.string(), shared: z.literal(true) }),
89
+ z.object({
90
+ name: z.string(),
91
+ shared: z.literal(true),
92
+ sharedKind: SharedTableKindSchema.optional(),
93
+ }),
70
94
  ]);
71
95
  export type DbTableDeclaration = z.infer<typeof DbTableDeclarationSchema>;
72
96
 
@@ -7,15 +7,7 @@
7
7
  import { describe, expect, it } from "vitest";
8
8
  import type { FeatureManifest } from "./manifest.schema.js";
9
9
  import { FeatureManifestSchema } from "./manifest.schema.js";
10
- import {
11
- dbTableNamesOf,
12
- findTenantColumnsInSharedTables,
13
- mayUseRawDb,
14
- ownsOnlySharedTables,
15
- sharedTableNamesOf,
16
- splitDbSchemaByTable,
17
- tenantTableNamesOf,
18
- } from "./shared-tables.js";
10
+ import { dbTableNamesOf, findTenantColumnsInSharedTables, mayUseRawDb, ownsOnlySharedTables, sharedKindOf, sharedTableNamesOf, splitDbSchemaByTable, tenantTableNamesOf } from "./shared-tables.js";
19
11
 
20
12
  const SOCLE = new Set(["accounts"]);
21
13
 
@@ -134,3 +126,25 @@ export const labels = pgTable("labels", {
134
126
  ).toEqual([{ table: "labels", tenantColumn: "org_id" }]);
135
127
  });
136
128
  });
129
+
130
+ describe("sharedKindOf — quelle brèche dans le rail tenant (D8)", () => {
131
+ it("une table tenant n'a AUCUNE nature : ce n'est pas une brèche", () => {
132
+ expect(sharedKindOf("orders")).toBeNull();
133
+ });
134
+
135
+ it("une table partagée sans `sharedKind` vaut `reference` — le sens historique", () => {
136
+ expect(sharedKindOf({ name: "currency_rates", shared: true })).toBe("reference");
137
+ });
138
+
139
+ it("une table `ownerless` est rendue telle quelle : sa feature l'écrit", () => {
140
+ expect(sharedKindOf({ name: "public_drafts", shared: true, sharedKind: "ownerless" })).toBe(
141
+ "ownerless",
142
+ );
143
+ });
144
+
145
+ it("une table `reference` explicite reste `reference`", () => {
146
+ expect(sharedKindOf({ name: "postal_codes", shared: true, sharedKind: "reference" })).toBe(
147
+ "reference",
148
+ );
149
+ });
150
+ });
@@ -19,7 +19,7 @@
19
19
  * they return. The module is GENERIC infrastructure (D10 amended): it speaks
20
20
  * "tenant", never a word of the product's domain.
21
21
  */
22
- import type { DbTableDeclaration, FeatureManifest } from "./manifest.schema.js";
22
+ import type { SharedTableKind, DbTableDeclaration, FeatureManifest } from "./manifest.schema.js";
23
23
 
24
24
  /** Name of a table however it was declared (bare string or object form). */
25
25
  export function dbTableNameOf(declaration: DbTableDeclaration): string {
@@ -111,3 +111,14 @@ export function findTenantColumnsInSharedTables(
111
111
  }
112
112
  return findings;
113
113
  }
114
+
115
+ /**
116
+ * The nature of a shared table: what the declaration says, or `reference` when
117
+ * it says nothing — the historical meaning of `shared: true`, kept so older
118
+ * manifests keep meaning what they meant (AGENTS.md §Multi-tenant rule 4).
119
+ * A tenant-scoped table has no nature: it is not a hole in the rail.
120
+ */
121
+ export function sharedKindOf(declaration: DbTableDeclaration): SharedTableKind | null {
122
+ if (typeof declaration === "string") return null;
123
+ return declaration.sharedKind ?? "reference";
124
+ }