@learncard/learn-cloud-service 2.5.34 → 2.5.35

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 (89) hide show
  1. package/.env.example +13 -0
  2. package/CHANGELOG.md +19 -0
  3. package/dist/accesslayer/share-content/delete.d.ts +5 -0
  4. package/dist/accesslayer/share-content/delete.d.ts.map +1 -0
  5. package/dist/accesslayer/share-content/index.d.ts +8 -0
  6. package/dist/accesslayer/share-content/index.d.ts.map +1 -0
  7. package/dist/accesslayer/share-content/initialize.d.ts +5 -0
  8. package/dist/accesslayer/share-content/initialize.d.ts.map +1 -0
  9. package/dist/accesslayer/share-content/internal.d.ts +12 -0
  10. package/dist/accesslayer/share-content/internal.d.ts.map +1 -0
  11. package/dist/accesslayer/share-content/put.d.ts +5 -0
  12. package/dist/accesslayer/share-content/put.d.ts.map +1 -0
  13. package/dist/accesslayer/share-content/read.d.ts +7 -0
  14. package/dist/accesslayer/share-content/read.d.ts.map +1 -0
  15. package/dist/accesslayer/share-content/types.d.ts +85 -0
  16. package/dist/accesslayer/share-content/types.d.ts.map +1 -0
  17. package/dist/config/environment.d.ts +12 -0
  18. package/dist/config/environment.d.ts.map +1 -1
  19. package/dist/helpers/share-content-auth/adapters.d.ts +12 -0
  20. package/dist/helpers/share-content-auth/adapters.d.ts.map +1 -0
  21. package/dist/helpers/share-content-auth/canonical.d.ts +15 -0
  22. package/dist/helpers/share-content-auth/canonical.d.ts.map +1 -0
  23. package/dist/helpers/share-content-auth/claims.d.ts +19 -0
  24. package/dist/helpers/share-content-auth/claims.d.ts.map +1 -0
  25. package/dist/helpers/share-content-auth/config.d.ts +12 -0
  26. package/dist/helpers/share-content-auth/config.d.ts.map +1 -0
  27. package/dist/helpers/share-content-auth/index.d.ts +8 -0
  28. package/dist/helpers/share-content-auth/index.d.ts.map +1 -0
  29. package/dist/helpers/share-content-auth/token.d.ts +28 -0
  30. package/dist/helpers/share-content-auth/token.d.ts.map +1 -0
  31. package/dist/helpers/share-content-auth/types.d.ts +70 -0
  32. package/dist/helpers/share-content-auth/types.d.ts.map +1 -0
  33. package/dist/helpers/share-content-auth/verifier.d.ts +9 -0
  34. package/dist/helpers/share-content-auth/verifier.d.ts.map +1 -0
  35. package/dist/index.js +2 -2
  36. package/dist/models/ShareContent.d.ts +160 -0
  37. package/dist/models/ShareContent.d.ts.map +1 -0
  38. package/dist/models/index.d.ts +1 -0
  39. package/dist/models/index.d.ts.map +1 -1
  40. package/dist/share-content/config.d.ts +28 -0
  41. package/dist/share-content/config.d.ts.map +1 -0
  42. package/dist/share-content/index.d.ts +6 -0
  43. package/dist/share-content/index.d.ts.map +1 -0
  44. package/dist/share-content/plugin.d.ts +5 -0
  45. package/dist/share-content/plugin.d.ts.map +1 -0
  46. package/dist/share-content/production.d.ts +15 -0
  47. package/dist/share-content/production.d.ts.map +1 -0
  48. package/dist/share-content/runtime.d.ts +23 -0
  49. package/dist/share-content/runtime.d.ts.map +1 -0
  50. package/dist/share-content/types.d.ts +23 -0
  51. package/dist/share-content/types.d.ts.map +1 -0
  52. package/lambda.ts +61 -0
  53. package/package.json +14 -14
  54. package/serverless.yml +25 -0
  55. package/src/accesslayer/share-content/delete.ts +124 -0
  56. package/src/accesslayer/share-content/index.ts +70 -0
  57. package/src/accesslayer/share-content/initialize.ts +24 -0
  58. package/src/accesslayer/share-content/internal.ts +89 -0
  59. package/src/accesslayer/share-content/put.ts +93 -0
  60. package/src/accesslayer/share-content/read.ts +124 -0
  61. package/src/accesslayer/share-content/types.ts +155 -0
  62. package/src/config/environment.ts +33 -0
  63. package/src/docker-entry.ts +19 -0
  64. package/src/helpers/share-content-auth/adapters.ts +48 -0
  65. package/src/helpers/share-content-auth/canonical.ts +91 -0
  66. package/src/helpers/share-content-auth/claims.ts +164 -0
  67. package/src/helpers/share-content-auth/config.ts +160 -0
  68. package/src/helpers/share-content-auth/index.ts +59 -0
  69. package/src/helpers/share-content-auth/token.ts +148 -0
  70. package/src/helpers/share-content-auth/types.ts +195 -0
  71. package/src/helpers/share-content-auth/verifier.ts +220 -0
  72. package/src/models/ShareContent.ts +198 -0
  73. package/src/models/index.ts +1 -0
  74. package/src/share-content/config.ts +199 -0
  75. package/src/share-content/index.ts +5 -0
  76. package/src/share-content/plugin.ts +317 -0
  77. package/src/share-content/production.ts +107 -0
  78. package/src/share-content/runtime.ts +90 -0
  79. package/src/share-content/types.ts +59 -0
  80. package/test/share-content-auth.spec.ts +1161 -0
  81. package/test/share-content-config.spec.ts +252 -0
  82. package/test/share-content-didkit.spec.ts +411 -0
  83. package/test/share-content-endpoint.spec.ts +825 -0
  84. package/test/share-content-redis-setup.ts +18 -0
  85. package/test/share-content.spec.ts +681 -0
  86. package/vitest.config.ts +1 -0
  87. package/vitest.share-content-config-unit.config.ts +8 -0
  88. package/vitest.share-content-endpoint.config.ts +21 -0
  89. package/vitest.share-content.config.ts +19 -0
@@ -0,0 +1,90 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+
3
+ import type { ShareContentRepository } from '@accesslayer/share-content';
4
+ import {
5
+ createDidkitPresentationVerifier,
6
+ createShareContentAuthorizationVerifier,
7
+ type LearnCardLike,
8
+ type ReplayStore,
9
+ } from '@helpers/share-content-auth';
10
+
11
+ import { resolveShareContentConfig, toShareContentPluginOptions } from './config';
12
+ import { shareContentFastifyPlugin } from './plugin';
13
+ import { SHARE_CONTENT_PLUGIN_PREFIX, type ShareContentPluginOptions } from './types';
14
+
15
+ /**
16
+ * Startup wrapper for the service-only share-content routes.
17
+ *
18
+ * The builder owns the fail-closed boot order: resolve the explicit config,
19
+ * then (only when it is valid and enabled) open the repository, *await* its
20
+ * index initialization, build the DIDKit verifier and the real Redis replay
21
+ * store, and return injectable plugin options. A disabled configuration returns
22
+ * immediately and performs none of those side effects, so a disabled boot never
23
+ * installs indexes or opens connections.
24
+ */
25
+
26
+ export type ShareContentRuntimeDependencies = {
27
+ /** Existing DIDKit verification seam; never constructed at import time. */
28
+ getLearnCard: () => Promise<LearnCardLike>;
29
+ /** Real `SET NX EX` replay store (tests may inject an explicit double). */
30
+ createReplayStore: () => { store: ReplayStore; close: () => Promise<void> };
31
+ /** Repository factory; must not connect until the enabled branch. */
32
+ getRepository: () => Promise<ShareContentRepository>;
33
+ };
34
+
35
+ export type EnabledShareContentRuntime = {
36
+ enabled: true;
37
+ pluginOptions: ShareContentPluginOptions;
38
+ /** Releases the replay-store connection. Repository pooling is owned by @mongo. */
39
+ close: () => Promise<void>;
40
+ };
41
+
42
+ export type ShareContentRuntime = { enabled: false } | EnabledShareContentRuntime;
43
+
44
+ export const buildShareContentRuntime = async (
45
+ rawConfig: unknown,
46
+ dependencies: ShareContentRuntimeDependencies
47
+ ): Promise<ShareContentRuntime> => {
48
+ const resolved = resolveShareContentConfig(rawConfig);
49
+
50
+ if (resolved.status === 'disabled') return { enabled: false };
51
+
52
+ if (resolved.status === 'invalid') {
53
+ // Explicit startup failure: never fall back to a weaker check.
54
+ throw new Error(
55
+ `Invalid LC-2187 share-content configuration: ${resolved.errors.join('; ')}`
56
+ );
57
+ }
58
+
59
+ const repository = await dependencies.getRepository();
60
+
61
+ // Enabled boot awaits index initialization before any route can serve.
62
+ await repository.initialize();
63
+
64
+ const replayStore = dependencies.createReplayStore();
65
+
66
+ const verifier = createShareContentAuthorizationVerifier({
67
+ config: resolved.trustConfig,
68
+ verifyPresentation: createDidkitPresentationVerifier(dependencies.getLearnCard),
69
+ replayStore: replayStore.store,
70
+ });
71
+
72
+ return {
73
+ enabled: true,
74
+ pluginOptions: toShareContentPluginOptions(resolved, { verifier, repository }),
75
+ close: async () => {
76
+ await replayStore.close();
77
+ },
78
+ };
79
+ };
80
+
81
+ /** Register the routes under the fixed prefix on an already-built Fastify server. */
82
+ export const registerShareContentRoutes = async (
83
+ fastify: FastifyInstance,
84
+ runtime: EnabledShareContentRuntime
85
+ ): Promise<void> => {
86
+ await fastify.register(shareContentFastifyPlugin, {
87
+ prefix: SHARE_CONTENT_PLUGIN_PREFIX,
88
+ ...runtime.pluginOptions,
89
+ });
90
+ };
@@ -0,0 +1,59 @@
1
+ import type { ShareContentRepository } from '@accesslayer/share-content';
2
+ import type { ReplayStore, ShareContentAuthorizationVerifier } from '@helpers/share-content-auth';
3
+
4
+ import type { ShareContentNamespacePolicy } from './config';
5
+
6
+ /**
7
+ * LC-2187 LearnCloud service-only share-content transport.
8
+ *
9
+ * This module is the C4 transport seam: it turns the reviewed C1 authorization
10
+ * verifier and the C2 immutable repository into the fixed service HTTP contract
11
+ * (`POST /internal/share-content/{op}`). It never redesigns the claim format,
12
+ * the repository state machine or the public share feature; those stay owned by
13
+ * their batches.
14
+ */
15
+
16
+ /** All shared-content service routes live under this configured prefix. */
17
+ export const SHARE_CONTENT_PLUGIN_PREFIX = '/internal/share-content';
18
+
19
+ /** The exact operation is selected by the server route, never by the body. */
20
+ export type ShareContentRouteOperation = 'put' | 'get' | 'stat' | 'delete' | 'readRecovery';
21
+
22
+ /** Hard transport bound mandated by the A2 review; the Fastify default. */
23
+ export const SHARE_CONTENT_MAX_REQUEST_BYTES = 1024 * 1024;
24
+
25
+ /**
26
+ * Contract error statuses. Response bodies are always the generic
27
+ * `{ ok: false, error }` shape; internal rejection reasons are never returned.
28
+ */
29
+ export const SHARE_CONTENT_ERROR_CODES = [
30
+ 'UNAUTHORIZED',
31
+ 'INVALID_INPUT',
32
+ 'PAYLOAD_TOO_LARGE',
33
+ 'NOT_FOUND',
34
+ 'CONFLICT',
35
+ 'UNAVAILABLE',
36
+ ] as const;
37
+
38
+ export type ShareContentErrorCode = (typeof SHARE_CONTENT_ERROR_CODES)[number];
39
+
40
+ export type ShareContentErrorBody = { ok: false; error: ShareContentErrorCode };
41
+
42
+ /**
43
+ * Injected transport dependencies. The route layer only orchestrates: it
44
+ * validates the wire body, hashes it, asks the injected verifier to authorize,
45
+ * checks the configured namespace policy, then calls the repository.
46
+ */
47
+ export type ShareContentPluginOptions = {
48
+ verifier: ShareContentAuthorizationVerifier;
49
+ repository: ShareContentRepository;
50
+ namespacePolicy: ShareContentNamespacePolicy;
51
+ /** Route body limit; must never exceed {@link SHARE_CONTENT_MAX_REQUEST_BYTES}. */
52
+ maxRequestBytes?: number;
53
+ };
54
+
55
+ /** Structural view of a Redis-like atomic replay store used by the runtime. */
56
+ export type ShareContentReplayStoreFactory = () => {
57
+ store: ReplayStore;
58
+ close: () => Promise<void>;
59
+ };