@seed-hypermedia/client 0.0.25 → 0.0.27

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.
@@ -0,0 +1,23 @@
1
+ import 'core-js/modules/es.uint8-array.from-base64.js';
2
+ import 'core-js/modules/es.uint8-array.to-base64.js';
3
+ declare global {
4
+ interface Uint8Array {
5
+ toBase64(options?: {
6
+ alphabet?: 'base64' | 'base64url';
7
+ omitPadding?: boolean;
8
+ }): string;
9
+ }
10
+ interface Uint8ArrayConstructor {
11
+ fromBase64(string: string, options?: {
12
+ alphabet?: 'base64' | 'base64url';
13
+ }): Uint8Array;
14
+ }
15
+ }
16
+ /**
17
+ * Encode Uint8Array to base64url string.
18
+ */
19
+ export declare function encode(data: Uint8Array): string;
20
+ /**
21
+ * Decode base64url string to Uint8Array.
22
+ */
23
+ export declare function decode(data: string): Uint8Array;
@@ -1,3 +1,9 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
1
7
  // src/hm-types.ts
2
8
  import * as z from "zod";
3
9
  var BlockRangeSchema = z.object({
@@ -600,7 +606,18 @@ var HMDraftContentSchema = z.object({
600
606
  // EditorBlock validation is handled elsewhere
601
607
  deps: z.array(z.string().min(1)).default([]),
602
608
  navigation: z.array(HMNavigationItemSchema).optional(),
603
- cursorPosition: z.number().optional()
609
+ cursorPosition: z.number().optional(),
610
+ /**
611
+ * Block IDs the local user touched since starting this draft. Used by the
612
+ * auto-rebase classifier to distinguish locally-edited blocks from incoming
613
+ * remote edits. Persisted so reload-after-rebase keeps the touch history.
614
+ */
615
+ mineTouchedIds: z.array(z.string()).optional(),
616
+ /**
617
+ * Snapshot of the published HMBlockNode[] tree at the time the draft was
618
+ * started (or last rebased). Acts as the merge base for three-way diff.
619
+ */
620
+ baseBlocks: z.array(z.any()).optional()
604
621
  });
605
622
  var HMDraftMetaBaseSchema = z.object({
606
623
  id: z.string(),
@@ -1187,6 +1204,7 @@ function isSurrogate(s, i) {
1187
1204
  }
1188
1205
 
1189
1206
  export {
1207
+ __export,
1190
1208
  BlockRangeSchema,
1191
1209
  unpackedHmIdSchema,
1192
1210
  HMBlockChildrenTypeSchema,
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Default Argon2id parameters for password-based key derivation.
3
+ */
4
+ export declare const DEFAULT_PARAMS: {
5
+ memoryCost: number;
6
+ timeCost: number;
7
+ parallelism: number;
8
+ };
9
+ /**
10
+ * Argon2id parameters for key derivation.
11
+ */
12
+ export interface Argon2Params {
13
+ memoryCost: number;
14
+ timeCost: number;
15
+ parallelism: number;
16
+ }
17
+ /**
18
+ * Derives a 256-bit key from a password using Argon2id.
19
+ */
20
+ export declare function deriveKeyFromPassword(password: string, salt: Uint8Array, params?: Argon2Params): Promise<Uint8Array>;
21
+ /**
22
+ * Encrypts bytes with XChaCha20-Poly1305 using the first 32 bytes of the provided key.
23
+ * Returns the nonce prefixed to the ciphertext.
24
+ */
25
+ export declare function encrypt(plaintext: Uint8Array, key: Uint8Array, nonce?: Uint8Array<ArrayBuffer>): Promise<Uint8Array>;
26
+ /**
27
+ * Decrypts nonce-prefixed XChaCha20-Poly1305 ciphertext using the first 32 bytes of the provided key.
28
+ */
29
+ export declare function decrypt(data: Uint8Array, key: Uint8Array): Promise<Uint8Array>;
@@ -12297,6 +12297,17 @@ export declare const HMDraftContentSchema: z.ZodObject<{
12297
12297
  text: string;
12298
12298
  }>, "many">>;
12299
12299
  cursorPosition: z.ZodOptional<z.ZodNumber>;
12300
+ /**
12301
+ * Block IDs the local user touched since starting this draft. Used by the
12302
+ * auto-rebase classifier to distinguish locally-edited blocks from incoming
12303
+ * remote edits. Persisted so reload-after-rebase keeps the touch history.
12304
+ */
12305
+ mineTouchedIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
12306
+ /**
12307
+ * Snapshot of the published HMBlockNode[] tree at the time the draft was
12308
+ * started (or last rebased). Acts as the merge base for three-way diff.
12309
+ */
12310
+ baseBlocks: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
12300
12311
  }, "strip", z.ZodTypeAny, {
12301
12312
  content: any[];
12302
12313
  deps: string[];
@@ -12307,6 +12318,8 @@ export declare const HMDraftContentSchema: z.ZodObject<{
12307
12318
  text: string;
12308
12319
  }[] | undefined;
12309
12320
  cursorPosition?: number | undefined;
12321
+ mineTouchedIds?: string[] | undefined;
12322
+ baseBlocks?: any[] | undefined;
12310
12323
  }, {
12311
12324
  content: any[];
12312
12325
  deps?: string[] | undefined;
@@ -12317,6 +12330,8 @@ export declare const HMDraftContentSchema: z.ZodObject<{
12317
12330
  text: string;
12318
12331
  }[] | undefined;
12319
12332
  cursorPosition?: number | undefined;
12333
+ mineTouchedIds?: string[] | undefined;
12334
+ baseBlocks?: any[] | undefined;
12320
12335
  }>;
12321
12336
  export type HMDraftContent = z.infer<typeof HMDraftContentSchema>;
12322
12337
  export declare const HMDraftMetaSchema: z.ZodEffects<z.ZodObject<{
package/dist/hm-types.mjs CHANGED
@@ -165,7 +165,7 @@ import {
165
165
  toNumber,
166
166
  unpackHmId,
167
167
  unpackedHmIdSchema
168
- } from "./chunk-KDTQQR2L.mjs";
168
+ } from "./chunk-F2YINBVV.mjs";
169
169
  export {
170
170
  BlockRangeSchema,
171
171
  BoldAnnotationSchema,
package/dist/index.d.ts CHANGED
@@ -40,4 +40,6 @@ export { editorBlockToHMBlock, editorBlocksToHMBlockNodes } from './editorblock-
40
40
  export { hmBlocksToEditorContent, hmBlockToEditorBlock, annotationContains } from './hmblock-to-editorblock';
41
41
  export { AnnotationSet, addSpanToAnnotation, pushSpanToAnnotation } from './unicode';
42
42
  export type { MutableAnnotation, SpanAnnotation } from './unicode';
43
+ export * as keyfile from './keyfile';
44
+ export * as keyEncryption from './encryption';
43
45
  export type { EditorBlock, EditorBaseBlock, EditorBlockProps, EditorBlockType, EditorParagraphBlock, EditorHeadingBlock, EditorCodeBlock, EditorImageBlock, EditorVideoBlock, EditorFileBlock, EditorButtonBlock, EditorEmbedBlock, EditorWebEmbedBlock, EditorMathBlock, EditorNostrBlock, EditorQueryBlock, EditorUnknownBlock, EditorText, EditorLink, EditorInlineEmbed, EditorInlineStyles, EditorAnnotationType, HMInlineContent, MediaBlockProps, DraftMediaRef, SearchResult, } from './editor-types';
package/dist/index.mjs CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  HMBlockSchema,
5
5
  HMRequestSchema,
6
6
  HYPERMEDIA_SCHEME,
7
+ __export,
7
8
  codePointLength,
8
9
  entityQueryPathToHmIdPath,
9
10
  getHMQueryString,
@@ -16,7 +17,7 @@ import {
16
17
  serializeBlockRange,
17
18
  toNumber,
18
19
  unpackHmId
19
- } from "./chunk-KDTQQR2L.mjs";
20
+ } from "./chunk-F2YINBVV.mjs";
20
21
 
21
22
  // src/capability.ts
22
23
  import { encode as cborEncode2 } from "@ipld/dag-cbor";
@@ -1416,8 +1417,8 @@ function buildHierarchy(elements) {
1416
1417
  return blocks;
1417
1418
  }
1418
1419
  async function teiToBlocks(teiXml, opts = {}) {
1419
- const { load } = await import("cheerio");
1420
- const $ = load(teiXml, { xml: true });
1420
+ const { load: load2 } = await import("cheerio");
1421
+ const $ = load2(teiXml, { xml: true });
1421
1422
  const title = extractTitle($);
1422
1423
  const displayAuthor = extractAuthors($);
1423
1424
  const summary = extractAbstract($);
@@ -4019,6 +4020,175 @@ function annotationContains(annotation, pos) {
4019
4020
  function isText(entry) {
4020
4021
  return (entry == null ? void 0 : entry.type) && entry.type == "text" && typeof entry.text == "string";
4021
4022
  }
4023
+
4024
+ // src/keyfile.ts
4025
+ var keyfile_exports = {};
4026
+ __export(keyfile_exports, {
4027
+ create: () => create,
4028
+ decrypt: () => decrypt2,
4029
+ load: () => load,
4030
+ parse: () => parse,
4031
+ principalStringFromSeed: () => principalStringFromSeed,
4032
+ stringify: () => stringify
4033
+ });
4034
+ import { ed25519 } from "@noble/curves/ed25519.js";
4035
+ import { base58btc as base58btc5 } from "multiformats/bases/base58";
4036
+
4037
+ // src/base64.ts
4038
+ import "core-js/modules/es.uint8-array.from-base64.js";
4039
+ import "core-js/modules/es.uint8-array.to-base64.js";
4040
+ function encode2(data) {
4041
+ return data.toBase64({ alphabet: "base64url", omitPadding: true });
4042
+ }
4043
+ function decode(data) {
4044
+ return Uint8Array.fromBase64(data, { alphabet: "base64url" });
4045
+ }
4046
+
4047
+ // src/encryption.ts
4048
+ var encryption_exports = {};
4049
+ __export(encryption_exports, {
4050
+ DEFAULT_PARAMS: () => DEFAULT_PARAMS,
4051
+ decrypt: () => decrypt,
4052
+ deriveKeyFromPassword: () => deriveKeyFromPassword,
4053
+ encrypt: () => encrypt
4054
+ });
4055
+ import { xchacha20poly1305 } from "@noble/ciphers/chacha.js";
4056
+ import { argon2id } from "hash-wasm";
4057
+ var DEFAULT_PARAMS = {
4058
+ memoryCost: 65536,
4059
+ timeCost: 3,
4060
+ parallelism: 4
4061
+ };
4062
+ async function deriveKeyFromPassword(password, salt, params = DEFAULT_PARAMS) {
4063
+ const hash = await argon2id({
4064
+ password,
4065
+ salt,
4066
+ parallelism: params.parallelism,
4067
+ iterations: params.timeCost,
4068
+ memorySize: params.memoryCost,
4069
+ hashLength: 32,
4070
+ outputType: "binary"
4071
+ });
4072
+ return new Uint8Array(hash);
4073
+ }
4074
+ async function encrypt(plaintext, key, nonce = crypto.getRandomValues(new Uint8Array(24))) {
4075
+ const xc = xchacha20poly1305(key.subarray(0, 32), nonce);
4076
+ const ciphertext = xc.encrypt(plaintext);
4077
+ const output = new Uint8Array(nonce.length + ciphertext.length);
4078
+ output.set(nonce);
4079
+ output.set(ciphertext, nonce.length);
4080
+ return output;
4081
+ }
4082
+ async function decrypt(data, key) {
4083
+ const nonce = data.subarray(0, 24);
4084
+ const ciphertext = data.subarray(24);
4085
+ const xc = xchacha20poly1305(key.subarray(0, 32), nonce);
4086
+ return xc.decrypt(ciphertext);
4087
+ }
4088
+
4089
+ // src/keyfile.ts
4090
+ var ED25519_VARINT_PREFIX = new Uint8Array([237, 1]);
4091
+ function principalStringFromSeed(seed) {
4092
+ const pub = ed25519.getPublicKey(seed);
4093
+ const principal = new Uint8Array(ED25519_VARINT_PREFIX.length + pub.length);
4094
+ principal.set(ED25519_VARINT_PREFIX, 0);
4095
+ principal.set(pub, ED25519_VARINT_PREFIX.length);
4096
+ return base58btc5.encode(principal);
4097
+ }
4098
+ async function create(input) {
4099
+ const createTime = input.createTime ?? (/* @__PURE__ */ new Date()).toISOString();
4100
+ const includedProfile = input.profile && (input.profile.name || input.profile.description) ? input.profile : void 0;
4101
+ if (!input.password) {
4102
+ return {
4103
+ createTime,
4104
+ publicKey: input.publicKey,
4105
+ keyB64: encode2(input.key),
4106
+ ...includedProfile ? { profile: includedProfile } : {}
4107
+ };
4108
+ }
4109
+ const salt = crypto.getRandomValues(new Uint8Array(16));
4110
+ const derivedKey = await deriveKeyFromPassword(input.password, salt, DEFAULT_PARAMS);
4111
+ const encrypted = await encrypt(input.key, derivedKey);
4112
+ return {
4113
+ createTime,
4114
+ publicKey: input.publicKey,
4115
+ keyB64: encode2(encrypted),
4116
+ encryption: {
4117
+ kdf: "argon2id",
4118
+ argon2: {
4119
+ memoryCost: DEFAULT_PARAMS.memoryCost,
4120
+ timeCost: DEFAULT_PARAMS.timeCost,
4121
+ parallelism: DEFAULT_PARAMS.parallelism,
4122
+ saltB64: encode2(salt)
4123
+ },
4124
+ cipher: "xchacha20poly1305"
4125
+ },
4126
+ ...includedProfile ? { profile: includedProfile } : {}
4127
+ };
4128
+ }
4129
+ function parse(json) {
4130
+ const value = JSON.parse(json);
4131
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
4132
+ throw new Error("Invalid key file payload");
4133
+ }
4134
+ return value;
4135
+ }
4136
+ function stringify(payload) {
4137
+ return `${JSON.stringify(payload, null, 2)}
4138
+ `;
4139
+ }
4140
+ async function decrypt2(payload, password) {
4141
+ if (!payload.keyB64) {
4142
+ throw new Error("keyB64 is required");
4143
+ }
4144
+ if (!payload.encryption) {
4145
+ return decode(payload.keyB64);
4146
+ }
4147
+ if (!password) {
4148
+ throw new Error("password is required for encrypted key files");
4149
+ }
4150
+ if (payload.encryption.kdf !== "argon2id") {
4151
+ throw new Error(`unsupported key derivation function "${payload.encryption.kdf}"`);
4152
+ }
4153
+ if (payload.encryption.cipher !== "xchacha20poly1305") {
4154
+ throw new Error(`unsupported cipher "${payload.encryption.cipher}"`);
4155
+ }
4156
+ if (!payload.encryption.argon2) {
4157
+ throw new Error("argon2 parameters are required");
4158
+ }
4159
+ if (!payload.encryption.argon2.memoryCost || !payload.encryption.argon2.timeCost || !payload.encryption.argon2.parallelism) {
4160
+ throw new Error("argon2 parameters must be greater than zero");
4161
+ }
4162
+ if (!payload.encryption.argon2.saltB64) {
4163
+ throw new Error("saltB64 is required for encrypted key files");
4164
+ }
4165
+ const derivedKey = await deriveKeyFromPassword(
4166
+ password,
4167
+ decode(payload.encryption.argon2.saltB64),
4168
+ {
4169
+ memoryCost: payload.encryption.argon2.memoryCost,
4170
+ timeCost: payload.encryption.argon2.timeCost,
4171
+ parallelism: payload.encryption.argon2.parallelism
4172
+ }
4173
+ );
4174
+ return decrypt(decode(payload.keyB64), derivedKey);
4175
+ }
4176
+ async function load(json, password) {
4177
+ const payload = parse(json);
4178
+ const seed = await decrypt2(payload, password);
4179
+ if (seed.length !== 32) {
4180
+ throw new Error(`invalid private key length: expected 32 bytes, got ${seed.length}`);
4181
+ }
4182
+ const publicKey = principalStringFromSeed(seed);
4183
+ if (payload.publicKey && payload.publicKey !== publicKey) {
4184
+ throw new Error("publicKey does not match private key");
4185
+ }
4186
+ return {
4187
+ payload,
4188
+ seed,
4189
+ publicKey
4190
+ };
4191
+ }
4022
4192
  export {
4023
4193
  AnnotationSet,
4024
4194
  DEFAULT_GROBID_URL,
@@ -4069,6 +4239,8 @@ export {
4069
4239
  hmIdPathToEntityQueryPath,
4070
4240
  isGrobidAvailable,
4071
4241
  isSurrogate,
4242
+ encryption_exports as keyEncryption,
4243
+ keyfile_exports as keyfile,
4072
4244
  markdownBlockNodesToHMBlockNodes,
4073
4245
  matchBlockIds,
4074
4246
  packBaseId,
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Derives the base58btc-multibase principal string for the given 32-byte ed25519 seed.
3
+ */
4
+ export declare function principalStringFromSeed(seed: Uint8Array): string;
5
+ /**
6
+ * Profile metadata included in exported key files for human readability.
7
+ */
8
+ export interface Profile {
9
+ name?: string;
10
+ description?: string;
11
+ }
12
+ /**
13
+ * Argon2id parameters recorded in encrypted key files.
14
+ */
15
+ export interface Argon2 {
16
+ memoryCost: number;
17
+ timeCost: number;
18
+ parallelism: number;
19
+ saltB64: string;
20
+ }
21
+ /**
22
+ * Encryption metadata recorded in encrypted key files.
23
+ */
24
+ export interface Encryption {
25
+ kdf: 'argon2id';
26
+ argon2: Argon2;
27
+ cipher: 'xchacha20poly1305';
28
+ }
29
+ /**
30
+ * Exported payload written to `.hmkey.json` files.
31
+ */
32
+ export interface Payload {
33
+ createTime: string;
34
+ publicKey: string;
35
+ keyB64: string;
36
+ encryption?: Encryption;
37
+ profile?: Profile;
38
+ }
39
+ /**
40
+ * Validated key material read from a serialized `.hmkey.json` file.
41
+ */
42
+ export interface LoadedPayload {
43
+ payload: Payload;
44
+ seed: Uint8Array;
45
+ publicKey: string;
46
+ }
47
+ /**
48
+ * Creates a `.hmkey.json` payload from raw key bytes.
49
+ */
50
+ export declare function create(input: {
51
+ publicKey: string;
52
+ key: Uint8Array;
53
+ password?: string;
54
+ profile?: Profile;
55
+ createTime?: string;
56
+ }): Promise<Payload>;
57
+ /**
58
+ * Parses a serialized `.hmkey.json` payload.
59
+ */
60
+ export declare function parse(json: string): Payload;
61
+ /**
62
+ * Serializes a `.hmkey.json` payload with a trailing newline.
63
+ */
64
+ export declare function stringify(payload: Payload): string;
65
+ /**
66
+ * Decrypts or decodes the raw key bytes from a `.hmkey.json` payload.
67
+ */
68
+ export declare function decrypt(payload: Payload, password?: string): Promise<Uint8Array>;
69
+ /**
70
+ * Parses, decrypts, and validates a serialized `.hmkey.json` payload.
71
+ */
72
+ export declare function load(json: string, password?: string): Promise<LoadedPayload>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-hypermedia/client",
3
- "version": "0.0.25",
3
+ "version": "0.0.27",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/seed-hypermedia/seed",
@@ -46,8 +46,12 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@ipld/dag-cbor": "^9.2.5",
49
+ "@noble/ciphers": "^2.1.1",
50
+ "@noble/curves": "^2.0.1",
49
51
  "blockstore-core": "^5.0.2",
50
52
  "cheerio": "^1.2.0",
53
+ "core-js": "^3.48.0",
54
+ "hash-wasm": "^4.12.0",
51
55
  "ipfs-unixfs-importer": "^15.3.2",
52
56
  "multiformats": "^13.4.1",
53
57
  "superjson": "2.2.1",
package/src/index.ts CHANGED
@@ -86,6 +86,9 @@ export {editorBlockToHMBlock, editorBlocksToHMBlockNodes} from './editorblock-to
86
86
  export {hmBlocksToEditorContent, hmBlockToEditorBlock, annotationContains} from './hmblock-to-editorblock'
87
87
  export {AnnotationSet, addSpanToAnnotation, pushSpanToAnnotation} from './unicode'
88
88
  export type {MutableAnnotation, SpanAnnotation} from './unicode'
89
+
90
+ export * as keyfile from './keyfile'
91
+ export * as keyEncryption from './encryption'
89
92
  export type {
90
93
  EditorBlock,
91
94
  EditorBaseBlock,