@learncard/cli 3.6.4 → 3.6.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @learncard/cli
2
2
 
3
+ ## 3.6.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1741](https://github.com/learningeconomy/LearnCard/pull/1741) [`b26df6a11a5ab4daa5cbfdfe2a28f0b7508d9229`](https://github.com/learningeconomy/LearnCard/commit/b26df6a11a5ab4daa5cbfdfe2a28f0b7508d9229) Thanks [@TaylorBeeston](https://github.com/TaylorBeeston)! - Persist saved profile changes before navigation without forcing profile refetches on every mount. Keep browser query-cache storage on localforage and native storage on SQLite across initialization and reload; native persistence failures never fall back to a stale second store. Revalidate installed apps after reload and apply nested tenant registry defaults correctly.
8
+
9
+ Add the disposable `lc verify` workflow with maintained user journeys, clean-checkout preparation, independently allocated service ports, confined local-service access, interruption-safe evidence, and ownership-checked crash recovery. Repair cold-start Vite loading, pin Astro's compatible Zod dependency, and load the example's CommonJS dependency correctly during server rendering. Build credential share links from the active tenant's configured host instead of the fixed development port.
10
+
11
+ Allow CLI schema/help loading before generated snippets exist; generate snippets atomically and load token snippets only when needed.
12
+
13
+ - Updated dependencies []:
14
+ - @learncard/network-brain-client@2.5.62
15
+
3
16
  ## 3.6.4
4
17
 
5
18
  ### Patch Changes
package/dist/index.js CHANGED
@@ -638,6 +638,19 @@ const CONSENT_CALLBACK_MJS = "import { createServer } from 'node:http';\nimport
638
638
  const READ_USER_DATA_MJS = "import { pathToFileURL } from 'node:url';\nimport { initLearnCard } from '@learncard/init';\nimport { verifyConsentRedirect } from './consent-callback.mjs';\n\nexport const readUserData = async (learnCard, userDid, contractUri) => {\n const profile = await learnCard.invoke.getProfile(userDid);\n if (!profile) return [];\n const hasAccess = () => learnCard.invoke.verifyConsent(contractUri, profile.profileId);\n if (!(await hasAccess())) return [];\n const records = [];\n let cursor;\n do {\n const page = await learnCard.invoke.getConsentFlowDataForDid(userDid, {\n limit: 100,\n ...(cursor ? { cursor } : {}),\n });\n records.push(...page.records.filter(record => record.contractUri === contractUri));\n if (!page.hasMore) return (await hasAccess()) ? records : [];\n if (!page.cursor || page.cursor === cursor) throw new Error('Pagination did not advance');\n cursor = page.cursor;\n } while (true);\n};\n\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\n const { SECURE_SEED, CONTRACT_URI, CONSENT_VP } = process.env;\n if (!SECURE_SEED || !CONTRACT_URI) throw new Error('Set SECURE_SEED and CONTRACT_URI');\n const learnCard = await initLearnCard({ seed: SECURE_SEED, network: true });\n const verified = await verifyConsentRedirect(learnCard, CONSENT_VP, CONTRACT_URI);\n if (verified.status !== 'verified') throw new Error('Consent denied or abandoned');\n const records = await readUserData(learnCard, verified.userDid, CONTRACT_URI);\n console.log(\n records.length ? `Active consent: ${records.length} record(s).` : 'No active consent.'\n );\n}\n";
639
639
  const ISSUE_THROUGH_CONTRACT_MJS = "import { pathToFileURL } from 'node:url';\nimport { initLearnCard } from '@learncard/init';\nimport { verifyConsentRedirect } from './consent-callback.mjs';\nimport { readUserData } from './read-user-data.mjs';\n\nexport const issueThroughContract = async (learnCard, userDid, contractUri) => {\n if ((await readUserData(learnCard, userDid, contractUri)).length === 0) {\n throw new Error('No active consent');\n }\n const base = learnCard.invoke.newCredential({ type: 'boost' });\n const unsignedCredential = {\n ...base,\n issuer: learnCard.id.did(),\n name: 'ConsentFlow Tutorial Complete',\n credentialSubject: {\n ...base.credentialSubject,\n id: userDid,\n achievement: {\n ...base.credentialSubject.achievement,\n name: 'ConsentFlow Tutorial Complete',\n description: 'Completed the ConsentFlow tutorial.',\n },\n },\n };\n const boostUri = await learnCard.invoke.createBoost(unsignedCredential, {\n name: 'ConsentFlow Tutorial Complete',\n category: 'Achievement',\n type: 'achievement',\n });\n const signedCredential = await learnCard.invoke.issueCredential({\n ...unsignedCredential,\n boostId: boostUri,\n });\n return learnCard.invoke.writeCredentialToContract(\n userDid,\n contractUri,\n signedCredential,\n boostUri\n );\n};\n\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\n const { SECURE_SEED, CONTRACT_URI, CONSENT_VP } = process.env;\n if (!SECURE_SEED || !CONTRACT_URI) throw new Error('Set SECURE_SEED and CONTRACT_URI');\n const learnCard = await initLearnCard({ seed: SECURE_SEED, network: true });\n const verified = await verifyConsentRedirect(learnCard, CONSENT_VP, CONTRACT_URI);\n if (verified.status !== 'verified') throw new Error('Consent denied or abandoned');\n const credentialUri = await issueThroughContract(learnCard, verified.userDid, CONTRACT_URI);\n console.log(`Issued: ${credentialUri}`);\n}\n";
640
640
 
641
+ var snippets = /*#__PURE__*/Object.freeze({
642
+ __proto__: null,
643
+ SEND_MJS: SEND_MJS,
644
+ SEND_SH: SEND_SH,
645
+ SEND_FROM_TEMPLATE_MJS: SEND_FROM_TEMPLATE_MJS,
646
+ WEBHOOK_MJS: WEBHOOK_MJS,
647
+ CLAIM_BUTTON_HTML: CLAIM_BUTTON_HTML,
648
+ CREATE_CONTRACT_MJS: CREATE_CONTRACT_MJS,
649
+ CONSENT_CALLBACK_MJS: CONSENT_CALLBACK_MJS,
650
+ READ_USER_DATA_MJS: READ_USER_DATA_MJS,
651
+ ISSUE_THROUGH_CONTRACT_MJS: ISSUE_THROUGH_CONTRACT_MJS
652
+ });
653
+
641
654
  var __defProp$j = Object.defineProperty;
642
655
  var __defProps$g = Object.defineProperties;
643
656
  var __getOwnPropDescs$g = Object.getOwnPropertyDescriptors;
@@ -1058,6 +1071,7 @@ const runToken = async (options) => {
1058
1071
  out.log(token);
1059
1072
  const file = path__default["default"].join(process.cwd(), "send.sh");
1060
1073
  let wroteSendSh = false;
1074
+ const { SEND_SH } = await Promise.resolve().then(function () { return snippets; });
1061
1075
  try {
1062
1076
  await fs__default["default"].writeFile(
1063
1077
  file,
@@ -3234,7 +3248,7 @@ const registerPromoteCommand = (program, run) => {
3234
3248
  };
3235
3249
 
3236
3250
  var name = "@learncard/cli";
3237
- var version = "3.6.4";
3251
+ var version = "3.6.5";
3238
3252
  var description = "Command Line Interface for LearnCard";
3239
3253
  var main = "dist/index.js";
3240
3254
  var bin = "dist/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@learncard/cli",
3
- "version": "3.6.4",
3
+ "version": "3.6.5",
4
4
  "description": "Command Line Interface for LearnCard",
5
5
  "main": "dist/index.js",
6
6
  "bin": "dist/index.js",
@@ -24,7 +24,7 @@
24
24
  "@learncard/learn-cloud-plugin": "2.3.46",
25
25
  "@learncard/ler-rs-plugin": "0.1.30",
26
26
  "@learncard/linked-claims-plugin": "0.2.39",
27
- "@learncard/network-brain-client": "2.5.61",
27
+ "@learncard/network-brain-client": "2.5.62",
28
28
  "@learncard/open-badge-v2-plugin": "1.1.40",
29
29
  "@learncard/render-method-plugin": "10.0.1",
30
30
  "@learncard/types": "5.23.1",
@@ -1,4 +1,5 @@
1
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
2
3
  import { dirname, resolve } from 'node:path';
3
4
  import { fileURLToPath } from 'node:url';
4
5
 
@@ -14,12 +15,33 @@ const snippets = {
14
15
  READ_USER_DATA_MJS: 'consentflow/read-user-data.mjs',
15
16
  ISSUE_THROUGH_CONTRACT_MJS: 'consentflow/issue-through-contract.mjs',
16
17
  };
17
- const lines = await Promise.all(
18
- Object.entries(snippets).map(
19
- async ([name, file]) =>
20
- `export const ${name} = ${JSON.stringify(await readFile(resolve(root, 'docs/snippets', file), 'utf8'))};\n`
21
- )
22
- );
23
- const output = resolve(root, 'packages/learn-card-cli/src/generated/snippets.ts');
24
- await mkdir(dirname(output), { recursive: true });
25
- await writeFile(output, '// Generated from docs/snippets. Do not edit.\n' + lines.join(''));
18
+ /** Deterministic and atomic: independent checkout users never read a partial module. */
19
+ export const embedSnippets = async (signal?: AbortSignal): Promise<void> => {
20
+ signal?.throwIfAborted();
21
+ const lines = await Promise.all(
22
+ Object.entries(snippets).map(
23
+ async ([name, file]) =>
24
+ `export const ${name} = ${JSON.stringify(await readFile(resolve(root, 'docs/snippets', file), { encoding: 'utf8', signal }))};\n`
25
+ )
26
+ );
27
+ const output = resolve(root, 'packages/learn-card-cli/src/generated/snippets.ts');
28
+ const content = '// Generated from docs/snippets. Do not edit.\n' + lines.join('');
29
+ try {
30
+ if ((await readFile(output, 'utf8')) === content) return;
31
+ } catch (error) {
32
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
33
+ }
34
+ await mkdir(dirname(output), { recursive: true });
35
+ const temporary = `${output}.${randomUUID()}.tmp`;
36
+ try {
37
+ await writeFile(temporary, content, { flag: 'wx', signal });
38
+ signal?.throwIfAborted();
39
+ await rename(temporary, output);
40
+ } finally {
41
+ await rm(temporary, { force: true });
42
+ }
43
+ };
44
+
45
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
46
+ await embedSnippets();
47
+ }
package/src/token.ts CHANGED
@@ -11,7 +11,6 @@ import {
11
11
  resolveServices,
12
12
  withEnvTokenLoader,
13
13
  } from './project';
14
- import { SEND_SH } from './generated/snippets';
15
14
  import { out } from './out';
16
15
 
17
16
  // Route-derived resources documented in auth-grants-and-api-tokens.md (not the stale singular aliases).
@@ -114,6 +113,8 @@ export const runToken = async (
114
113
  out.log(token);
115
114
  const file = path.join(process.cwd(), 'send.sh');
116
115
  let wroteSendSh = false;
116
+ // Static import would make org schema validation require this absent-on-clean-checkout asset.
117
+ const { SEND_SH } = await import('./generated/snippets');
117
118
  try {
118
119
  await fs.writeFile(
119
120
  file,