@hraness/wordcell 0.22.4 → 0.23.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/README.md +63 -22
- package/dist/authoring.js +2 -2
- package/dist/{authority-06eqhqam.js → authority-35cbvp1x.js} +2 -2
- package/dist/benchmark.js +3 -3
- package/dist/capture.js +6 -6
- package/dist/cli.js +26 -24
- package/dist/clip/acquire.js +3 -3
- package/dist/clip/args.js +1 -1
- package/dist/clip/cli.js +6 -6
- package/dist/clip/doctor.js +4 -4
- package/dist/evaluation-builder.js +10 -10
- package/dist/evaluation-kb.js +10 -10
- package/dist/graph-authority.js +4 -4
- package/dist/graph-percolation.js +6 -6
- package/dist/graph.js +5 -1
- package/dist/{index-bcknqxrq.js → index-0eacgvpv.js} +9 -1
- package/dist/{index-xv80vzfp.js → index-1er88ckw.js} +4 -4
- package/dist/{index-233z9wmn.js → index-1s8mc4hz.js} +13 -5
- package/dist/{index-de2w8crk.js → index-2fmnttca.js} +82 -15
- package/dist/{index-vnybgywh.js → index-ahyhryb8.js} +1 -1
- package/dist/{index-ecqpgr5y.js → index-b0khj0h5.js} +164 -9
- package/dist/{index-jk1sgx36.js → index-bdwcjvr4.js} +1 -1
- package/dist/{index-dfag79p7.js → index-byz4kzww.js} +3 -0
- package/dist/{index-hgve9rh2.js → index-cd75vky9.js} +96 -1
- package/dist/{index-rr9kzq4n.js → index-fp732bgg.js} +2 -2
- package/dist/{index-qbssx940.js → index-jvb7w0gg.js} +227 -9
- package/dist/{index-f984hw45.js → index-k86wepd8.js} +1 -1
- package/dist/{index-1k595z6v.js → index-mnq2wy34.js} +2 -2
- package/dist/{index-n386evxj.js → index-nn67kzwa.js} +1 -1
- package/dist/{index-fc4dr114.js → index-nwrehke7.js} +1 -1
- package/dist/{index-76dbn6pe.js → index-p70t8j13.js} +1 -1
- package/dist/{index-tf4wzjew.js → index-pgtm2nhf.js} +1 -1
- package/dist/{index-054mb7d3.js → index-qfqjfxaa.js} +10 -9
- package/dist/{index-pnt9wr59.js → index-r0m5taz2.js} +2 -2
- package/dist/{index-bqtqeak3.js → index-r4qs6vq4.js} +4 -4
- package/dist/{index-djrcc1yf.js → index-tcaq1c7f.js} +4 -4
- package/dist/{index-q7tbg07z.js → index-tvd2sqrx.js} +7 -7
- package/dist/{index-9h7zh0wt.js → index-vtwbapjr.js} +3814 -162
- package/dist/{index-g5vsqmdy.js → index-xwxy71ew.js} +190 -4
- package/dist/index.js +22 -14
- package/dist/pdf.js +3 -3
- package/dist/percolate.js +2 -2
- package/dist/portfolio.js +10 -10
- package/dist/publish.js +3 -3
- package/dist/sdk.js +9 -9
- package/dist/search.js +2 -2
- package/dist/semantic.js +4 -4
- package/dist/{support-m3t917ev.js → support-c8hy7qfa.js} +25 -23
- package/dist/url-intelligence.js +1 -1
- package/dist/workflows/decision-context.js +10 -10
- package/dist/workflows/index.js +10 -10
- package/package.json +12 -4
- package/skills/wordcell/AGENTS.md +1 -0
- package/skills/wordcell/SKILL.md +5 -3
- package/skills/wordcell/references/plan-structure.md +1 -1
- package/skills/wordcell/references/query.md +35 -1
- package/skills/wordcell/references/session-memory.md +207 -0
- package/skills/wordcell/references/url-platforms.md +1 -1
- package/src/authoring-import.ts +32 -0
- package/src/authoring-model.ts +185 -2
- package/src/authoring-program.ts +19 -7
- package/src/cli-help.ts +893 -0
- package/src/cli-intro.ts +1 -1
- package/src/cli-program.ts +575 -82
- package/src/cli-style.ts +132 -0
- package/src/clip/acquire.ts +20 -0
- package/src/clip/args.ts +3 -0
- package/src/clip/cli.ts +82 -5
- package/src/clip/cookie-permission.ts +241 -0
- package/src/clip/url-metadata-cli.ts +6 -5
- package/src/clip/url-metadata.ts +14 -0
- package/src/graph.ts +88 -13
- package/src/import-supermemory.ts +1629 -0
- package/src/mcp-server.ts +415 -0
- package/src/mcp-tools.ts +1122 -0
- package/src/mention-index.ts +194 -0
- package/src/pdf/cli.ts +5 -4
- package/src/support-profile.ts +1 -1
- package/src/support.ts +3 -0
- package/src/vault.ts +26 -2
package/src/authoring-model.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { posix, relative, resolve, sep } from "node:path";
|
|
3
3
|
import { Document, isMap, isScalar, isSeq, parseDocument, type YAMLMap, type YAMLSeq } from "yaml";
|
|
4
4
|
import { type NoteLockOptions } from "./note-lock.js";
|
|
5
|
-
import { isCanonicalNoteId, isCanonicalRelationPredicate } from "./graph.js";
|
|
5
|
+
import { isCanonicalNoteId, isCanonicalRelationPredicate, isMetadataNumber } from "./graph.js";
|
|
6
6
|
import { parseDocumentId, parseQualifiedDocumentUri } from "./portfolio-identity.js";
|
|
7
7
|
|
|
8
8
|
export const MAX_NOTE_BYTES = 16 * 1024 * 1024;
|
|
@@ -721,12 +721,188 @@ export function renderUpdatedNoteBody(
|
|
|
721
721
|
return `${header}${parts.newline}${parts.newline}${normalized}`;
|
|
722
722
|
}
|
|
723
723
|
|
|
724
|
-
|
|
724
|
+
/**
|
|
725
|
+
* Extra top-level frontmatter values that internal writers, such as the
|
|
726
|
+
* supermemory importer, own. Public authoring never passes them.
|
|
727
|
+
*/
|
|
728
|
+
export type FrontmatterScalar = string | number | boolean;
|
|
729
|
+
export type FrontmatterFieldValue =
|
|
730
|
+
| FrontmatterScalar
|
|
731
|
+
| readonly string[]
|
|
732
|
+
| Readonly<Record<string, FrontmatterScalar>>;
|
|
733
|
+
export type FrontmatterFields = Readonly<Record<string, FrontmatterFieldValue>>;
|
|
734
|
+
/** Field updates; `null` deletes the key. `title` is allowed here. */
|
|
735
|
+
export type FrontmatterFieldUpdates = Readonly<Record<string, FrontmatterFieldValue | null>>;
|
|
736
|
+
|
|
737
|
+
export const MAX_FRONTMATTER_FIELDS = 64;
|
|
738
|
+
export const MAX_FRONTMATTER_LIST_ITEMS = 256;
|
|
739
|
+
const FRONTMATTER_FIELD_KEY = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
740
|
+
const FRONTMATTER_MAP_KEY = /^[A-Za-z0-9_.:-]{1,64}$/u;
|
|
741
|
+
const RESERVED_FRONTMATTER_FIELDS: ReadonlySet<string> = new Set([
|
|
742
|
+
"document_id", "type", "title", "tags", "relations",
|
|
743
|
+
]);
|
|
744
|
+
|
|
745
|
+
function validateFieldString(value: unknown, label: string): string {
|
|
746
|
+
if (typeof value !== "string") throw new TypeError(`frontmatter field ${label} must be a string`);
|
|
747
|
+
if (
|
|
748
|
+
Buffer.from(value, "utf8").toString("utf8") !== value
|
|
749
|
+
|| /[\u0000-\u001f\u007f]/u.test(value)
|
|
750
|
+
) {
|
|
751
|
+
throw new TypeError(`frontmatter field ${label} must be a single line of well-formed text`);
|
|
752
|
+
}
|
|
753
|
+
return value;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function validateFieldScalar(value: unknown, label: string): FrontmatterScalar {
|
|
757
|
+
if (typeof value === "boolean") return value;
|
|
758
|
+
if (typeof value === "number") {
|
|
759
|
+
if (!isMetadataNumber(value)) {
|
|
760
|
+
throw new TypeError(`frontmatter field ${label} must be a finite number and a safe integer when integral`);
|
|
761
|
+
}
|
|
762
|
+
return value;
|
|
763
|
+
}
|
|
764
|
+
return validateFieldString(value, label);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
function validateFieldValue(value: unknown, key: string): FrontmatterFieldValue {
|
|
768
|
+
if (Array.isArray(value)) {
|
|
769
|
+
if (value.length > MAX_FRONTMATTER_LIST_ITEMS) {
|
|
770
|
+
throw new TypeError(`frontmatter field ${key} has too many items`);
|
|
771
|
+
}
|
|
772
|
+
return value.map((item, index) => validateFieldString(item, `${key}[${index}]`));
|
|
773
|
+
}
|
|
774
|
+
if (typeof value === "object" && value !== null) {
|
|
775
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
776
|
+
throw new TypeError(`frontmatter field ${key} must be a plain map`);
|
|
777
|
+
}
|
|
778
|
+
const entries = Object.entries(value);
|
|
779
|
+
if (entries.length > MAX_FRONTMATTER_FIELDS) {
|
|
780
|
+
throw new TypeError(`frontmatter field ${key} has too many keys`);
|
|
781
|
+
}
|
|
782
|
+
const result: Record<string, FrontmatterScalar> = {};
|
|
783
|
+
for (const [nested, item] of entries) {
|
|
784
|
+
if (!FRONTMATTER_MAP_KEY.test(nested) || nested === "__proto__") {
|
|
785
|
+
throw new TypeError(`frontmatter field ${key} has an invalid key: ${JSON.stringify(nested)}`);
|
|
786
|
+
}
|
|
787
|
+
result[nested] = validateFieldScalar(item, `${key}.${nested}`);
|
|
788
|
+
}
|
|
789
|
+
return result;
|
|
790
|
+
}
|
|
791
|
+
return validateFieldScalar(value, key);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function validateFieldKey(key: string, allowTitle: boolean): void {
|
|
795
|
+
if (!FRONTMATTER_FIELD_KEY.test(key)) {
|
|
796
|
+
throw new TypeError(`not a valid frontmatter field name: ${JSON.stringify(key)}`);
|
|
797
|
+
}
|
|
798
|
+
if (RESERVED_FRONTMATTER_FIELDS.has(key) && !(allowTitle && key === "title")) {
|
|
799
|
+
throw new TypeError(`frontmatter field ${key} is reserved`);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/** Validate importer-owned fields in their given key order. */
|
|
804
|
+
export function validateFrontmatterFields(fields: FrontmatterFields | undefined): FrontmatterFields {
|
|
805
|
+
if (fields === undefined) return {};
|
|
806
|
+
const entries = Object.entries(fields);
|
|
807
|
+
if (entries.length > MAX_FRONTMATTER_FIELDS) throw new TypeError("too many frontmatter fields");
|
|
808
|
+
const result: Record<string, FrontmatterFieldValue> = {};
|
|
809
|
+
for (const [key, value] of entries) {
|
|
810
|
+
validateFieldKey(key, false);
|
|
811
|
+
result[key] = validateFieldValue(value, key);
|
|
812
|
+
}
|
|
813
|
+
return result;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/** Validate field updates; `title` must pass `validateTitle` and `null` deletes. */
|
|
817
|
+
export function validateFrontmatterFieldUpdates(
|
|
818
|
+
fields: FrontmatterFieldUpdates | undefined,
|
|
819
|
+
): FrontmatterFieldUpdates {
|
|
820
|
+
if (fields === undefined) return {};
|
|
821
|
+
const entries = Object.entries(fields);
|
|
822
|
+
if (entries.length > MAX_FRONTMATTER_FIELDS) throw new TypeError("too many frontmatter fields");
|
|
823
|
+
const result: Record<string, FrontmatterFieldValue | null> = {};
|
|
824
|
+
for (const [key, value] of entries) {
|
|
825
|
+
validateFieldKey(key, true);
|
|
826
|
+
if (key === "title") {
|
|
827
|
+
if (typeof value !== "string") throw new TypeError("frontmatter field title must be a string");
|
|
828
|
+
result[key] = validateTitle(value);
|
|
829
|
+
} else {
|
|
830
|
+
result[key] = value === null ? null : validateFieldValue(value, key);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
return result;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function canonicalJson(value: unknown): string {
|
|
837
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
838
|
+
if (typeof value === "object" && value !== null) {
|
|
839
|
+
const keys = Object.keys(value).sort();
|
|
840
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson((value as Record<string, unknown>)[key])}`).join(",")}}`;
|
|
841
|
+
}
|
|
842
|
+
return JSON.stringify(value) ?? "undefined";
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/** The plain value of one top-level key, or `undefined` when absent. */
|
|
846
|
+
export function topLevelValue(parts: FrontmatterParts, key: string): unknown {
|
|
847
|
+
const contents = parts.document.contents;
|
|
848
|
+
if (!isMap(contents) || !contents.has(key)) return undefined;
|
|
849
|
+
const node: unknown = contents.get(key, true);
|
|
850
|
+
if (typeof node === "object" && node !== null && "toJSON" in node && typeof node.toJSON === "function") {
|
|
851
|
+
return (node as { toJSON: () => unknown }).toJSON();
|
|
852
|
+
}
|
|
853
|
+
return node;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
export function sameFrontmatterValue(present: unknown, requested: unknown): boolean {
|
|
857
|
+
return canonicalJson(present) === canonicalJson(requested);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Replace the body and set or delete top-level fields in one rendered write.
|
|
862
|
+
* With no fields this is exactly `renderUpdatedNoteBody`.
|
|
863
|
+
*/
|
|
864
|
+
export function renderUpdatedNoteBodyAndFields(
|
|
865
|
+
snapshot: Pick<NoteSnapshot, "content" | "relativePath">,
|
|
866
|
+
parts: FrontmatterParts,
|
|
867
|
+
body: string,
|
|
868
|
+
fields: FrontmatterFieldUpdates | undefined,
|
|
869
|
+
): string {
|
|
870
|
+
const updates = validateFrontmatterFieldUpdates(fields);
|
|
871
|
+
const bodyOnly = renderUpdatedNoteBody(snapshot, parts, body);
|
|
872
|
+
if (Object.keys(updates).length === 0) return bodyOnly;
|
|
873
|
+
if (!parts.hadFrontmatter || !isMap(parts.document.contents)) {
|
|
874
|
+
throw new TypeError("frontmatter fields require a note with a frontmatter map");
|
|
875
|
+
}
|
|
876
|
+
const contents = parts.document.contents;
|
|
877
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
878
|
+
if (value === null) {
|
|
879
|
+
contents.delete(key);
|
|
880
|
+
} else if (!sameFrontmatterValue(topLevelValue(parts, key), value)) {
|
|
881
|
+
contents.set(key, parts.document.createNode(value));
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
const content = renderFrontmatter({
|
|
885
|
+
...parts,
|
|
886
|
+
bodySuffix: `${parts.newline}${parts.newline}${normalizedRequestedBody(body)}`,
|
|
887
|
+
});
|
|
888
|
+
if (Buffer.byteLength(content, "utf8") > MAX_NOTE_BYTES) {
|
|
889
|
+
throw new RangeError("the note is too large for bounded authoring");
|
|
890
|
+
}
|
|
891
|
+
return content;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
export function renderCreatedNote(
|
|
895
|
+
input: CreateNoteInput,
|
|
896
|
+
documentId: string,
|
|
897
|
+
fields?: FrontmatterFields,
|
|
898
|
+
): string {
|
|
725
899
|
const title = validateTitle(input.title);
|
|
726
900
|
const type = validateType(input.type);
|
|
727
901
|
const tags = validateTags(input.tags);
|
|
902
|
+
const extra = validateFrontmatterFields(fields);
|
|
728
903
|
const metadata: Record<string, unknown> = { document_id: documentId, type, title };
|
|
729
904
|
if (tags.length > 0) metadata["tags"] = tags;
|
|
905
|
+
for (const [key, value] of Object.entries(extra)) metadata[key] = value;
|
|
730
906
|
const document = new Document(metadata, { schema: "core" });
|
|
731
907
|
const body = normalizedRequestedBody(input.body ?? `# ${title}\n`);
|
|
732
908
|
return `---\n${document.toString({ lineWidth: 0 })}---\n\n${body}`;
|
|
@@ -786,8 +962,10 @@ export function assertCompatibleCreate(
|
|
|
786
962
|
snapshot: NoteSnapshot,
|
|
787
963
|
input: CreateNoteInput,
|
|
788
964
|
requestedDocumentId: string | undefined,
|
|
965
|
+
fields?: FrontmatterFields,
|
|
789
966
|
): CompatibleCreate {
|
|
790
967
|
const parts = frontmatter(snapshot.content, snapshot.relativePath);
|
|
968
|
+
const requestedFields = validateFrontmatterFields(fields);
|
|
791
969
|
const requestedType = validateType(input.type);
|
|
792
970
|
const requestedTitle = validateTitle(input.title);
|
|
793
971
|
if (topLevelScalar(parts, "type") !== requestedType) {
|
|
@@ -810,6 +988,11 @@ export function assertCompatibleCreate(
|
|
|
810
988
|
) {
|
|
811
989
|
throw new NoteAlreadyExistsError(snapshot.relativePath, "body differs");
|
|
812
990
|
}
|
|
991
|
+
for (const [key, value] of Object.entries(requestedFields)) {
|
|
992
|
+
if (!sameFrontmatterValue(topLevelValue(parts, key), value)) {
|
|
993
|
+
throw new NoteAlreadyExistsError(snapshot.relativePath, `${key} differs`);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
813
996
|
const existingId = existingDocumentId(parts);
|
|
814
997
|
if (
|
|
815
998
|
requestedDocumentId !== undefined
|
package/src/authoring-program.ts
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
MAX_NOTE_BYTES, NoteRevisionConflictError, addRelationToParts, assertCompatibleCreate,
|
|
5
5
|
assertExpected, canonicalNoteId, canonicalRelationTarget, checkedExpectedRevision,
|
|
6
6
|
existingDocumentId, frontmatter, isErrno, normalizeRelationPredicate, noteResult, pathFor, relationsFromParts,
|
|
7
|
-
removeRelationFromParts, renderCreatedNote, renderFrontmatter,
|
|
8
|
-
sameQuarantinedSnapshot, sameSnapshot, withRecoveryPath,
|
|
7
|
+
removeRelationFromParts, renderCreatedNote, renderFrontmatter, renderUpdatedNoteBodyAndFields, requireRevision, revisionFor,
|
|
8
|
+
sameQuarantinedSnapshot, sameSnapshot, validateFrontmatterFieldUpdates, validateFrontmatterFields, withRecoveryPath,
|
|
9
9
|
} from "./authoring-model.js";
|
|
10
10
|
import type {
|
|
11
11
|
AuthoringDependencies, AuthoringInstallContext, AuthoringOptions, CreateNoteInput,
|
|
12
|
-
DirectoryIdentity, NoteAuthoringResult, NoteRevision, NoteSnapshot, RecoveryLocation, UpdateNoteBodyOptions, Vault,
|
|
12
|
+
DirectoryIdentity, FrontmatterFieldUpdates, FrontmatterFields, NoteAuthoringResult, NoteRevision, NoteSnapshot, RecoveryLocation, UpdateNoteBodyOptions, Vault,
|
|
13
13
|
} from "./authoring-model.js";
|
|
14
14
|
import type { NoteLock } from "./note-lock.js";
|
|
15
15
|
import { parseDocumentId } from "./portfolio-identity.js";
|
|
@@ -211,12 +211,18 @@ function installNote(
|
|
|
211
211
|
});
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
/**
|
|
215
|
+
* `fields` is internal: extra top-level frontmatter keys owned by an importer.
|
|
216
|
+
* The public `createNote` never passes it.
|
|
217
|
+
*/
|
|
214
218
|
export function createNoteProgram(
|
|
215
219
|
platform: AuthoringPlatform, root: string, input: CreateNoteInput, options: AuthoringOptions,
|
|
220
|
+
fields?: FrontmatterFields,
|
|
216
221
|
): Effect.Effect<NoteAuthoringResult, AuthoringFailure> {
|
|
217
222
|
return Effect.gen(function*() {
|
|
218
223
|
const vault = yield* authoringNative(() => platform.resolveVault(root));
|
|
219
224
|
const id = yield* authoringSync(() => canonicalNoteId(input.id));
|
|
225
|
+
const extra = yield* authoringSync(() => validateFrontmatterFields(fields));
|
|
220
226
|
const requestedDocumentId = yield* authoringSync(() => input.documentId === undefined ? undefined : parseDocumentId(input.documentId));
|
|
221
227
|
const expected = yield* authoringSync(() => checkedExpectedRevision(options));
|
|
222
228
|
const dependencies = yield* authoringSync(() => platform.dependenciesFor(options.dependencies));
|
|
@@ -227,12 +233,12 @@ export function createNoteProgram(
|
|
|
227
233
|
const existing = yield* authoringNative(() => platform.readOptionalSnapshot(vault, id));
|
|
228
234
|
if (existing !== null) {
|
|
229
235
|
yield* authoringSync(() => assertExpected(existing, expected));
|
|
230
|
-
const compatible = yield* authoringSync(() => assertCompatibleCreate(existing, input, requestedDocumentId));
|
|
236
|
+
const compatible = yield* authoringSync(() => assertCompatibleCreate(existing, input, requestedDocumentId, extra));
|
|
231
237
|
return yield* authoringSync(() => noteResult(existing, compatible.relations, false, compatible.documentId));
|
|
232
238
|
}
|
|
233
239
|
if (expected !== undefined) return yield* fail(new NoteRevisionConflictError(`${id}.md`, expected, null));
|
|
234
240
|
const documentId = yield* authoringSync(() => requestedDocumentId ?? parseDocumentId(dependencies.documentId()));
|
|
235
|
-
const content = yield* authoringSync(() => renderCreatedNote(input, documentId));
|
|
241
|
+
const content = yield* authoringSync(() => renderCreatedNote(input, documentId, extra));
|
|
236
242
|
const revision = yield* installNote(platform, vault, id, content, null, lock, dependencies);
|
|
237
243
|
return { changed: true, path: `${id}.md`, revision, relations: [], documentId };
|
|
238
244
|
}),
|
|
@@ -277,12 +283,18 @@ export function editNoteRelationProgram(
|
|
|
277
283
|
});
|
|
278
284
|
}
|
|
279
285
|
|
|
286
|
+
/**
|
|
287
|
+
* `fields` is internal: top-level frontmatter keys (and `title`) set in the
|
|
288
|
+
* same atomic write, where `null` deletes a key. The public `updateNoteBody`
|
|
289
|
+
* never passes it, so the authored frontmatter bytes stay untouched there.
|
|
290
|
+
*/
|
|
280
291
|
export function updateNoteBodyProgram(
|
|
281
292
|
platform: AuthoringPlatform, root: string, idInput: string, body: string,
|
|
282
|
-
options: UpdateNoteBodyOptions,
|
|
293
|
+
options: UpdateNoteBodyOptions, fields?: FrontmatterFieldUpdates,
|
|
283
294
|
): Effect.Effect<NoteAuthoringResult, AuthoringFailure> {
|
|
284
295
|
return Effect.gen(function*() {
|
|
285
296
|
const id = yield* authoringSync(() => canonicalNoteId(idInput));
|
|
297
|
+
const updates = yield* authoringSync(() => validateFrontmatterFieldUpdates(fields));
|
|
286
298
|
const expected = yield* authoringSync(() => {
|
|
287
299
|
if (typeof options?.expectedRevision !== "string") {
|
|
288
300
|
throw new TypeError("expectedRevision is required for a note body update");
|
|
@@ -302,7 +314,7 @@ export function updateNoteBodyProgram(
|
|
|
302
314
|
if (identity.kind === "invalid") return yield* fail(new TypeError("the note has an invalid document_id"));
|
|
303
315
|
const documentId = identity.kind === "valid" ? identity.documentId : undefined;
|
|
304
316
|
const relations = yield* authoringSync(() => relationsFromParts(parts, source.relativePath));
|
|
305
|
-
const content = yield* authoringSync(() =>
|
|
317
|
+
const content = yield* authoringSync(() => renderUpdatedNoteBodyAndFields(source, parts, body, updates));
|
|
306
318
|
if (content === source.content) return yield* authoringSync(() => noteResult(source, relations, false, documentId));
|
|
307
319
|
const revision = yield* installNote(platform, vault, id, content, source, lock, dependencies);
|
|
308
320
|
return yield* authoringSync(() => noteResult({ relativePath: source.relativePath, revision }, relations, true, documentId));
|