@company-semantics/contracts 44.4.0 → 45.0.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": "@company-semantics/contracts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "45.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
"zod": "^4.4.3"
|
|
131
131
|
},
|
|
132
132
|
"devDependencies": {
|
|
133
|
-
"@types/node": "^
|
|
133
|
+
"@types/node": "^22.20.1",
|
|
134
134
|
"amphtml-validator": "^1.0.38",
|
|
135
135
|
"culori": "^4.0.2",
|
|
136
136
|
"husky": "^9.1.7",
|
|
@@ -150,7 +150,6 @@
|
|
|
150
150
|
"minimatch@<10.2.3": ">=10.2.3",
|
|
151
151
|
"js-yaml@>=4.0.0 <4.3.0": ">=4.3.0 <5.0.0",
|
|
152
152
|
"js-yaml@>=5.0.0 <5.2.2": ">=5.2.2 <6.0.0",
|
|
153
|
-
"rollup@<4.59.0": ">=4.59.0",
|
|
154
153
|
"picomatch@<4.0.4": ">=4.0.4",
|
|
155
154
|
"esbuild@<0.28.1": ">=0.28.1",
|
|
156
155
|
"linkify-it@<5.0.2": ">=5.0.2",
|
|
@@ -98,6 +98,37 @@ describe("resource-keys: system-scoped internalAdmin* types", () => {
|
|
|
98
98
|
});
|
|
99
99
|
});
|
|
100
100
|
|
|
101
|
+
describe("resource-keys: userMd (user-scoped singleton)", () => {
|
|
102
|
+
const USER_ID = "55555555-5555-4555-8555-555555555555";
|
|
103
|
+
const key: ResourceKey = { type: "userMd", userId: USER_ID };
|
|
104
|
+
|
|
105
|
+
it("toQueryKey produces [type, userId] with no doc identity", () => {
|
|
106
|
+
const qk = toQueryKey(key);
|
|
107
|
+
expect(qk).toEqual(["userMd", USER_ID]);
|
|
108
|
+
// A per-user singleton: the user IS the identity, so there is no third
|
|
109
|
+
// segment even though the payload is a work item (ADR-BE-487).
|
|
110
|
+
expect(qk).toHaveLength(2);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("roundtrips through fromQueryKey", () => {
|
|
114
|
+
const parsed = fromQueryKey(toQueryKey(key));
|
|
115
|
+
expect(parsed).toEqual(key);
|
|
116
|
+
expect("orgId" in parsed).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("matchesResourceKey is user-scoped: rejects another user's doc", () => {
|
|
120
|
+
const otherUser = "66666666-6666-4666-8666-666666666666";
|
|
121
|
+
expect(matchesResourceKey(toQueryKey(key), key)).toBe(true);
|
|
122
|
+
expect(matchesResourceKey(["userMd", otherUser], key)).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("does not collide with the other user-scoped keys", () => {
|
|
126
|
+
const viewerKey: ResourceKey = { type: "viewer", userId: USER_ID };
|
|
127
|
+
expect(matchesResourceKey(toQueryKey(viewerKey), key)).toBe(false);
|
|
128
|
+
expect(matchesResourceKey(toQueryKey(key), viewerKey)).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
101
132
|
describe("resource-keys: companyMdAccessRequests (per-doc identity)", () => {
|
|
102
133
|
const DOC_ID = "22222222-2222-4222-8222-222222222222";
|
|
103
134
|
const key: ResourceKey = {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = 'b10dd357bc4f' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = 'b10dd357bc4fd990f9d6646954b3b1aa38f9dce4dd64ecfe8f5f83eb8f51a568' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -4736,8 +4736,8 @@ export interface components {
|
|
|
4736
4736
|
extractionStatus: "pending" | "extracting" | "complete" | "failed";
|
|
4737
4737
|
errorClass?: string | null;
|
|
4738
4738
|
errorReason?: string | null;
|
|
4739
|
-
createdAt
|
|
4740
|
-
updatedAt
|
|
4739
|
+
createdAt?: string;
|
|
4740
|
+
updatedAt?: string;
|
|
4741
4741
|
};
|
|
4742
4742
|
UpdateCompanyMdTitleRequest: {
|
|
4743
4743
|
title: string;
|
package/src/content/schemas.ts
CHANGED
|
@@ -199,8 +199,16 @@ export const CompanyMdDocResponseSchema = z.object({
|
|
|
199
199
|
*/
|
|
200
200
|
errorClass: z.string().nullable().optional(),
|
|
201
201
|
errorReason: z.string().nullable().optional(),
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Withheld from non-readers — see `CompanyMdDocRelations` for why, and keep the
|
|
204
|
+
* two in lockstep. Present on every reader path; absent on the
|
|
205
|
+
* `locked_requestable` / `redacted_admin` stubs, where a moving timestamp is
|
|
206
|
+
* the last remaining side channel on a doc whose body, collaborators and
|
|
207
|
+
* structure are already withheld. Absence means "you were not told", not
|
|
208
|
+
* "never modified".
|
|
209
|
+
*/
|
|
210
|
+
createdAt: z.string().optional(),
|
|
211
|
+
updatedAt: z.string().optional(),
|
|
204
212
|
});
|
|
205
213
|
|
|
206
214
|
// ---------------------------------------------------------------------------
|
package/src/org/company-md.ts
CHANGED
|
@@ -275,8 +275,25 @@ export interface CompanyMdDocRelations {
|
|
|
275
275
|
readonly inheritsFrom: string | null;
|
|
276
276
|
readonly sources: readonly CompanyMdSource[];
|
|
277
277
|
readonly dependencies: readonly CompanyMdDependency[];
|
|
278
|
-
|
|
279
|
-
|
|
278
|
+
/**
|
|
279
|
+
* WITHHELD FROM NON-READERS, and optional for exactly that reason.
|
|
280
|
+
*
|
|
281
|
+
* A non-reader's doc read is a stub: `content` is `''`, and `sources` /
|
|
282
|
+
* `dependencies` / `members` are empty. These two timestamps were the one
|
|
283
|
+
* thing left on it that still reported activity INSIDE the document — that
|
|
284
|
+
* someone wrote to it, and roughly when. On a doc whose body, collaborators
|
|
285
|
+
* and structure are all withheld, a moving `updatedAt` is a side channel: poll
|
|
286
|
+
* it and you learn how busy a document you cannot open is.
|
|
287
|
+
*
|
|
288
|
+
* So they are absent on the `locked_requestable` / `redacted_admin` branches
|
|
289
|
+
* and present on every reader path. Optional rather than nullable because
|
|
290
|
+
* "withheld" is the absence of an answer, not an answer of `null` — and
|
|
291
|
+
* because an older consumer reading the new API sees a missing field, which is
|
|
292
|
+
* the shape it already tolerates for every other stub-withheld value here.
|
|
293
|
+
* Treat absence as "you were not told", never as "never modified".
|
|
294
|
+
*/
|
|
295
|
+
readonly createdAt?: string;
|
|
296
|
+
readonly updatedAt?: string;
|
|
280
297
|
}
|
|
281
298
|
|
|
282
299
|
export type CompanyMdDoc = CompanyMdDocCore &
|
package/src/resource-keys.ts
CHANGED
|
@@ -84,6 +84,12 @@ export type ResourceKey =
|
|
|
84
84
|
| { type: "dismissedBanners"; userId: string }
|
|
85
85
|
| { type: "userOrgs"; userId: string }
|
|
86
86
|
| { type: "sessions"; userId: string }
|
|
87
|
+
// The viewer's personal doc (`/api/me/md`) — a per-user singleton, so it is
|
|
88
|
+
// keyed by userId alone with no doc identity segment. Its PAYLOAD is a work
|
|
89
|
+
// item, lazily materialized on first read (ADR-BE-487), but the resource is
|
|
90
|
+
// "this user's personal doc", not "work item <id>": a future generic
|
|
91
|
+
// `workItem` key would be a different resource with a different identity.
|
|
92
|
+
| { type: "userMd"; userId: string }
|
|
87
93
|
| { type: "viewer"; userId: string };
|
|
88
94
|
|
|
89
95
|
/**
|
|
@@ -131,6 +137,7 @@ const USER_SCOPED_TYPES = [
|
|
|
131
137
|
"dismissedBanners",
|
|
132
138
|
"userOrgs",
|
|
133
139
|
"sessions",
|
|
140
|
+
"userMd",
|
|
134
141
|
"viewer",
|
|
135
142
|
] as const;
|
|
136
143
|
|
|
@@ -186,6 +193,7 @@ export function toQueryKey(key: ResourceKey): readonly string[] {
|
|
|
186
193
|
case "dismissedBanners":
|
|
187
194
|
case "userOrgs":
|
|
188
195
|
case "sessions":
|
|
196
|
+
case "userMd":
|
|
189
197
|
case "viewer":
|
|
190
198
|
return [key.type, key.userId] as const;
|
|
191
199
|
|