@tearleads/client-sdk 0.1.40 → 0.1.42

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 (102) hide show
  1. package/client/Tearleads.js +3 -0
  2. package/client/Tearleads.js.map +1 -1
  3. package/data/documents/shared/syncResponses.js +5 -7
  4. package/data/documents/shared/syncResponses.js.map +1 -1
  5. package/data/keyingProjectionVerification/accessManifestCheckpointEnforcement.js +2 -0
  6. package/data/keyingProjectionVerification/accessManifestCheckpointEnforcement.js.map +1 -1
  7. package/data/keyingProjectionVerification/checkpointContext.d.ts +12 -0
  8. package/data/keyingProjectionVerification/checkpointContext.js +16 -0
  9. package/data/keyingProjectionVerification/checkpointContext.js.map +1 -1
  10. package/data/keyingProjectionVerification/containerDestinationVerification.js +3 -1
  11. package/data/keyingProjectionVerification/containerDestinationVerification.js.map +1 -1
  12. package/data/keyingProjectionVerification/containerProjectionVerification.js +12 -6
  13. package/data/keyingProjectionVerification/containerProjectionVerification.js.map +1 -1
  14. package/data/keyingProjectionVerification/documentProjectionVerification.js +10 -4
  15. package/data/keyingProjectionVerification/documentProjectionVerification.js.map +1 -1
  16. package/data/persistence/keyingCheckpointAdvancePersistence.js +27 -8
  17. package/data/persistence/keyingCheckpointAdvancePersistence.js.map +1 -1
  18. package/data/persistence/verifiedManifestBundlePersistence.d.ts +45 -0
  19. package/data/persistence/verifiedManifestBundlePersistence.js +282 -0
  20. package/data/persistence/verifiedManifestBundlePersistence.js.map +1 -0
  21. package/data/sqlite/schema.d.ts +157 -0
  22. package/data/sqlite/schema.js +3 -0
  23. package/data/sqlite/schema.js.map +1 -1
  24. package/data/sqlite/verifiedManifestBundleSchema.d.ts +174 -0
  25. package/data/sqlite/verifiedManifestBundleSchema.js +36 -0
  26. package/data/sqlite/verifiedManifestBundleSchema.js.map +1 -0
  27. package/internal/api-client/ApiClient.d.ts +4 -11
  28. package/internal/api-client/ApiClient.js +8 -6
  29. package/internal/api-client/ApiClient.js.map +1 -1
  30. package/internal/api-client/index.d.ts +1 -1
  31. package/internal/api-client/index.js +0 -1
  32. package/internal/api-client/index.js.map +1 -1
  33. package/internal/api-client/principalHistoryRequest.js +8 -2
  34. package/internal/api-client/principalHistoryRequest.js.map +1 -1
  35. package/internal/api-client/principalHistoryRequestContext.js +15 -5
  36. package/internal/api-client/principalHistoryRequestContext.js.map +1 -1
  37. package/internal/api-client/routes/writerProjections.js +6 -3
  38. package/internal/api-client/routes/writerProjections.js.map +1 -1
  39. package/internal/api-client/verifiedManifestCitations.d.ts +23 -0
  40. package/internal/api-client/verifiedManifestCitations.js +60 -0
  41. package/internal/api-client/verifiedManifestCitations.js.map +1 -0
  42. package/internal/api-client/writerProjectionRequests.js +75 -14
  43. package/internal/api-client/writerProjectionRequests.js.map +1 -1
  44. package/internal/crypto/index.d.ts +0 -1
  45. package/internal/crypto/index.js +0 -1
  46. package/internal/crypto/index.js.map +1 -1
  47. package/internal/validators/documentSyncRefinements.js +5 -0
  48. package/internal/validators/documentSyncRefinements.js.map +1 -1
  49. package/internal/validators/knownManifestsRefinements.js +5 -0
  50. package/internal/validators/knownManifestsRefinements.js.map +1 -0
  51. package/internal/validators/operation/documentSync.d.ts +3 -0
  52. package/internal/validators/operation/writerProjections.d.ts +85 -319
  53. package/internal/validators/operation/writerProjections.js +11 -11
  54. package/internal/validators/operation/writerProjections.js.map +1 -1
  55. package/internal/validators/response/container.d.ts +429 -20
  56. package/internal/validators/response/container.js +40 -5
  57. package/internal/validators/response/container.js.map +1 -1
  58. package/internal/validators/response/documentMutation.d.ts +440 -20
  59. package/internal/validators/response/documentMutation.js +33 -3
  60. package/internal/validators/response/documentMutation.js.map +1 -1
  61. package/internal/validators/response/documentSyncSchema.js +35 -1
  62. package/internal/validators/response/documentSyncSchema.js.map +1 -1
  63. package/internal/validators/response/index.d.ts +3 -2
  64. package/internal/validators/response/index.js +3 -2
  65. package/internal/validators/response/index.js.map +1 -1
  66. package/internal/validators/response/manifestBundleReferences.js +79 -0
  67. package/internal/validators/response/manifestBundleReferences.js.map +1 -0
  68. package/internal/validators/response/writerProjectionWire.d.ts +19 -0
  69. package/internal/validators/response/writerProjectionWire.js +132 -0
  70. package/internal/validators/response/writerProjectionWire.js.map +1 -0
  71. package/internal/validators/schema.js +20 -0
  72. package/internal/validators/schema.js.map +1 -1
  73. package/internal/validators/util/index.d.ts +1 -1
  74. package/internal/validators/util/index.js +1 -1
  75. package/internal/validators/util/index.js.map +1 -1
  76. package/internal/validators/util/knownManifestWire.d.ts +11 -0
  77. package/internal/validators/util/knownManifestWire.js +28 -0
  78. package/internal/validators/util/knownManifestWire.js.map +1 -0
  79. package/internal/validators/writerProjectionRefinements.js +5 -0
  80. package/internal/validators/writerProjectionRefinements.js.map +1 -1
  81. package/package.json +1 -1
  82. package/workflows/documents/syncContentKeys.js +4 -9
  83. package/workflows/documents/syncContentKeys.js.map +1 -1
  84. package/workflows/registration/provisioningBootstrapBuilders.d.ts +0 -5
  85. package/workflows/sync/remoteReset.js +2 -0
  86. package/workflows/sync/remoteReset.js.map +1 -1
  87. package/internal/api-client/projectionHistoryTransport.js +0 -107
  88. package/internal/api-client/projectionHistoryTransport.js.map +0 -1
  89. package/internal/api-client/verifiedProjectionHistory.d.ts +0 -4
  90. package/internal/api-client/verifiedProjectionHistory.js +0 -12
  91. package/internal/api-client/verifiedProjectionHistory.js.map +0 -1
  92. package/internal/crypto/projectionHistory.d.ts +0 -14
  93. package/internal/crypto/projectionHistory.js +0 -90
  94. package/internal/crypto/projectionHistory.js.map +0 -1
  95. package/internal/crypto/projectionHistorySlots.d.ts +0 -13
  96. package/internal/crypto/projectionHistorySlots.js +0 -81
  97. package/internal/crypto/projectionHistorySlots.js.map +0 -1
  98. package/internal/validators/projectionHistoryRefinements.js +0 -5
  99. package/internal/validators/projectionHistoryRefinements.js.map +0 -1
  100. package/internal/validators/util/projectionHistoryWire.d.ts +0 -23
  101. package/internal/validators/util/projectionHistoryWire.js +0 -33
  102. package/internal/validators/util/projectionHistoryWire.js.map +0 -1
@@ -1,107 +0,0 @@
1
- import { captureProjectionHistory, restoreProjectionHistory, } from "../crypto/index.js";
2
- import { isContainerWriterProjectionResponse, isDocumentWriterProjectionResponse, } from "../validators/response/index.js";
3
- import { PROJECTION_HISTORY_HINT_CHARACTERS, ProjectionHistoryPrefixesSchema, } from "../validators/util/index.js";
4
- import { registerProjectionHistoryAdmission } from "./verifiedProjectionHistory.js";
5
- const MAX_HISTORY_CHARACTERS = 16_000_000;
6
- const MAX_PROJECTIONS = 16;
7
- /** Session-scoped evidence cache, independent of the short-lived current-head cache. */
8
- export class ProjectionHistoryTransport {
9
- retained = new Map();
10
- characters = 0;
11
- generation = 0;
12
- clear() {
13
- this.generation += 1;
14
- this.characters = 0;
15
- this.retained.clear();
16
- }
17
- remember(path, entries) {
18
- const characters = JSON.stringify(entries).length;
19
- if (characters > MAX_HISTORY_CHARACTERS)
20
- return;
21
- this.characters -= this.retained.get(path)?.characters ?? 0;
22
- this.retained.delete(path);
23
- this.retained.set(path, { entries, characters });
24
- this.characters += characters;
25
- while (this.characters > MAX_HISTORY_CHARACTERS ||
26
- this.retained.size > MAX_PROJECTIONS) {
27
- const oldest = this.retained.entries().next().value;
28
- if (!oldest)
29
- break;
30
- this.characters -= oldest[1].characters;
31
- this.retained.delete(oldest[0]);
32
- }
33
- }
34
- prepare(path, validator, method, options, operation) {
35
- if (method !== "GET" ||
36
- ![
37
- "containers.writerProjection.get",
38
- "documents.writerProjection.get",
39
- ].includes(operation.id))
40
- return { options, validator };
41
- const generation = this.generation;
42
- const mayRetain = options?.headers === undefined;
43
- const candidates = mayRetain
44
- ? (this.retained.get(path)?.entries ?? [])
45
- : [];
46
- const requested = [];
47
- for (const entry of candidates) {
48
- const prefixes = [...requested, entry].map((item) => item.prefix);
49
- if (!ProjectionHistoryPrefixesSchema.safeParse(prefixes).success)
50
- continue;
51
- // Bound negotiation headers; deep paths merely omit fewer hints.
52
- if (encodeURIComponent(JSON.stringify(prefixes)).length <=
53
- PROJECTION_HISTORY_HINT_CHARACTERS)
54
- requested.push(entry);
55
- }
56
- const hint = JSON.stringify(requested.map((entry) => entry.prefix));
57
- const requestOptions = requested.length
58
- ? {
59
- ...options,
60
- headers: { "x-projection-history": encodeURIComponent(hint) },
61
- }
62
- : options;
63
- return {
64
- options: requestOptions,
65
- validator: (value) => {
66
- if (!validator(value))
67
- return false;
68
- let projection;
69
- if (isContainerWriterProjectionResponse(value) ||
70
- isDocumentWriterProjectionResponse(value))
71
- projection = value;
72
- else
73
- return false;
74
- // A transparent token-refresh retry still holds its requested bytes.
75
- // Restore them here; the admission guard below preserves auth clearing.
76
- if (!restoreProjectionHistory(projection, requested) ||
77
- !validator(value))
78
- return false;
79
- const captured = JSON.stringify(captureProjectionHistory(projection).map((entry) => entry.prefix));
80
- registerProjectionHistoryAdmission(projection, () => {
81
- if (!mayRetain || generation !== this.generation)
82
- return;
83
- // A consumer may mutate a decoded object. Never retain bytes other
84
- // than the exact evidence that passed through the verification call.
85
- const observed = captureProjectionHistory(projection);
86
- if (JSON.stringify(observed.map((entry) => entry.prefix)) !== captured)
87
- return;
88
- this.remember(path, observed);
89
- });
90
- return true;
91
- },
92
- };
93
- }
94
- wrapRequest(request) {
95
- return (path, validator, method, body, options, operation) => {
96
- const prepared = this.prepare(path, validator, method, options, operation);
97
- return request(path, prepared.validator, method, body, prepared.options, operation);
98
- };
99
- }
100
- wrapRequestResult(request) {
101
- return (path, validator, method, body, options, operation) => {
102
- const prepared = this.prepare(path, validator, method, options, operation);
103
- return request(path, prepared.validator, method, body, prepared.options, operation);
104
- };
105
- }
106
- }
107
- //# sourceMappingURL=projectionHistoryTransport.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"projectionHistoryTransport.js","sourceRoot":"","sources":["projectionHistoryTransport.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n captureProjectionHistory,\n type HistoryProjection,\n type RetainedProjectionHistory,\n restoreProjectionHistory,\n} from \"@tearleads/crypto\";\nimport {\n isContainerWriterProjectionResponse,\n isDocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport {\n PROJECTION_HISTORY_HINT_CHARACTERS,\n ProjectionHistoryPrefixesSchema,\n} from \"@tearleads/validators/util\";\nimport type { OperationRequestFn, OperationRequestResultFn } from \"./types\";\nimport { registerProjectionHistoryAdmission } from \"./verifiedProjectionHistory\";\n\nconst MAX_HISTORY_CHARACTERS = 16_000_000;\nconst MAX_PROJECTIONS = 16;\ninterface RetainedHistory {\n readonly entries: RetainedProjectionHistory[];\n readonly characters: number;\n}\n\n/** Session-scoped evidence cache, independent of the short-lived current-head cache. */\nexport class ProjectionHistoryTransport {\n private readonly retained = new Map<string, RetainedHistory>();\n private characters = 0;\n private generation = 0;\n\n clear(): void {\n this.generation += 1;\n this.characters = 0;\n this.retained.clear();\n }\n\n private remember(path: string, entries: RetainedProjectionHistory[]): void {\n const characters = JSON.stringify(entries).length;\n if (characters > MAX_HISTORY_CHARACTERS) return;\n this.characters -= this.retained.get(path)?.characters ?? 0;\n this.retained.delete(path);\n this.retained.set(path, { entries, characters });\n this.characters += characters;\n while (\n this.characters > MAX_HISTORY_CHARACTERS ||\n this.retained.size > MAX_PROJECTIONS\n ) {\n const oldest = this.retained.entries().next().value;\n if (!oldest) break;\n this.characters -= oldest[1].characters;\n this.retained.delete(oldest[0]);\n }\n }\n\n private prepare<T>(\n path: string,\n validator: (value: unknown) => value is T,\n method: Parameters<OperationRequestFn>[2],\n options: Parameters<OperationRequestFn>[4],\n operation: Parameters<OperationRequestFn>[5],\n ): {\n options: Parameters<OperationRequestFn>[4];\n validator: (value: unknown) => value is T;\n } {\n if (\n method !== \"GET\" ||\n ![\n \"containers.writerProjection.get\",\n \"documents.writerProjection.get\",\n ].includes(operation.id)\n )\n return { options, validator };\n const generation = this.generation;\n const mayRetain = options?.headers === undefined;\n const candidates = mayRetain\n ? (this.retained.get(path)?.entries ?? [])\n : [];\n const requested: RetainedProjectionHistory[] = [];\n for (const entry of candidates) {\n const prefixes = [...requested, entry].map((item) => item.prefix);\n if (!ProjectionHistoryPrefixesSchema.safeParse(prefixes).success)\n continue;\n // Bound negotiation headers; deep paths merely omit fewer hints.\n if (\n encodeURIComponent(JSON.stringify(prefixes)).length <=\n PROJECTION_HISTORY_HINT_CHARACTERS\n )\n requested.push(entry);\n }\n const hint = JSON.stringify(requested.map((entry) => entry.prefix));\n const requestOptions = requested.length\n ? {\n ...options,\n headers: { \"x-projection-history\": encodeURIComponent(hint) },\n }\n : options;\n return {\n options: requestOptions,\n validator: (value: unknown): value is T => {\n if (!validator(value)) return false;\n let projection: HistoryProjection;\n if (\n isContainerWriterProjectionResponse(value) ||\n isDocumentWriterProjectionResponse(value)\n )\n projection = value;\n else return false;\n // A transparent token-refresh retry still holds its requested bytes.\n // Restore them here; the admission guard below preserves auth clearing.\n if (\n !restoreProjectionHistory(projection, requested) ||\n !validator(value)\n )\n return false;\n const captured = JSON.stringify(\n captureProjectionHistory(projection).map((entry) => entry.prefix),\n );\n registerProjectionHistoryAdmission(projection, () => {\n if (!mayRetain || generation !== this.generation) return;\n // A consumer may mutate a decoded object. Never retain bytes other\n // than the exact evidence that passed through the verification call.\n const observed = captureProjectionHistory(projection);\n if (\n JSON.stringify(observed.map((entry) => entry.prefix)) !== captured\n )\n return;\n this.remember(path, observed);\n });\n return true;\n },\n };\n }\n\n wrapRequest(request: OperationRequestFn): OperationRequestFn {\n return (path, validator, method, body, options, operation) => {\n const prepared = this.prepare(\n path,\n validator,\n method,\n options,\n operation,\n );\n return request(\n path,\n prepared.validator,\n method,\n body,\n prepared.options,\n operation,\n );\n };\n }\n\n wrapRequestResult(\n request: OperationRequestResultFn,\n ): OperationRequestResultFn {\n return (path, validator, method, body, options, operation) => {\n const prepared = this.prepare(\n path,\n validator,\n method,\n options,\n operation,\n );\n return request(\n path,\n prepared.validator,\n method,\n body,\n prepared.options,\n operation,\n );\n };\n }\n}\n"]}
@@ -1,4 +0,0 @@
1
- /** Transport registers immutable candidates; only successful SDK verification admits one. */
2
- export declare function registerProjectionHistoryAdmission(projection: object, admit: () => void): void;
3
- /** Does not grant access or advance a security checkpoint. */
4
- export declare function retainVerifiedProjectionHistory(projection: object): void;
@@ -1,12 +0,0 @@
1
- const admissions = new WeakMap();
2
- /** Transport registers immutable candidates; only successful SDK verification admits one. */
3
- export function registerProjectionHistoryAdmission(projection, admit) {
4
- admissions.set(projection, admit);
5
- }
6
- /** Does not grant access or advance a security checkpoint. */
7
- export function retainVerifiedProjectionHistory(projection) {
8
- const admit = admissions.get(projection);
9
- admissions.delete(projection);
10
- admit?.();
11
- }
12
- //# sourceMappingURL=verifiedProjectionHistory.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"verifiedProjectionHistory.js","sourceRoot":"","sources":["verifiedProjectionHistory.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAAG,IAAI,OAAO,EAAsB,CAAC;AAErD,6FAA6F;AAC7F,MAAM,UAAU,kCAAkC,CAChD,UAAkB,EAClB,KAAiB;IAEjB,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,+BAA+B,CAAC,UAAkB;IAChE,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACzC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC9B,KAAK,EAAE,EAAE,CAAC;AACZ,CAAC","sourcesContent":["const admissions = new WeakMap<object, () => void>();\n\n/** Transport registers immutable candidates; only successful SDK verification admits one. */\nexport function registerProjectionHistoryAdmission(\n projection: object,\n admit: () => void,\n): void {\n admissions.set(projection, admit);\n}\n\n/** Does not grant access or advance a security checkpoint. */\nexport function retainVerifiedProjectionHistory(projection: object): void {\n const admit = admissions.get(projection);\n admissions.delete(projection);\n admit?.();\n}\n"]}
@@ -1,14 +0,0 @@
1
- import type { ProjectionHistoryPrefix } from "../validators/util/index.js";
2
- import { type HistoryProjection } from "./projectionHistorySlots.js";
3
- export type { HistoryProjection } from "./projectionHistorySlots.js";
4
- export interface RetainedProjectionHistory {
5
- readonly prefix: ProjectionHistoryPrefix;
6
- readonly arrayKey: string;
7
- readonly values: readonly unknown[];
8
- }
9
- /** Byte commitments describe evidence, not an authorization or verification result. */
10
- export declare function captureProjectionHistory(projection: HistoryProjection): RetainedProjectionHistory[];
11
- /** Call only after normal server authorization and stored-evidence verification. */
12
- export declare function omitProjectionHistory<T extends HistoryProjection>(projection: T, hints: readonly ProjectionHistoryPrefix[]): T;
13
- /** Rebuild exact cached evidence before the caller performs all ordinary checks. */
14
- export declare function restoreProjectionHistory(projection: HistoryProjection, requested: readonly RetainedProjectionHistory[]): boolean;
@@ -1,90 +0,0 @@
1
- import { bytesToHex } from "./hex.js";
2
- import { createIncrementalSha256 } from "./incrementalSha256.js";
3
- import { projectionHistoryArrays, } from "./projectionHistorySlots.js";
4
- function historyDigest(key, values) {
5
- const hash = createIncrementalSha256();
6
- hash.update(new TextEncoder().encode(JSON.stringify(["tearleads.projection-history", key, values])));
7
- return bytesToHex(hash.digest());
8
- }
9
- /** Byte commitments describe evidence, not an authorization or verification result. */
10
- export function captureProjectionHistory(projection) {
11
- return projectionHistoryArrays(projection).flatMap((array) => array.chains.flatMap((chain) => {
12
- if (chain.values.length === 0)
13
- return [];
14
- const values = structuredClone(chain.values);
15
- return [
16
- {
17
- arrayKey: array.key,
18
- values,
19
- prefix: {
20
- key: chain.key,
21
- count: values.length,
22
- digest: historyDigest(chain.key, values),
23
- },
24
- },
25
- ];
26
- }));
27
- }
28
- /** Call only after normal server authorization and stored-evidence verification. */
29
- export function omitProjectionHistory(projection, hints) {
30
- if (hints.length === 0)
31
- return projection;
32
- // Server loaders may share arrays and objects between paths. JSON wire
33
- // locations are independent: trimming one must not change another slot.
34
- const result = JSON.parse(JSON.stringify(projection));
35
- const requested = new Map(hints.map((hint) => [hint.key, hint]));
36
- const omitted = [];
37
- for (const array of projectionHistoryArrays(result)) {
38
- const removed = new Map();
39
- for (const chain of array.chains) {
40
- const hint = requested.get(chain.key);
41
- if (!hint || hint.count > chain.values.length)
42
- continue;
43
- const prefix = chain.values.slice(0, hint.count);
44
- if (historyDigest(chain.key, prefix) !== hint.digest)
45
- continue;
46
- for (const item of prefix)
47
- removed.set(item, (removed.get(item) ?? 0) + 1);
48
- omitted.push(hint);
49
- }
50
- const retained = array.values.filter((value) => {
51
- const remaining = removed.get(value) ?? 0;
52
- if (remaining === 0)
53
- return true;
54
- removed.set(value, remaining - 1);
55
- return false;
56
- });
57
- array.values.length = 0;
58
- for (const value of retained)
59
- array.values.push(value);
60
- }
61
- if (omitted.length)
62
- result.historyPrefixes = omitted;
63
- return result;
64
- }
65
- /** Rebuild exact cached evidence before the caller performs all ordinary checks. */
66
- export function restoreProjectionHistory(projection, requested) {
67
- const arrays = new Map(projectionHistoryArrays(projection).map((array) => [array.key, array]));
68
- const available = new Map(requested.map((entry) => [entry.prefix.key, entry]));
69
- for (const prefix of projection.historyPrefixes ?? []) {
70
- const entry = available.get(prefix.key);
71
- if (!entry ||
72
- entry.prefix.count !== prefix.count ||
73
- entry.prefix.digest !== prefix.digest)
74
- return false;
75
- const target = arrays.get(entry.arrayKey);
76
- if (!target || historyDigest(prefix.key, entry.values) !== prefix.digest)
77
- return false;
78
- const suffix = target.values.slice();
79
- target.values.length = 0;
80
- for (const value of structuredClone(entry.values))
81
- target.values.push(value);
82
- for (const value of suffix)
83
- target.values.push(value);
84
- target.restoreOrder?.();
85
- available.delete(prefix.key);
86
- }
87
- delete projection.historyPrefixes;
88
- return true;
89
- }
90
- //# sourceMappingURL=projectionHistory.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"projectionHistory.js","sourceRoot":"","sources":["projectionHistory.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ProjectionHistoryPrefix } from \"@tearleads/validators/util\";\nimport { bytesToHex } from \"./hex\";\nimport { createIncrementalSha256 } from \"./incrementalSha256\";\nimport {\n type HistoryProjection,\n projectionHistoryArrays,\n} from \"./projectionHistorySlots\";\n\nexport type { HistoryProjection } from \"./projectionHistorySlots\";\nexport interface RetainedProjectionHistory {\n readonly prefix: ProjectionHistoryPrefix;\n readonly arrayKey: string;\n readonly values: readonly unknown[];\n}\n\nfunction historyDigest(key: string, values: readonly unknown[]): string {\n const hash = createIncrementalSha256();\n hash.update(\n new TextEncoder().encode(\n JSON.stringify([\"tearleads.projection-history\", key, values]),\n ),\n );\n return bytesToHex(hash.digest());\n}\n\n/** Byte commitments describe evidence, not an authorization or verification result. */\nexport function captureProjectionHistory(\n projection: HistoryProjection,\n): RetainedProjectionHistory[] {\n return projectionHistoryArrays(projection).flatMap((array) =>\n array.chains.flatMap((chain) => {\n if (chain.values.length === 0) return [];\n const values = structuredClone(chain.values);\n return [\n {\n arrayKey: array.key,\n values,\n prefix: {\n key: chain.key,\n count: values.length,\n digest: historyDigest(chain.key, values),\n },\n },\n ];\n }),\n );\n}\n\n/** Call only after normal server authorization and stored-evidence verification. */\nexport function omitProjectionHistory<T extends HistoryProjection>(\n projection: T,\n hints: readonly ProjectionHistoryPrefix[],\n): T {\n if (hints.length === 0) return projection;\n // Server loaders may share arrays and objects between paths. JSON wire\n // locations are independent: trimming one must not change another slot.\n const result: T = JSON.parse(JSON.stringify(projection));\n const requested = new Map(hints.map((hint) => [hint.key, hint]));\n const omitted: ProjectionHistoryPrefix[] = [];\n for (const array of projectionHistoryArrays(result)) {\n const removed = new Map<unknown, number>();\n for (const chain of array.chains) {\n const hint = requested.get(chain.key);\n if (!hint || hint.count > chain.values.length) continue;\n const prefix = chain.values.slice(0, hint.count);\n if (historyDigest(chain.key, prefix) !== hint.digest) continue;\n for (const item of prefix)\n removed.set(item, (removed.get(item) ?? 0) + 1);\n omitted.push(hint);\n }\n const retained = array.values.filter((value) => {\n const remaining = removed.get(value) ?? 0;\n if (remaining === 0) return true;\n removed.set(value, remaining - 1);\n return false;\n });\n array.values.length = 0;\n for (const value of retained) array.values.push(value);\n }\n if (omitted.length) result.historyPrefixes = omitted;\n return result;\n}\n\n/** Rebuild exact cached evidence before the caller performs all ordinary checks. */\nexport function restoreProjectionHistory(\n projection: HistoryProjection,\n requested: readonly RetainedProjectionHistory[],\n): boolean {\n const arrays = new Map(\n projectionHistoryArrays(projection).map((array) => [array.key, array]),\n );\n const available = new Map(\n requested.map((entry) => [entry.prefix.key, entry]),\n );\n for (const prefix of projection.historyPrefixes ?? []) {\n const entry = available.get(prefix.key);\n if (\n !entry ||\n entry.prefix.count !== prefix.count ||\n entry.prefix.digest !== prefix.digest\n )\n return false;\n const target = arrays.get(entry.arrayKey);\n if (!target || historyDigest(prefix.key, entry.values) !== prefix.digest)\n return false;\n const suffix = target.values.slice();\n target.values.length = 0;\n for (const value of structuredClone(entry.values))\n target.values.push(value);\n for (const value of suffix) target.values.push(value);\n target.restoreOrder?.();\n available.delete(prefix.key);\n }\n delete projection.historyPrefixes;\n return true;\n}\n"]}
@@ -1,13 +0,0 @@
1
- import type { ContainerWriterProjectionResponse, DocumentWriterProjectionResponse } from "../validators/response/index.js";
2
- export type HistoryProjection = ContainerWriterProjectionResponse | DocumentWriterProjectionResponse;
3
- export interface ProjectionHistoryArray {
4
- readonly key: string;
5
- readonly values: unknown[];
6
- readonly restoreOrder?: (() => void) | undefined;
7
- readonly chains: readonly {
8
- readonly key: string;
9
- readonly values: unknown[];
10
- }[];
11
- }
12
- /** Only these evidence arrays can be omitted. Current heads and keys always travel. */
13
- export declare function projectionHistoryArrays(projection: HistoryProjection): ProjectionHistoryArray[];
@@ -1,81 +0,0 @@
1
- import { compareCanonicalStrings } from "./canonicalOrdering.js";
2
- function manifestEpoch(bundle) {
3
- const epoch = Reflect.get(bundle.manifest, "epoch");
4
- return typeof epoch === "number" && Number.isFinite(epoch) ? epoch : 0;
5
- }
6
- function isDocumentProjection(projection) {
7
- return "documentId" in projection && "documentManifest" in projection;
8
- }
9
- function unambiguousArrays(arrays) {
10
- const occurrences = new Map();
11
- for (const array of arrays)
12
- occurrences.set(array.key, (occurrences.get(array.key) ?? 0) + 1);
13
- // Ambiguous locations cannot safely negotiate an omission. Deliver them full.
14
- return arrays.filter((array) => occurrences.get(array.key) === 1);
15
- }
16
- function manifestHistoryArray(key, values, newestFirst) {
17
- const groups = new Map();
18
- for (const value of values) {
19
- const chain = JSON.stringify([
20
- key,
21
- Reflect.get(value.manifest, "organizationId"),
22
- Reflect.get(value.manifest, "objectKind"),
23
- Reflect.get(value.manifest, "objectId"),
24
- ]);
25
- const entries = groups.get(chain) ?? [];
26
- entries.push(value);
27
- groups.set(chain, entries);
28
- }
29
- return {
30
- key,
31
- values,
32
- restoreOrder: newestFirst
33
- ? () => {
34
- values.sort((a, b) => manifestEpoch(b) - manifestEpoch(a));
35
- }
36
- : undefined,
37
- chains: [...groups].map(([chain, entries]) => ({
38
- key: chain,
39
- values: entries.sort((a, b) => manifestEpoch(a) - manifestEpoch(b) ||
40
- compareCanonicalStrings(a.manifestHash, b.manifestHash)),
41
- })),
42
- };
43
- }
44
- /** Only these evidence arrays can be omitted. Current heads and keys always travel. */
45
- export function projectionHistoryArrays(projection) {
46
- const arrays = [];
47
- const identity = isDocumentProjection(projection)
48
- ? [
49
- "document",
50
- Reflect.get(projection.documentManifest.manifest, "organizationId"),
51
- projection.documentId,
52
- ]
53
- : ["container", projection.organizationId, projection.containerId];
54
- const add = (name, values) => {
55
- const key = JSON.stringify([...identity, name]);
56
- arrays.push({ key, values, chains: [{ key, values }] });
57
- };
58
- const manifests = (name, values, newestFirst = false) => {
59
- const key = JSON.stringify([...identity, name]);
60
- arrays.push(manifestHistoryArray(key, values, newestFirst));
61
- };
62
- const path = (name, value) => {
63
- for (const [index, kek] of value.containerKeks.entries())
64
- manifests(`${name}:kek:${index}:${kek.containerId}`, kek.containerManifestHistory);
65
- };
66
- const evidence = projection.policyEvidence;
67
- add("organizationPayloads", evidence.organizationPayloads);
68
- if (!isDocumentProjection(projection))
69
- path("container", projection);
70
- else {
71
- for (const [index, value] of projection.authorizingContainerPaths.entries())
72
- path(`path:${index}:${value.containerId}`, value);
73
- // A dependency path is ordered root-to-leaf, not an unordered history.
74
- for (const [index, values,] of projection.documentManifestContainerPaths.entries())
75
- add(`documentManifestContainerPaths:${index}`, values);
76
- manifests("documentContainerManifestHistory", projection.documentContainerManifestHistory);
77
- manifests("documentManifestHistory", projection.documentManifestHistory, true);
78
- }
79
- return unambiguousArrays(arrays);
80
- }
81
- //# sourceMappingURL=projectionHistorySlots.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"projectionHistorySlots.js","sourceRoot":"","sources":["projectionHistorySlots.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccessManifestBundleWireResponse,\n ContainerKeyingPathResponse,\n ContainerWriterProjectionResponse,\n DocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { compareCanonicalStrings } from \"./canonicalOrdering\";\n\nexport type HistoryProjection =\n | ContainerWriterProjectionResponse\n | DocumentWriterProjectionResponse;\nexport interface ProjectionHistoryArray {\n readonly key: string;\n readonly values: unknown[];\n readonly restoreOrder?: (() => void) | undefined;\n readonly chains: readonly {\n readonly key: string;\n readonly values: unknown[];\n }[];\n}\n\nfunction manifestEpoch(bundle: AccessManifestBundleWireResponse): number {\n const epoch: unknown = Reflect.get(bundle.manifest, \"epoch\");\n return typeof epoch === \"number\" && Number.isFinite(epoch) ? epoch : 0;\n}\n\nfunction isDocumentProjection(\n projection: HistoryProjection,\n): projection is DocumentWriterProjectionResponse {\n return \"documentId\" in projection && \"documentManifest\" in projection;\n}\n\nfunction unambiguousArrays(\n arrays: ProjectionHistoryArray[],\n): ProjectionHistoryArray[] {\n const occurrences = new Map<string, number>();\n for (const array of arrays)\n occurrences.set(array.key, (occurrences.get(array.key) ?? 0) + 1);\n // Ambiguous locations cannot safely negotiate an omission. Deliver them full.\n return arrays.filter((array) => occurrences.get(array.key) === 1);\n}\n\nfunction manifestHistoryArray(\n key: string,\n values: AccessManifestBundleWireResponse[],\n newestFirst: boolean,\n): ProjectionHistoryArray {\n const groups = new Map<string, AccessManifestBundleWireResponse[]>();\n for (const value of values) {\n const chain = JSON.stringify([\n key,\n Reflect.get(value.manifest, \"organizationId\"),\n Reflect.get(value.manifest, \"objectKind\"),\n Reflect.get(value.manifest, \"objectId\"),\n ]);\n const entries = groups.get(chain) ?? [];\n entries.push(value);\n groups.set(chain, entries);\n }\n return {\n key,\n values,\n restoreOrder: newestFirst\n ? () => {\n values.sort((a, b) => manifestEpoch(b) - manifestEpoch(a));\n }\n : undefined,\n chains: [...groups].map(([chain, entries]) => ({\n key: chain,\n values: entries.sort(\n (a, b) =>\n manifestEpoch(a) - manifestEpoch(b) ||\n compareCanonicalStrings(a.manifestHash, b.manifestHash),\n ),\n })),\n };\n}\n\n/** Only these evidence arrays can be omitted. Current heads and keys always travel. */\nexport function projectionHistoryArrays(\n projection: HistoryProjection,\n): ProjectionHistoryArray[] {\n const arrays: ProjectionHistoryArray[] = [];\n const identity = isDocumentProjection(projection)\n ? [\n \"document\",\n Reflect.get(projection.documentManifest.manifest, \"organizationId\"),\n projection.documentId,\n ]\n : [\"container\", projection.organizationId, projection.containerId];\n const add = (name: string, values: unknown[]) => {\n const key = JSON.stringify([...identity, name]);\n arrays.push({ key, values, chains: [{ key, values }] });\n };\n const manifests = (\n name: string,\n values: AccessManifestBundleWireResponse[],\n newestFirst = false,\n ) => {\n const key = JSON.stringify([...identity, name]);\n arrays.push(manifestHistoryArray(key, values, newestFirst));\n };\n const path = (name: string, value: ContainerKeyingPathResponse) => {\n for (const [index, kek] of value.containerKeks.entries())\n manifests(\n `${name}:kek:${index}:${kek.containerId}`,\n kek.containerManifestHistory,\n );\n };\n const evidence = projection.policyEvidence;\n add(\"organizationPayloads\", evidence.organizationPayloads);\n if (!isDocumentProjection(projection)) path(\"container\", projection);\n else {\n for (const [index, value] of projection.authorizingContainerPaths.entries())\n path(`path:${index}:${value.containerId}`, value);\n // A dependency path is ordered root-to-leaf, not an unordered history.\n for (const [\n index,\n values,\n ] of projection.documentManifestContainerPaths.entries())\n add(`documentManifestContainerPaths:${index}`, values);\n manifests(\n \"documentContainerManifestHistory\",\n projection.documentContainerManifestHistory,\n );\n manifests(\n \"documentManifestHistory\",\n projection.documentManifestHistory,\n true,\n );\n }\n return unambiguousArrays(arrays);\n}\n"]}
@@ -1,5 +0,0 @@
1
- export const projectionHistoryHeaderRefinement = {
2
- id: "request.projection-history-prefixes",
3
- description: "x-projection-history must decode to at most 32 prefixes with unique keys, positive safe integer counts, and lowercase SHA-256 digests",
4
- };
5
- //# sourceMappingURL=projectionHistoryRefinements.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"projectionHistoryRefinements.js","sourceRoot":"","sources":["projectionHistoryRefinements.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iCAAiC,GAAG;IAC/C,EAAE,EAAE,qCAAqC;IACzC,WAAW,EACT,uIAAuI;CACjI,CAAC","sourcesContent":["export const projectionHistoryHeaderRefinement = {\n id: \"request.projection-history-prefixes\",\n description:\n \"x-projection-history must decode to at most 32 prefixes with unique keys, positive safe integer counts, and lowercase SHA-256 digests\",\n} as const;\n"]}
@@ -1,23 +0,0 @@
1
- import { z } from "zod";
2
- /** Request budgets restrict cache hints, never the history a reader can recover. */
3
- export declare const PROJECTION_HISTORY_HINT_CHARACTERS = 4096;
4
- declare const ProjectionHistoryPrefixSchema: z.ZodObject<{
5
- key: z.ZodCustom<string, string>;
6
- count: z.ZodCustom<number, number>;
7
- digest: z.ZodCustom<string, string>;
8
- }, z.core.$strict>;
9
- export declare const ProjectionHistoryPrefixesSchema: z.ZodCustom<{
10
- key: string;
11
- count: number;
12
- digest: string;
13
- }[], {
14
- key: string;
15
- count: number;
16
- digest: string;
17
- }[]>;
18
- export type ProjectionHistoryPrefix = z.infer<typeof ProjectionHistoryPrefixSchema>;
19
- export declare function parseProjectionHistoryHints(value: string): ProjectionHistoryPrefix[] | null;
20
- export declare const ProjectionHistoryHeadersSchema: z.ZodObject<{
21
- "x-projection-history": z.ZodOptional<z.ZodString>;
22
- }, z.core.$strip>;
23
- export {};
@@ -1,33 +0,0 @@
1
- import { z } from "zod";
2
- import { registerJsonSchemaFragment, registerJsonSchemaRuntimeRefinements, } from "../jsonSchema.js";
3
- import { projectionHistoryHeaderRefinement } from "../projectionHistoryRefinements.js";
4
- import { arraySchema, boundedNonEmptyStringSchema, boundedPositiveIntegerSchema, sha256HexStringSchema, } from "../schema.js";
5
- /** Request budgets restrict cache hints, never the history a reader can recover. */
6
- export const PROJECTION_HISTORY_HINT_CHARACTERS = 4096;
7
- const ProjectionHistoryPrefixSchema = z.strictObject({
8
- key: boundedNonEmptyStringSchema(512),
9
- count: boundedPositiveIntegerSchema(Number.MAX_SAFE_INTEGER),
10
- digest: sha256HexStringSchema,
11
- });
12
- export const ProjectionHistoryPrefixesSchema = arraySchema(ProjectionHistoryPrefixSchema, 32);
13
- export function parseProjectionHistoryHints(value) {
14
- try {
15
- const parsed = ProjectionHistoryPrefixesSchema.safeParse(JSON.parse(decodeURIComponent(value)));
16
- return parsed.success &&
17
- new Set(parsed.data.map((item) => item.key)).size === parsed.data.length
18
- ? parsed.data
19
- : null;
20
- }
21
- catch {
22
- return null;
23
- }
24
- }
25
- const hintHeader = registerJsonSchemaFragment(z.string().max(PROJECTION_HISTORY_HINT_CHARACTERS), {
26
- type: "string",
27
- maxLength: PROJECTION_HISTORY_HINT_CHARACTERS,
28
- description: "URI-encoded JSON array of exact device-verified history prefixes (key, count, digest); at most 32 unique keys",
29
- });
30
- export const ProjectionHistoryHeadersSchema = z.object({
31
- "x-projection-history": registerJsonSchemaRuntimeRefinements(hintHeader.refine((value) => parseProjectionHistoryHints(value) !== null), [projectionHistoryHeaderRefinement]).optional(),
32
- });
33
- //# sourceMappingURL=projectionHistoryWire.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"projectionHistoryWire.js","sourceRoot":"","sources":["projectionHistoryWire.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,0BAA0B,EAC1B,oCAAoC,GACrC,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EACL,WAAW,EACX,2BAA2B,EAC3B,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,WAAW,CAAC;AAEnB,oFAAoF;AACpF,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;AACvD,MAAM,6BAA6B,GAAG,CAAC,CAAC,YAAY,CAAC;IACnD,GAAG,EAAE,2BAA2B,CAAC,GAAG,CAAC;IACrC,KAAK,EAAE,4BAA4B,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAC5D,MAAM,EAAE,qBAAqB;CAC9B,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,WAAW,CACxD,6BAA6B,EAC7B,EAAE,CACH,CAAC;AAKF,MAAM,UAAU,2BAA2B,CACzC,KAAa;IAEb,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,+BAA+B,CAAC,SAAS,CACtD,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CACtC,CAAC;QACF,OAAO,MAAM,CAAC,OAAO;YACnB,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,CAAC,MAAM;YACxE,CAAC,CAAC,MAAM,CAAC,IAAI;YACb,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,GAAG,0BAA0B,CAC3C,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,kCAAkC,CAAC,EAClD;IACE,IAAI,EAAE,QAAQ;IACd,SAAS,EAAE,kCAAkC;IAC7C,WAAW,EACT,+GAA+G;CAClH,CACF,CAAC;AACF,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IACrD,sBAAsB,EAAE,oCAAoC,CAC1D,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,2BAA2B,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,EACzE,CAAC,iCAAiC,CAAC,CACpC,CAAC,QAAQ,EAAE;CACb,CAAC,CAAC","sourcesContent":["import { z } from \"zod\";\nimport {\n registerJsonSchemaFragment,\n registerJsonSchemaRuntimeRefinements,\n} from \"../jsonSchema\";\nimport { projectionHistoryHeaderRefinement } from \"../projectionHistoryRefinements\";\nimport {\n arraySchema,\n boundedNonEmptyStringSchema,\n boundedPositiveIntegerSchema,\n sha256HexStringSchema,\n} from \"../schema\";\n\n/** Request budgets restrict cache hints, never the history a reader can recover. */\nexport const PROJECTION_HISTORY_HINT_CHARACTERS = 4096;\nconst ProjectionHistoryPrefixSchema = z.strictObject({\n key: boundedNonEmptyStringSchema(512),\n count: boundedPositiveIntegerSchema(Number.MAX_SAFE_INTEGER),\n digest: sha256HexStringSchema,\n});\nexport const ProjectionHistoryPrefixesSchema = arraySchema(\n ProjectionHistoryPrefixSchema,\n 32,\n);\nexport type ProjectionHistoryPrefix = z.infer<\n typeof ProjectionHistoryPrefixSchema\n>;\n\nexport function parseProjectionHistoryHints(\n value: string,\n): ProjectionHistoryPrefix[] | null {\n try {\n const parsed = ProjectionHistoryPrefixesSchema.safeParse(\n JSON.parse(decodeURIComponent(value)),\n );\n return parsed.success &&\n new Set(parsed.data.map((item) => item.key)).size === parsed.data.length\n ? parsed.data\n : null;\n } catch {\n return null;\n }\n}\n\nconst hintHeader = registerJsonSchemaFragment(\n z.string().max(PROJECTION_HISTORY_HINT_CHARACTERS),\n {\n type: \"string\",\n maxLength: PROJECTION_HISTORY_HINT_CHARACTERS,\n description:\n \"URI-encoded JSON array of exact device-verified history prefixes (key, count, digest); at most 32 unique keys\",\n },\n);\nexport const ProjectionHistoryHeadersSchema = z.object({\n \"x-projection-history\": registerJsonSchemaRuntimeRefinements(\n hintHeader.refine((value) => parseProjectionHistoryHints(value) !== null),\n [projectionHistoryHeaderRefinement],\n ).optional(),\n});\n"]}