apple-notes-mcp 2.15.3 → 2.15.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/build/index.js +498 -94
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -40498,6 +40498,87 @@ function styleValue(field) {
|
|
|
40498
40498
|
}
|
|
40499
40499
|
return Buffer.from(field.value).toString("hex");
|
|
40500
40500
|
}
|
|
40501
|
+
function storedStyleSignature(data) {
|
|
40502
|
+
return JSON.stringify(
|
|
40503
|
+
decodeWireFields(data).filter((field) => field.fieldNumber !== 1).map((field) => [
|
|
40504
|
+
field.fieldNumber,
|
|
40505
|
+
field.wireType,
|
|
40506
|
+
field.bytes ? styleValue({
|
|
40507
|
+
fieldNumber: field.fieldNumber,
|
|
40508
|
+
wireType: field.wireType,
|
|
40509
|
+
value: field.bytes
|
|
40510
|
+
}) : field.varint.toString()
|
|
40511
|
+
])
|
|
40512
|
+
);
|
|
40513
|
+
}
|
|
40514
|
+
function nativeRunSemantics(data) {
|
|
40515
|
+
const result = {
|
|
40516
|
+
complete: true,
|
|
40517
|
+
unknown: false,
|
|
40518
|
+
structuredParagraph: false,
|
|
40519
|
+
links: false,
|
|
40520
|
+
objects: []
|
|
40521
|
+
};
|
|
40522
|
+
const check2 = (fields, schema) => {
|
|
40523
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40524
|
+
for (const field of fields) {
|
|
40525
|
+
if (!Object.hasOwn(schema, field.fieldNumber)) result.unknown = true;
|
|
40526
|
+
else if (seen.has(field.fieldNumber) || field.wireType !== schema[field.fieldNumber])
|
|
40527
|
+
result.complete = false;
|
|
40528
|
+
seen.add(field.fieldNumber);
|
|
40529
|
+
}
|
|
40530
|
+
};
|
|
40531
|
+
const nested = (field) => {
|
|
40532
|
+
if (field.wireType !== 2 || !field.bytes) {
|
|
40533
|
+
result.complete = false;
|
|
40534
|
+
return [];
|
|
40535
|
+
}
|
|
40536
|
+
return decodeWireFields(field.bytes);
|
|
40537
|
+
};
|
|
40538
|
+
try {
|
|
40539
|
+
const fields = decodeWireFields(data);
|
|
40540
|
+
check2(fields, { 1: 0, 2: 2, 3: 2, 5: 0, 6: 0, 7: 0, 8: 0, 9: 2, 10: 2, 12: 2, 14: 0 });
|
|
40541
|
+
if (fields.filter((f) => f.fieldNumber === 1).length !== 1) result.complete = false;
|
|
40542
|
+
result.links = fields.some((f) => f.fieldNumber === 9);
|
|
40543
|
+
for (const field of fields) {
|
|
40544
|
+
if (field.fieldNumber === 2) {
|
|
40545
|
+
const paragraph = nested(field);
|
|
40546
|
+
check2(paragraph, { 1: 0, 2: 0, 4: 0, 5: 2, 8: 0, 9: 2 });
|
|
40547
|
+
for (const style of paragraph.filter((f) => f.fieldNumber === 1)) {
|
|
40548
|
+
const type = style.varint === void 0 ? void 0 : Number(BigInt.asIntN(64, style.varint));
|
|
40549
|
+
if (type !== void 0 && type >= 100 && type <= 103) result.structuredParagraph = true;
|
|
40550
|
+
else if (type === void 0 || ![-1, 0, 1, 2, 3, 4].includes(type)) result.unknown = true;
|
|
40551
|
+
}
|
|
40552
|
+
for (const uuid2 of paragraph.filter((f) => f.fieldNumber === 9))
|
|
40553
|
+
if (uuid2.bytes?.length !== 16) result.complete = false;
|
|
40554
|
+
for (const todo of paragraph.filter((f) => f.fieldNumber === 5)) {
|
|
40555
|
+
result.structuredParagraph = true;
|
|
40556
|
+
const item = nested(todo);
|
|
40557
|
+
check2(item, { 1: 2, 2: 0 });
|
|
40558
|
+
const id2 = item.filter((f) => f.fieldNumber === 1);
|
|
40559
|
+
if (id2.length !== 1 || id2[0].bytes?.length !== 16) result.complete = false;
|
|
40560
|
+
}
|
|
40561
|
+
} else if (field.fieldNumber === 3) check2(nested(field), { 1: 2, 2: 5, 3: 0 });
|
|
40562
|
+
else if (field.fieldNumber === 10) check2(nested(field), { 1: 5, 2: 5, 3: 5, 4: 5 });
|
|
40563
|
+
else if (field.fieldNumber === 12) {
|
|
40564
|
+
const attachment = nested(field);
|
|
40565
|
+
check2(attachment, { 1: 2, 2: 2 });
|
|
40566
|
+
const ids = attachment.filter((f) => f.fieldNumber === 1), types = attachment.filter((f) => f.fieldNumber === 2);
|
|
40567
|
+
if (ids.length !== 1 || types.length !== 1 || !ids[0].bytes || !types[0].bytes) {
|
|
40568
|
+
result.complete = false;
|
|
40569
|
+
continue;
|
|
40570
|
+
}
|
|
40571
|
+
const utf82 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
40572
|
+
const id2 = utf82.decode(ids[0].bytes), type = utf82.decode(types[0].bytes);
|
|
40573
|
+
if (!id2 || !type) result.complete = false;
|
|
40574
|
+
result.objects.push({ id: id2, type });
|
|
40575
|
+
}
|
|
40576
|
+
}
|
|
40577
|
+
} catch {
|
|
40578
|
+
result.complete = false;
|
|
40579
|
+
}
|
|
40580
|
+
return result;
|
|
40581
|
+
}
|
|
40501
40582
|
function parseRichNote(data, nativeTags = [], options = {}) {
|
|
40502
40583
|
const doc = decodeMessage(data);
|
|
40503
40584
|
const wrapper = embeddedMessage(getField(doc, 2));
|
|
@@ -40526,9 +40607,8 @@ function parseRichNote(data, nativeTags = [], options = {}) {
|
|
|
40526
40607
|
highlight: Boolean(varintValue(getField(fields, 14))),
|
|
40527
40608
|
start: position,
|
|
40528
40609
|
length,
|
|
40529
|
-
signature:
|
|
40530
|
-
|
|
40531
|
-
)
|
|
40610
|
+
signature: storedStyleSignature(run.value),
|
|
40611
|
+
nativeSemantics: nativeRunSemantics(run.value)
|
|
40532
40612
|
});
|
|
40533
40613
|
if (/[^\s\ufffc]/u.test(text2.slice(position, position + length))) {
|
|
40534
40614
|
const baseline = varintValue(getField(fields, 8)) ?? 0;
|
|
@@ -40596,7 +40676,7 @@ function parseRichNote(data, nativeTags = [], options = {}) {
|
|
|
40596
40676
|
function readRichNote(id2, options = {}) {
|
|
40597
40677
|
const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]+)$/i.exec(id2)?.[1];
|
|
40598
40678
|
if (!pk) throw new Error("Invalid exact note ID");
|
|
40599
|
-
const sql = `BEGIN; SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE=${pk}; SELECT json_group_object(ZIDENTIFIER,ZALTTEXT) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} AND ZTYPEUTI1='com.apple.notes.inlinetextattachment.hashtag'; SELECT json_group_array(json_object('id',ZIDENTIFIER,'pk',Z_PK,'type',COALESCE(ZTYPEUTI1,ZTYPEUTI),'mergeable',hex(COALESCE(ZMERGEABLEDATA1,ZMERGEABLEDATA)),'view',ZATTACHMENTVIEWTYPE)) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} OR ZNOTE=${pk}; COMMIT;`;
|
|
40679
|
+
const sql = `BEGIN; SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE=${pk}; SELECT json_group_object(ZIDENTIFIER,ZALTTEXT) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} AND ZTYPEUTI1='com.apple.notes.inlinetextattachment.hashtag'; SELECT json_group_array(json_object('id',ZIDENTIFIER,'pk',Z_PK,'type',COALESCE(ZTYPEUTI1,ZTYPEUTI),'mergeable',hex(COALESCE(ZMERGEABLEDATA1,ZMERGEABLEDATA)),'view',ZATTACHMENTVIEWTYPE,'altText',ZALTTEXT)) FROM ZICCLOUDSYNCINGOBJECT WHERE ZNOTE1=${pk} OR ZNOTE=${pk}; COMMIT;`;
|
|
40600
40680
|
const rows = execFileSync4("/usr/bin/sqlite3", ["-readonly", dbPath, sql], {
|
|
40601
40681
|
encoding: "utf8",
|
|
40602
40682
|
timeout: 5e3,
|
|
@@ -40615,14 +40695,21 @@ function readRichNote(id2, options = {}) {
|
|
|
40615
40695
|
const tagMap = tags;
|
|
40616
40696
|
const objectData = JSON.parse(rows[2] || "[]");
|
|
40617
40697
|
if (!Array.isArray(objectData) || objectData.some(
|
|
40618
|
-
(row) => !row || typeof row.id !== "string" || !Number.isInteger(row.pk) || typeof row.mergeable !== "string" || !/^[0-9a-f]*$/i.test(row.mergeable)
|
|
40698
|
+
(row) => !row || typeof row.id !== "string" || !Number.isInteger(row.pk) || typeof row.mergeable !== "string" || !/^[0-9a-f]*$/i.test(row.mergeable) || row.altText !== void 0 && row.altText !== null && typeof row.altText !== "string"
|
|
40619
40699
|
))
|
|
40620
40700
|
throw new Error("Invalid native object metadata");
|
|
40621
40701
|
rich.objectData = uniqueById(
|
|
40622
40702
|
objectData.filter((row) => rich.nativeObjectIds.includes(row.id)).sort((a, b) => a.id.localeCompare(b.id))
|
|
40623
40703
|
);
|
|
40624
|
-
|
|
40625
|
-
rich.
|
|
40704
|
+
const selectedRows = new Map(rich.objectData.map((row) => [row.id, row]));
|
|
40705
|
+
rich.nativeObjectDataComplete = objectData.every((candidate) => {
|
|
40706
|
+
const row = selectedRows.get(candidate.id);
|
|
40707
|
+
return !row || ["pk", "type", "mergeable", "view", "altText"].every(
|
|
40708
|
+
(key) => candidate[key] === row[key]
|
|
40709
|
+
);
|
|
40710
|
+
});
|
|
40711
|
+
rich.revision = createHash("sha256").update(rich.revision).update(JSON.stringify(rich.objectData)).update(JSON.stringify(rich.nativeObjectDataComplete)).digest("hex");
|
|
40712
|
+
rich.nativeTagObjectIds = /* @__PURE__ */ Object.create(null);
|
|
40626
40713
|
for (const id3 of rich.nativeObjectIds)
|
|
40627
40714
|
if (tagMap[id3]) {
|
|
40628
40715
|
const tag = tagMap[id3].replace(/^#/, "");
|
|
@@ -48626,6 +48713,188 @@ import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as
|
|
|
48626
48713
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
48627
48714
|
import { join as join17 } from "node:path";
|
|
48628
48715
|
|
|
48716
|
+
// src/utils/richContentPreservation.ts
|
|
48717
|
+
var TAG_OBJECT_TYPE = "com.apple.notes.inlinetextattachment.hashtag";
|
|
48718
|
+
function requirePreservationMetadata(rich) {
|
|
48719
|
+
const unavailable = (message = "Native object, checklist or formatting preservation metadata is unavailable") => {
|
|
48720
|
+
throw new Error(message);
|
|
48721
|
+
};
|
|
48722
|
+
const { objects, objectData, checklistItems, styleRuns, nativeTagObjectIds } = rich;
|
|
48723
|
+
if (!objects || !objectData || !checklistItems || !styleRuns || !nativeTagObjectIds || rich.nativeObjectDataComplete !== true)
|
|
48724
|
+
return unavailable();
|
|
48725
|
+
const ids = new Set(rich.nativeObjectIds);
|
|
48726
|
+
if (ids.size !== rich.nativeObjectIds.length || objects.length !== ids.size || objectData.length !== ids.size || new Set(objects.map((o) => o.id)).size !== ids.size || new Set(objectData.map((o) => o.id)).size !== ids.size || rich.hasNativeObjects !== Boolean(ids.size) || rich.hasChecklist !== Boolean(checklistItems.length))
|
|
48727
|
+
return unavailable();
|
|
48728
|
+
let objectEnd = 0;
|
|
48729
|
+
for (const object3 of objects) {
|
|
48730
|
+
const data = objectData.find((row) => row.id === object3.id);
|
|
48731
|
+
if (!ids.has(object3.id) || !Number.isInteger(object3.start) || !Number.isInteger(object3.length) || object3.start < objectEnd || object3.length < 1 || object3.start + object3.length > rich.text.length || !data || data.type !== object3.type || !Number.isInteger(data.pk) || !/^[0-9a-f]*$/iu.test(data.mergeable) || data.view !== null && !Number.isInteger(data.view) || !Object.hasOwn(data, "altText") || data.altText !== null && typeof data.altText !== "string")
|
|
48732
|
+
return unavailable();
|
|
48733
|
+
objectEnd = object3.start + object3.length;
|
|
48734
|
+
}
|
|
48735
|
+
const mappedIds = /* @__PURE__ */ new Set();
|
|
48736
|
+
if (new Set(rich.nativeTags).size !== rich.nativeTags.length || Object.keys(nativeTagObjectIds).length !== rich.nativeTags.length)
|
|
48737
|
+
return unavailable();
|
|
48738
|
+
for (const [tag, tagIds] of Object.entries(nativeTagObjectIds)) {
|
|
48739
|
+
if (!rich.nativeTags.includes(tag) || !tagIds.length) return unavailable();
|
|
48740
|
+
for (const id2 of tagIds) {
|
|
48741
|
+
if (mappedIds.has(id2) || !objects.some((o) => o.id === id2 && o.type === TAG_OBJECT_TYPE) || objectData.find((o) => o.id === id2)?.altText?.replace(/^#/, "") !== tag)
|
|
48742
|
+
return unavailable();
|
|
48743
|
+
mappedIds.add(id2);
|
|
48744
|
+
}
|
|
48745
|
+
}
|
|
48746
|
+
if (objects.some((o) => o.type === TAG_OBJECT_TYPE && !mappedIds.has(o.id))) return unavailable();
|
|
48747
|
+
if (new Set(checklistItems.map((item) => item.id)).size !== checklistItems.length)
|
|
48748
|
+
return unavailable();
|
|
48749
|
+
for (const item of checklistItems) {
|
|
48750
|
+
const end = item.start + item.text.length;
|
|
48751
|
+
if (!item.id || !Number.isInteger(item.start) || item.start < 0 || end > rich.text.length || rich.text.slice(item.start, end) !== item.text || end < rich.text.length && rich.text[end] !== "\n")
|
|
48752
|
+
return unavailable();
|
|
48753
|
+
}
|
|
48754
|
+
let position = 0;
|
|
48755
|
+
const firstReferences = /* @__PURE__ */ new Map();
|
|
48756
|
+
for (const run of styleRuns) {
|
|
48757
|
+
if (run.start !== position || !Number.isInteger(run.length) || run.length < 0 || typeof run.signature !== "string" || run.nativeSemantics?.complete !== true || run.length === 0 && (run.nativeSemantics.unknown || run.nativeSemantics.structuredParagraph || run.nativeSemantics.links || run.nativeSemantics.objects.length > 0))
|
|
48758
|
+
return unavailable();
|
|
48759
|
+
const references = run.nativeSemantics.objects;
|
|
48760
|
+
if (references.length > 1 || rich.text.slice(run.start, run.start + run.length).includes("\uFFFC") && references.length !== 1)
|
|
48761
|
+
return unavailable("Native object placement preservation metadata is unavailable");
|
|
48762
|
+
for (const reference of references) {
|
|
48763
|
+
if (!objectData.some((data) => data.id === reference.id && data.type === reference.type))
|
|
48764
|
+
return unavailable("Native object placement preservation metadata is unavailable");
|
|
48765
|
+
if (!firstReferences.has(reference.id))
|
|
48766
|
+
firstReferences.set(reference.id, {
|
|
48767
|
+
start: run.start,
|
|
48768
|
+
length: run.length,
|
|
48769
|
+
type: reference.type
|
|
48770
|
+
});
|
|
48771
|
+
}
|
|
48772
|
+
position += run.length;
|
|
48773
|
+
}
|
|
48774
|
+
if (position !== rich.text.length) return unavailable();
|
|
48775
|
+
if (objects.some((object3) => {
|
|
48776
|
+
const reference = firstReferences.get(object3.id);
|
|
48777
|
+
return !reference || reference.start !== object3.start || reference.length !== object3.length || reference.type !== object3.type;
|
|
48778
|
+
}))
|
|
48779
|
+
return unavailable("Native object placement preservation metadata is unavailable");
|
|
48780
|
+
for (const link of rich.links)
|
|
48781
|
+
if (!Number.isInteger(link.start) || !Number.isInteger(link.length) || link.start < 0 || link.length < 1 || link.start + link.length > rich.text.length || rich.text.slice(link.start, link.start + link.length) !== link.text)
|
|
48782
|
+
return unavailable();
|
|
48783
|
+
}
|
|
48784
|
+
function assertZeroLengthFormatting(before, after, spans) {
|
|
48785
|
+
const oldEmpty = before.styleRuns.filter((run) => run.length === 0).map((run) => {
|
|
48786
|
+
const span = spans.find(
|
|
48787
|
+
(span2) => run.start >= span2.before && run.start <= span2.before + span2.length
|
|
48788
|
+
);
|
|
48789
|
+
if (!span) throw new Error("Zero-length formatting falls inside the requested removal");
|
|
48790
|
+
return { ...run, start: span.after + run.start - span.before };
|
|
48791
|
+
});
|
|
48792
|
+
const newEmpty = after.styleRuns.filter(
|
|
48793
|
+
(run) => run.length === 0 && spans.some((span) => run.start >= span.after && run.start <= span.after + span.length)
|
|
48794
|
+
);
|
|
48795
|
+
if (JSON.stringify(oldEmpty) !== JSON.stringify(newEmpty))
|
|
48796
|
+
throw new Error("Existing zero-length formatting metadata changed");
|
|
48797
|
+
}
|
|
48798
|
+
function retainedSpans(before, after, change) {
|
|
48799
|
+
if (change.kind !== "remove-tag") {
|
|
48800
|
+
if (change.kind === "append" ? !after.text.startsWith(before.text) : after.text !== before.text)
|
|
48801
|
+
throw new Error("Existing note text or whitespace was not preserved");
|
|
48802
|
+
return {
|
|
48803
|
+
spans: [{ before: 0, after: 0, length: before.text.length }],
|
|
48804
|
+
removed: /* @__PURE__ */ new Set()
|
|
48805
|
+
};
|
|
48806
|
+
}
|
|
48807
|
+
const removed = new Set(before.nativeTagObjectIds[change.tag]);
|
|
48808
|
+
if (!removed.size || after.nativeTags.includes(change.tag))
|
|
48809
|
+
throw new Error("Native tag removal was not verified");
|
|
48810
|
+
const spans = [];
|
|
48811
|
+
let position = 0, output = "";
|
|
48812
|
+
for (const object3 of before.objects.filter((object4) => removed.has(object4.id))) {
|
|
48813
|
+
const text2 = before.text.slice(object3.start, object3.start + object3.length);
|
|
48814
|
+
if (text2 !== "\uFFFC" && text2 !== `#${change.tag}`)
|
|
48815
|
+
throw new Error("Native tag removal range is unsupported");
|
|
48816
|
+
const prefix = before.text.slice(position, object3.start);
|
|
48817
|
+
spans.push({ before: position, after: output.length, length: prefix.length });
|
|
48818
|
+
output += prefix;
|
|
48819
|
+
position = object3.start + object3.length;
|
|
48820
|
+
}
|
|
48821
|
+
spans.push({ before: position, after: output.length, length: before.text.length - position });
|
|
48822
|
+
output += before.text.slice(position);
|
|
48823
|
+
if (after.text !== output)
|
|
48824
|
+
throw new Error("Existing note text or whitespace was not preserved around the removed tag");
|
|
48825
|
+
return { spans, removed };
|
|
48826
|
+
}
|
|
48827
|
+
function assertRetainedRichContent(before, after, change = { kind: "unchanged" }) {
|
|
48828
|
+
requirePreservationMetadata(before);
|
|
48829
|
+
requirePreservationMetadata(after);
|
|
48830
|
+
const { spans, removed } = retainedSpans(before, after, change);
|
|
48831
|
+
const rangeStart = (start, length) => {
|
|
48832
|
+
const span = spans.find(
|
|
48833
|
+
(span2) => start >= span2.before && start + length <= span2.before + span2.length
|
|
48834
|
+
);
|
|
48835
|
+
return span && span.after + start - span.before;
|
|
48836
|
+
};
|
|
48837
|
+
const retainedIds = before.nativeObjectIds.filter((id2) => !removed.has(id2));
|
|
48838
|
+
if (retainedIds.some((id2) => !after.nativeObjectIds.includes(id2)) || [...removed].some((id2) => after.nativeObjectIds.includes(id2)) || change.kind !== "append" && after.nativeObjectIds.length !== retainedIds.length || change.kind === "append" && after.objects.some(
|
|
48839
|
+
(object3) => !retainedIds.includes(object3.id) && object3.start < before.text.length
|
|
48840
|
+
))
|
|
48841
|
+
throw new Error("Existing native object identity or placement changed");
|
|
48842
|
+
for (const object3 of before.objects.filter((object4) => !removed.has(object4.id))) {
|
|
48843
|
+
const actual = after.objects.find((current) => current.id === object3.id);
|
|
48844
|
+
if (actual.type !== object3.type || actual.length !== object3.length || actual.start !== rangeStart(object3.start, object3.length))
|
|
48845
|
+
throw new Error("Existing native object range changed");
|
|
48846
|
+
const oldData = before.objectData.find((row) => row.id === object3.id), newData = after.objectData.find((row) => row.id === object3.id);
|
|
48847
|
+
if (oldData.pk !== newData.pk || oldData.type !== newData.type || oldData.mergeable !== newData.mergeable || oldData.view !== newData.view || oldData.altText !== newData.altText)
|
|
48848
|
+
throw new Error("Existing native object payload changed");
|
|
48849
|
+
}
|
|
48850
|
+
const tags = before.nativeTags.filter(
|
|
48851
|
+
(tag) => change.kind !== "remove-tag" || tag !== change.tag
|
|
48852
|
+
);
|
|
48853
|
+
if (tags.some(
|
|
48854
|
+
(tag) => JSON.stringify(before.nativeTagObjectIds[tag]) !== JSON.stringify(after.nativeTagObjectIds[tag])
|
|
48855
|
+
) || change.kind !== "append" && after.nativeTags.length !== tags.length)
|
|
48856
|
+
throw new Error("Existing native tag identities changed");
|
|
48857
|
+
const links = change.kind === "append" ? after.links.filter((link) => link.start < before.text.length) : after.links;
|
|
48858
|
+
if (links.length !== before.links.length || before.links.some((link, index) => {
|
|
48859
|
+
const actual = links[index];
|
|
48860
|
+
return actual.text !== link.text || actual.url !== link.url || actual.length !== link.length || actual.start !== rangeStart(link.start, link.length);
|
|
48861
|
+
}))
|
|
48862
|
+
throw new Error("Existing links or their ranges were not preserved");
|
|
48863
|
+
const items = change.kind === "append" ? after.checklistItems.filter((item) => item.start < before.text.length) : after.checklistItems;
|
|
48864
|
+
if (items.length !== before.checklistItems.length || before.checklistItems.some((item, index) => {
|
|
48865
|
+
const actual = items[index];
|
|
48866
|
+
const retained = spans.flatMap((span) => {
|
|
48867
|
+
const start2 = Math.max(item.start, span.before), end = Math.min(item.start + item.text.length, span.before + span.length);
|
|
48868
|
+
return start2 < end ? [before.text.slice(start2, end)] : [];
|
|
48869
|
+
}).join("");
|
|
48870
|
+
const startSpan = spans.find(
|
|
48871
|
+
(span) => item.start >= span.before && item.start <= span.before + span.length
|
|
48872
|
+
);
|
|
48873
|
+
const start = startSpan && startSpan.after + item.start - startSpan.before;
|
|
48874
|
+
return actual.id !== item.id || actual.text !== retained || actual.done !== item.done || actual.start !== start;
|
|
48875
|
+
}))
|
|
48876
|
+
throw new Error("Existing checklist item identity, state or range changed");
|
|
48877
|
+
for (const span of spans) {
|
|
48878
|
+
let offset = 0, oldIndex = 0, newIndex = 0;
|
|
48879
|
+
while (offset < span.length) {
|
|
48880
|
+
const oldPosition = span.before + offset, newPosition = span.after + offset;
|
|
48881
|
+
while (before.styleRuns[oldIndex].start + before.styleRuns[oldIndex].length <= oldPosition)
|
|
48882
|
+
oldIndex++;
|
|
48883
|
+
while (after.styleRuns[newIndex].start + after.styleRuns[newIndex].length <= newPosition)
|
|
48884
|
+
newIndex++;
|
|
48885
|
+
const oldRun = before.styleRuns[oldIndex], newRun = after.styleRuns[newIndex];
|
|
48886
|
+
if (oldRun.signature !== newRun.signature || oldRun.paragraphStyle !== newRun.paragraphStyle || oldRun.blockQuote !== newRun.blockQuote || oldRun.highlight !== newRun.highlight)
|
|
48887
|
+
throw new Error("Existing rich formatting changed");
|
|
48888
|
+
offset += Math.min(
|
|
48889
|
+
span.length - offset,
|
|
48890
|
+
oldRun.start + oldRun.length - oldPosition,
|
|
48891
|
+
newRun.start + newRun.length - newPosition
|
|
48892
|
+
);
|
|
48893
|
+
}
|
|
48894
|
+
}
|
|
48895
|
+
assertZeroLengthFormatting(before, after, spans);
|
|
48896
|
+
}
|
|
48897
|
+
|
|
48629
48898
|
// src/services/shortcutConsent.ts
|
|
48630
48899
|
function shortcutConsentHint(shortcut) {
|
|
48631
48900
|
const target = shortcut ? `"${shortcut}"` : "the bridge Shortcut";
|
|
@@ -48649,6 +48918,144 @@ function normalizeNativeTags(tags) {
|
|
|
48649
48918
|
)
|
|
48650
48919
|
];
|
|
48651
48920
|
}
|
|
48921
|
+
var TAG_OBJECT_TYPE2 = "com.apple.notes.inlinetextattachment.hashtag";
|
|
48922
|
+
function plainNativeRange(rich, start, length) {
|
|
48923
|
+
return rich.styleRuns.every((run) => {
|
|
48924
|
+
if (run.start >= start + length || run.start + run.length <= start) return true;
|
|
48925
|
+
const metadata = run.nativeSemantics;
|
|
48926
|
+
return metadata.complete && !metadata.unknown && !metadata.structuredParagraph && !metadata.links && !metadata.objects.length;
|
|
48927
|
+
});
|
|
48928
|
+
}
|
|
48929
|
+
function preservedTextSpans(before, after, missing) {
|
|
48930
|
+
const oldIds = new Set(before.nativeObjectIds);
|
|
48931
|
+
const added = after.objects.filter((o) => !oldIds.has(o.id));
|
|
48932
|
+
const tagsById = new Map(
|
|
48933
|
+
Object.entries(after.nativeTagObjectIds).flatMap(
|
|
48934
|
+
([tag, ids]) => ids.map((id2) => [id2, tag])
|
|
48935
|
+
)
|
|
48936
|
+
);
|
|
48937
|
+
if (!added.length || added.some((o) => o.type !== TAG_OBJECT_TYPE2 || !missing.includes(tagsById.get(o.id))) || missing.some((tag) => !added.some((o) => tagsById.get(o.id) === tag)))
|
|
48938
|
+
return void 0;
|
|
48939
|
+
const spans = [];
|
|
48940
|
+
let oldPosition = 0, newPosition = 0, appended = false;
|
|
48941
|
+
for (const object3 of added) {
|
|
48942
|
+
const prefix = after.text.slice(newPosition, object3.start);
|
|
48943
|
+
const remaining2 = before.text.slice(oldPosition);
|
|
48944
|
+
const length = Math.min(prefix.length, remaining2.length);
|
|
48945
|
+
if (prefix.slice(0, length) !== remaining2.slice(0, length) || prefix.length > length && !/^[ \n]*$/u.test(prefix.slice(length)))
|
|
48946
|
+
return void 0;
|
|
48947
|
+
if (length) spans.push({ before: oldPosition, after: newPosition, length });
|
|
48948
|
+
oldPosition += length;
|
|
48949
|
+
const literal2 = `#${tagsById.get(object3.id)}`;
|
|
48950
|
+
const rendered = after.text.slice(object3.start, object3.start + object3.length);
|
|
48951
|
+
if (rendered !== "\uFFFC" && rendered !== literal2) return void 0;
|
|
48952
|
+
const previous = [...before.text.slice(Math.max(0, oldPosition - 2), oldPosition)].at(-1) || "";
|
|
48953
|
+
const next = String.fromCodePoint(before.text.codePointAt(oldPosition + literal2.length) ?? 0);
|
|
48954
|
+
if (oldPosition === before.text.length) appended = true;
|
|
48955
|
+
else if (before.text.startsWith(literal2, oldPosition) && !/[\p{L}\p{M}\p{N}_#-]/u.test(previous) && !/[\p{L}\p{M}\p{N}_-]/u.test(next) && plainNativeRange(before, oldPosition, literal2.length))
|
|
48956
|
+
oldPosition += literal2.length;
|
|
48957
|
+
else return void 0;
|
|
48958
|
+
newPosition = object3.start + object3.length;
|
|
48959
|
+
}
|
|
48960
|
+
const remaining = before.text.slice(oldPosition);
|
|
48961
|
+
const suffix = after.text.slice(newPosition);
|
|
48962
|
+
if (suffix !== remaining && !(appended && !remaining && /^[ \n]*$/u.test(suffix)))
|
|
48963
|
+
return void 0;
|
|
48964
|
+
if (remaining.length)
|
|
48965
|
+
spans.push({ before: oldPosition, after: newPosition, length: remaining.length });
|
|
48966
|
+
return spans;
|
|
48967
|
+
}
|
|
48968
|
+
function preservesAddedSemantics(before, after, spans) {
|
|
48969
|
+
const oldIds = new Set(before.nativeObjectIds);
|
|
48970
|
+
const added = after.objects.filter((o) => !oldIds.has(o.id));
|
|
48971
|
+
const changed = [];
|
|
48972
|
+
let end = 0;
|
|
48973
|
+
for (const span of spans) {
|
|
48974
|
+
if (end < span.after) changed.push({ start: end, end: span.after });
|
|
48975
|
+
end = span.after + span.length;
|
|
48976
|
+
}
|
|
48977
|
+
if (end < after.text.length) changed.push({ start: end, end: after.text.length });
|
|
48978
|
+
for (const run of after.styleRuns) {
|
|
48979
|
+
if (!changed.some((range) => run.start < range.end && run.start + run.length > range.start))
|
|
48980
|
+
continue;
|
|
48981
|
+
const metadata = run.nativeSemantics;
|
|
48982
|
+
if (!metadata.complete || metadata.unknown || metadata.structuredParagraph || metadata.links)
|
|
48983
|
+
return false;
|
|
48984
|
+
const object3 = added.find((o) => run.start === o.start && run.length === o.length);
|
|
48985
|
+
if (object3) {
|
|
48986
|
+
if (metadata.objects.length !== 1 || metadata.objects[0].id !== object3.id || metadata.objects[0].type !== object3.type)
|
|
48987
|
+
return false;
|
|
48988
|
+
} else if (metadata.objects.length || added.some((o) => run.start < o.start + o.length && run.start + run.length > o.start))
|
|
48989
|
+
return false;
|
|
48990
|
+
}
|
|
48991
|
+
return true;
|
|
48992
|
+
}
|
|
48993
|
+
function preservesNativeContent(before, after, spans) {
|
|
48994
|
+
try {
|
|
48995
|
+
assertZeroLengthFormatting(before, after, spans);
|
|
48996
|
+
} catch {
|
|
48997
|
+
return false;
|
|
48998
|
+
}
|
|
48999
|
+
const rangeStart = (start, length) => {
|
|
49000
|
+
const span = spans.find((s) => start >= s.before && start + length <= s.before + s.length);
|
|
49001
|
+
return span && span.after + start - span.before;
|
|
49002
|
+
};
|
|
49003
|
+
for (const object3 of before.objects) {
|
|
49004
|
+
const actual = after.objects.find((o) => o.id === object3.id);
|
|
49005
|
+
if (!actual || actual.type !== object3.type || actual.length !== object3.length || actual.start !== rangeStart(object3.start, object3.length))
|
|
49006
|
+
return false;
|
|
49007
|
+
}
|
|
49008
|
+
for (const object3 of before.objectData) {
|
|
49009
|
+
const actual = after.objectData.find((o) => o.id === object3.id);
|
|
49010
|
+
if (!actual || actual.pk !== object3.pk || actual.type !== object3.type || actual.mergeable !== object3.mergeable || actual.view !== object3.view || actual.altText !== object3.altText)
|
|
49011
|
+
return false;
|
|
49012
|
+
}
|
|
49013
|
+
for (const tag of before.nativeTags)
|
|
49014
|
+
if (JSON.stringify(before.nativeTagObjectIds[tag]) !== JSON.stringify(after.nativeTagObjectIds[tag]))
|
|
49015
|
+
return false;
|
|
49016
|
+
if (before.checklistItems.length !== after.checklistItems.length) return false;
|
|
49017
|
+
for (const [index, item] of before.checklistItems.entries()) {
|
|
49018
|
+
const actual = after.checklistItems[index];
|
|
49019
|
+
if (actual.id !== item.id || actual.text !== item.text || actual.done !== item.done || actual.start !== rangeStart(item.start, item.text.length))
|
|
49020
|
+
return false;
|
|
49021
|
+
}
|
|
49022
|
+
if (before.links.length !== after.links.length) return false;
|
|
49023
|
+
for (const [index, link] of before.links.entries()) {
|
|
49024
|
+
const actual = after.links[index];
|
|
49025
|
+
if (actual.text !== link.text || actual.url !== link.url || actual.length !== link.length || actual.start !== rangeStart(link.start, link.length))
|
|
49026
|
+
return false;
|
|
49027
|
+
}
|
|
49028
|
+
let oldIndex = 0, newIndex = 0;
|
|
49029
|
+
for (const span of spans) {
|
|
49030
|
+
let offset = 0;
|
|
49031
|
+
while (offset < span.length) {
|
|
49032
|
+
const oldPosition = span.before + offset, newPosition = span.after + offset;
|
|
49033
|
+
while (before.styleRuns[oldIndex].start + before.styleRuns[oldIndex].length <= oldPosition)
|
|
49034
|
+
oldIndex++;
|
|
49035
|
+
while (after.styleRuns[newIndex].start + after.styleRuns[newIndex].length <= newPosition)
|
|
49036
|
+
newIndex++;
|
|
49037
|
+
const oldRun = before.styleRuns[oldIndex], newRun = after.styleRuns[newIndex];
|
|
49038
|
+
if (oldRun.signature !== newRun.signature || oldRun.paragraphStyle !== newRun.paragraphStyle || oldRun.blockQuote !== newRun.blockQuote || oldRun.highlight !== newRun.highlight)
|
|
49039
|
+
return false;
|
|
49040
|
+
offset += Math.min(
|
|
49041
|
+
span.length - offset,
|
|
49042
|
+
oldRun.start + oldRun.length - oldPosition,
|
|
49043
|
+
newRun.start + newRun.length - newPosition
|
|
49044
|
+
);
|
|
49045
|
+
}
|
|
49046
|
+
}
|
|
49047
|
+
return true;
|
|
49048
|
+
}
|
|
49049
|
+
function nativeTagAdditionPreserved(before, after, tags) {
|
|
49050
|
+
requirePreservationMetadata(before);
|
|
49051
|
+
requirePreservationMetadata(after);
|
|
49052
|
+
const missing = tags.filter((tag) => !before.nativeTags.includes(tag));
|
|
49053
|
+
const allTags = [.../* @__PURE__ */ new Set([...before.nativeTags, ...tags])];
|
|
49054
|
+
const spans = preservedTextSpans(before, after, missing);
|
|
49055
|
+
return after.nativeTags.length === allTags.length && allTags.every((tag) => after.nativeTags.includes(tag)) && before.nativeObjectIds.every((id2) => after.nativeObjectIds.includes(id2)) && before.hasChecklist === after.hasChecklist && Boolean(
|
|
49056
|
+
spans && preservesAddedSemantics(before, after, spans) && preservesNativeContent(before, after, spans)
|
|
49057
|
+
);
|
|
49058
|
+
}
|
|
48652
49059
|
function addNativeTags(request, deps) {
|
|
48653
49060
|
if (!/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i.test(request.id))
|
|
48654
49061
|
throw new Error("An exact CoreData note ID is required");
|
|
@@ -48663,32 +49070,35 @@ function addNativeTags(request, deps) {
|
|
|
48663
49070
|
const missing = tags.filter((tag) => !before.rich.nativeTags.includes(tag));
|
|
48664
49071
|
if (!missing.length)
|
|
48665
49072
|
return { nativeTags: before.rich.nativeTags, added: [], contentHash: before.contentHash };
|
|
49073
|
+
requirePreservationMetadata(before.rich);
|
|
48666
49074
|
const candidates = deps.candidates(request.title, request.scopeText);
|
|
48667
49075
|
if (candidates.length !== 1 || candidates[0] !== request.id)
|
|
48668
49076
|
throw new Error(
|
|
48669
49077
|
"Shortcuts selection is ambiguous or points to a different note; nothing was changed"
|
|
48670
49078
|
);
|
|
48671
|
-
|
|
49079
|
+
const current = deps.read(request.id);
|
|
49080
|
+
if (current.contentHash !== request.expectedContentHash)
|
|
48672
49081
|
throw new Error("Note revision changed during preflight; nothing was changed");
|
|
49082
|
+
requirePreservationMetadata(current.rich);
|
|
48673
49083
|
let transportWarning;
|
|
48674
49084
|
try {
|
|
48675
49085
|
deps.run({ title: request.title, scopeText: request.scopeText, tags: missing });
|
|
48676
49086
|
} catch (error2) {
|
|
48677
49087
|
transportWarning = error2 instanceof Error ? error2.message : "Shortcuts completion was uncertain";
|
|
48678
49088
|
}
|
|
48679
|
-
const
|
|
48680
|
-
|
|
48681
|
-
|
|
48682
|
-
|
|
48683
|
-
|
|
48684
|
-
|
|
48685
|
-
|
|
48686
|
-
|
|
48687
|
-
}
|
|
48688
|
-
|
|
48689
|
-
|
|
48690
|
-
|
|
48691
|
-
);
|
|
49089
|
+
const verificationFailure = (detail) => new CodedError(
|
|
49090
|
+
(transportWarning ? "Shortcuts did not complete cleanly, and exact-ID tags/text/links/native-content readback was not verified." : "Shortcuts ran, but exact-ID tags/text/links/native-content readback was not verified.") + " Read the note before any retry." + (detail ? ` ${detail}` : "") + (transportWarning ? ` ${transportWarning}` : ""),
|
|
49091
|
+
{ code: "verification_failed", indeterminate: true }
|
|
49092
|
+
);
|
|
49093
|
+
let after;
|
|
49094
|
+
try {
|
|
49095
|
+
after = deps.read(request.id);
|
|
49096
|
+
requirePreservationMetadata(after.rich);
|
|
49097
|
+
} catch (error2) {
|
|
49098
|
+
throw verificationFailure(error2 instanceof Error ? error2.message : String(error2));
|
|
49099
|
+
}
|
|
49100
|
+
if (after.title !== before.title || !nativeTagAdditionPreserved(before.rich, after.rich, tags)) {
|
|
49101
|
+
throw verificationFailure();
|
|
48692
49102
|
}
|
|
48693
49103
|
return {
|
|
48694
49104
|
nativeTags: after.rich.nativeTags,
|
|
@@ -49127,53 +49537,25 @@ function backgroundDependencies(manager) {
|
|
|
49127
49537
|
function assertPreserved(before, after, options = {}) {
|
|
49128
49538
|
if (before.id !== after.id || before.title !== after.title)
|
|
49129
49539
|
throw new Error("Note identity changed");
|
|
49130
|
-
|
|
49131
|
-
|
|
49132
|
-
|
|
49133
|
-
|
|
49134
|
-
|
|
49135
|
-
|
|
49136
|
-
|
|
49137
|
-
|
|
49138
|
-
|
|
49139
|
-
|
|
49140
|
-
|
|
49141
|
-
|
|
49142
|
-
|
|
49143
|
-
|
|
49144
|
-
|
|
49145
|
-
if (JSON.stringify(before.checklist) !== JSON.stringify(
|
|
49146
|
-
options.append ? after.checklist.slice(0, before.checklist.length) : after.checklist
|
|
49147
|
-
))
|
|
49148
|
-
throw new Error("Existing checklist items changed");
|
|
49149
|
-
for (const item of before.rich.checklistItems || []) {
|
|
49150
|
-
const actual = after.rich.checklistItems?.find((current) => current.id === item.id);
|
|
49151
|
-
if (!actual || actual.text !== item.text || actual.done !== item.done)
|
|
49152
|
-
throw new Error("Existing checklist item identity or state changed");
|
|
49153
|
-
}
|
|
49154
|
-
if (before.rich.nativeTags.filter((t) => t !== options.tagChange).some((t) => !after.rich.nativeTags.includes(t)))
|
|
49155
|
-
throw new Error("Existing native tag was lost");
|
|
49156
|
-
const allowedRemoved = options.tagChange ? before.rich.nativeTagObjectIds?.[options.tagChange] || [] : [];
|
|
49157
|
-
for (const object3 of before.rich.objectData || []) {
|
|
49158
|
-
if (allowedRemoved.includes(object3.id)) continue;
|
|
49159
|
-
const actual = after.rich.objectData?.find((o) => o.id === object3.id);
|
|
49160
|
-
if (!actual || actual.mergeable !== object3.mergeable || actual.view !== object3.view)
|
|
49161
|
-
throw new Error("Existing native object content or presentation changed");
|
|
49162
|
-
}
|
|
49163
|
-
if (!options.tagChange && before.rich.styleRuns && after.rich.styleRuns) {
|
|
49164
|
-
const end = before.rich.text.trimEnd().length;
|
|
49165
|
-
for (const oldRun of before.rich.styleRuns)
|
|
49166
|
-
for (const newRun of after.rich.styleRuns) {
|
|
49167
|
-
const overlapStart = Math.max(oldRun.start, newRun.start);
|
|
49168
|
-
const overlapEnd = Math.min(
|
|
49169
|
-
end,
|
|
49170
|
-
oldRun.start + oldRun.length,
|
|
49171
|
-
newRun.start + newRun.length
|
|
49172
|
-
);
|
|
49173
|
-
if (overlapStart < overlapEnd && /[^\s\ufffc]/u.test(before.rich.text.slice(overlapStart, overlapEnd)) && oldRun.signature !== newRun.signature)
|
|
49174
|
-
throw new Error("Existing rich formatting changed");
|
|
49175
|
-
}
|
|
49540
|
+
if (options.tagChange && !options.removeTag) {
|
|
49541
|
+
if (!nativeTagAdditionPreserved(before.rich, after.rich, [options.tagChange]))
|
|
49542
|
+
throw new Error("Native tag addition or retained content was not verified");
|
|
49543
|
+
} else {
|
|
49544
|
+
try {
|
|
49545
|
+
assertRetainedRichContent(
|
|
49546
|
+
before.rich,
|
|
49547
|
+
after.rich,
|
|
49548
|
+
options.tagChange ? { kind: "remove-tag", tag: options.tagChange } : options.append ? { kind: "append" } : { kind: "unchanged" }
|
|
49549
|
+
);
|
|
49550
|
+
} catch (error2) {
|
|
49551
|
+
if (error2 instanceof Error && error2.message === "Existing note text or whitespace was not preserved")
|
|
49552
|
+
throw new Error("Existing note text was not preserved", { cause: error2 });
|
|
49553
|
+
throw error2;
|
|
49554
|
+
}
|
|
49176
49555
|
}
|
|
49556
|
+
for (const snapshot of [before, after])
|
|
49557
|
+
if (JSON.stringify(snapshot.checklist) !== JSON.stringify(snapshot.rich.checklistItems.map(({ text: text2, done }) => ({ text: text2, done }))))
|
|
49558
|
+
throw new Error("Native checklist readers disagree about item text or state");
|
|
49177
49559
|
}
|
|
49178
49560
|
function describeTransportFailure(error2) {
|
|
49179
49561
|
const detail = error2;
|
|
@@ -49187,12 +49569,37 @@ function textBelowTitle(text2, title) {
|
|
|
49187
49569
|
if (firstLine.trim() !== title.trim()) return text2;
|
|
49188
49570
|
return lineEnd < 0 ? "" : text2.slice(lineEnd + 1);
|
|
49189
49571
|
}
|
|
49572
|
+
function assertChecklistEndAppendAvailable(rich) {
|
|
49573
|
+
requirePreservationMetadata(rich);
|
|
49574
|
+
const items = [...rich.checklistItems].sort((a, b) => a.start - b.start);
|
|
49575
|
+
if (!items.length) return;
|
|
49576
|
+
const last = items.at(-1);
|
|
49577
|
+
if (items.some(
|
|
49578
|
+
(item, index) => item.start > 0 && rich.text[item.start - 1] !== "\n" || index > 0 && item.start !== items[index - 1].start + items[index - 1].text.length + 1
|
|
49579
|
+
) || !/^\n?$/u.test(rich.text.slice(last.start + last.text.length)))
|
|
49580
|
+
throw new CodedError(
|
|
49581
|
+
"Checklist end-append requires one existing checklist group at the end of the note. Notes may insert into an earlier group when text or native objects follow it, or when groups are separate. Nothing changed",
|
|
49582
|
+
{ code: "validation_error", committed: false, indeterminate: false }
|
|
49583
|
+
);
|
|
49584
|
+
}
|
|
49585
|
+
function requireBackgroundPreflightMetadata(rich) {
|
|
49586
|
+
try {
|
|
49587
|
+
requirePreservationMetadata(rich);
|
|
49588
|
+
} catch (error2) {
|
|
49589
|
+
throw new CodedError(error2 instanceof Error ? error2.message : String(error2), {
|
|
49590
|
+
code: "validation_error",
|
|
49591
|
+
committed: false,
|
|
49592
|
+
indeterminate: false
|
|
49593
|
+
});
|
|
49594
|
+
}
|
|
49595
|
+
}
|
|
49190
49596
|
function mutateBackground(request, operation, data, verify, deps) {
|
|
49191
49597
|
if (request.scopeText.length < 12 || request.scopeText.length > 500 || /[\r\n\0]/u.test(request.scopeText))
|
|
49192
49598
|
throw new Error("Use a distinctive existing single-line scope of 12\u2013500 characters");
|
|
49193
49599
|
const before = deps.read(request.id);
|
|
49194
49600
|
if (before.hash !== request.expectedContentHash)
|
|
49195
49601
|
throw new Error("Note revision changed; read it again");
|
|
49602
|
+
requireBackgroundPreflightMetadata(before.rich);
|
|
49196
49603
|
if (!before.rich.text.includes(request.scopeText))
|
|
49197
49604
|
throw new Error("Scope is absent from exact note");
|
|
49198
49605
|
if (!textBelowTitle(before.rich.text, before.title).includes(request.scopeText))
|
|
@@ -49203,8 +49610,10 @@ function mutateBackground(request, operation, data, verify, deps) {
|
|
|
49203
49610
|
const candidates = deps.candidates(before.title, request.scopeText);
|
|
49204
49611
|
if (candidates.length !== 1 || candidates[0] !== request.id)
|
|
49205
49612
|
throw new Error("Ambiguous note selection; nothing changed");
|
|
49206
|
-
|
|
49207
|
-
|
|
49613
|
+
const current = deps.read(request.id);
|
|
49614
|
+
if (current.hash !== before.hash) throw new Error("Note revision changed during preflight");
|
|
49615
|
+
requireBackgroundPreflightMetadata(current.rich);
|
|
49616
|
+
if (operation === "create-checklist-item") assertChecklistEndAppendAvailable(current.rich);
|
|
49208
49617
|
let transportUncertain = false;
|
|
49209
49618
|
let transportMessage = "";
|
|
49210
49619
|
let transportError;
|
|
@@ -49308,7 +49717,7 @@ function setNativeTag(manager, request) {
|
|
|
49308
49717
|
request.present ? "add-tag" : "remove-tag",
|
|
49309
49718
|
{ tag },
|
|
49310
49719
|
(before, after) => {
|
|
49311
|
-
assertPreserved(before, after, { tagChange: tag });
|
|
49720
|
+
assertPreserved(before, after, { tagChange: tag, removeTag: !request.present });
|
|
49312
49721
|
if (after.rich.nativeTags.includes(tag) !== request.present)
|
|
49313
49722
|
throw new Error("Native tag state not verified");
|
|
49314
49723
|
const removed = before.rich.nativeObjectIds.filter(
|
|
@@ -55247,30 +55656,13 @@ function readSnapshot(manager, id2) {
|
|
|
55247
55656
|
const rich = readRichNote(id2);
|
|
55248
55657
|
if (enriched.revision !== rich.revision)
|
|
55249
55658
|
throw new Error("Native metadata changed during read; read the note again");
|
|
55659
|
+
requirePreservationMetadata(rich);
|
|
55250
55660
|
return { id: id2, title: note.title, body, rich, hash: richContentHash(body, enriched) };
|
|
55251
55661
|
}
|
|
55252
55662
|
function assertExistingContentPreserved(before, after) {
|
|
55253
55663
|
if (before.id !== after.id || before.title !== after.title)
|
|
55254
55664
|
throw new Error("Note identity changed");
|
|
55255
|
-
|
|
55256
|
-
if (!tidy(after.rich.text).startsWith(tidy(before.rich.text)))
|
|
55257
|
-
throw new Error("Existing note text was not preserved");
|
|
55258
|
-
if (linkSignature(before.rich.links) !== linkSignature(after.rich.links.slice(0, before.rich.links.length)))
|
|
55259
|
-
throw new Error("Existing links were not preserved");
|
|
55260
|
-
if (before.rich.nativeObjectIds.some((id2) => !after.rich.nativeObjectIds.includes(id2)))
|
|
55261
|
-
throw new Error("Existing native object was lost");
|
|
55262
|
-
if (before.rich.nativeTags.some((tag) => !after.rich.nativeTags.includes(tag)))
|
|
55263
|
-
throw new Error("Existing native tag was lost");
|
|
55264
|
-
for (const item of before.rich.checklistItems || []) {
|
|
55265
|
-
const actual = after.rich.checklistItems?.find((current) => current.id === item.id);
|
|
55266
|
-
if (!actual || actual.text !== item.text || actual.done !== item.done)
|
|
55267
|
-
throw new Error("Existing checklist item identity or state changed");
|
|
55268
|
-
}
|
|
55269
|
-
for (const object3 of before.rich.objectData || []) {
|
|
55270
|
-
const actual = after.rich.objectData?.find((current) => current.id === object3.id);
|
|
55271
|
-
if (!actual || actual.mergeable !== object3.mergeable || actual.view !== object3.view)
|
|
55272
|
-
throw new Error("Existing native object content or presentation changed");
|
|
55273
|
-
}
|
|
55665
|
+
assertRetainedRichContent(before.rich, after.rich, { kind: "append" });
|
|
55274
55666
|
}
|
|
55275
55667
|
function localAttachment(path10) {
|
|
55276
55668
|
if (!isAbsolute4(path10)) throw new Error("An absolute local file path is required");
|
|
@@ -55647,8 +56039,19 @@ function attachFile(manager, args, checked, progress = { insertionStarted: false
|
|
|
55647
56039
|
transportWarning: "Transport was uncertain; exact bytes and prior content were verified"
|
|
55648
56040
|
} : {}
|
|
55649
56041
|
};
|
|
56042
|
+
} catch (error2) {
|
|
56043
|
+
if (progress.insertionStarted)
|
|
56044
|
+
throw new CodedError(
|
|
56045
|
+
`Attachment outcome uncertain; read the exact note before any retry: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
56046
|
+
{ code: "verification_failed", indeterminate: true }
|
|
56047
|
+
);
|
|
56048
|
+
throw error2;
|
|
55650
56049
|
} finally {
|
|
55651
|
-
|
|
56050
|
+
try {
|
|
56051
|
+
rmSync7(directory, { recursive: true, force: true });
|
|
56052
|
+
} catch {
|
|
56053
|
+
console.error("Could not remove private temporary attachment files");
|
|
56054
|
+
}
|
|
55652
56055
|
}
|
|
55653
56056
|
}
|
|
55654
56057
|
|
|
@@ -58520,7 +58923,7 @@ function registerSvgAnalysis(server2) {
|
|
|
58520
58923
|
// src/tools/nativeTagsBridge.ts
|
|
58521
58924
|
var noteId2 = exactIdInput(
|
|
58522
58925
|
"ICNote",
|
|
58523
|
-
/^x-coredata:\/\/[0-9a-
|
|
58926
|
+
/^x-coredata:\/\/[0-9a-fA-F-]+\/ICNote\/p\d+$/i,
|
|
58524
58927
|
NOTE_ID_MESSAGE
|
|
58525
58928
|
);
|
|
58526
58929
|
var revision2 = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
@@ -58554,8 +58957,9 @@ function registerNativeTagsBridge(server2, manager) {
|
|
|
58554
58957
|
if (!body) throw new Error("Note content is unavailable");
|
|
58555
58958
|
const enriched = enrichNoteRead(id2, body);
|
|
58556
58959
|
const rich = readRichNote(id2);
|
|
58557
|
-
if (
|
|
58960
|
+
if (enriched.revision !== rich.revision)
|
|
58558
58961
|
throw new Error("Native metadata changed during read; read the note again");
|
|
58962
|
+
restoreNoteLinks(body, rich);
|
|
58559
58963
|
return {
|
|
58560
58964
|
contentHash: richContentHash(body, enriched),
|
|
58561
58965
|
title: note.title,
|
|
@@ -59731,7 +60135,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
59731
60135
|
);
|
|
59732
60136
|
tool(
|
|
59733
60137
|
"create-checklist-item",
|
|
59734
|
-
"Use when: appending one real unchecked Notes checklist item.\nReturns: the created checklist identity after exact-note readback.\nDo not use when: plain text is sufficient or more than one item is requested.\nSafety: preserves existing text, links, native objects, checklist identities, and formatting.",
|
|
60138
|
+
"Use when: appending one real unchecked Notes checklist item.\nReturns: the created checklist identity after exact-note readback.\nDo not use when: plain text is sufficient or more than one item is requested.\nSafety: preserves existing text, links, native objects, checklist identities, and formatting. If a checklist already exists, requires one contiguous group at the end of the note; otherwise refuses before running the bridge.",
|
|
59735
60139
|
{
|
|
59736
60140
|
...common,
|
|
59737
60141
|
text: external_exports.string().min(1).max(1e4).refine((s) => !/[\r\n\0]/u.test(s), "One line per checklist item")
|
|
@@ -59755,7 +60159,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
59755
60159
|
);
|
|
59756
60160
|
tool(
|
|
59757
60161
|
"create-checklist-items",
|
|
59758
|
-
"Use when: appending several real unchecked Notes checklist items to one note, in order.\nReturns: each landed item's native identity and text, the final order check, and the new revision; on a stop, which items landed, the item whose outcome is uncertain, and the items not attempted.\nDo not use when: one item is enough (create-checklist-item) or plain text is sufficient.\nSafety: runs the verified single-item bridge once per item, chaining each verified revision into the next;
|
|
60162
|
+
"Use when: appending several real unchecked Notes checklist items to one note, in order.\nReturns: each landed item's native identity and text, the final order check, and the new revision; on a stop, which items landed, the item whose outcome is uncertain, and the items not attempted.\nDo not use when: one item is enough (create-checklist-item) or plain text is sufficient.\nSafety: runs the verified single-item bridge once per item, chaining each verified revision into the next; requires any existing checklist to be one contiguous group at the end of the note before dispatch. Checks after every item that exactly one new unchecked item with that text and a new identity appeared and that earlier items kept theirs, and stops at the first uncertain result without retrying. Each item takes a few seconds, so a full batch can run about a minute; if the call times out on the client side, read the note before retrying and send only items that are not present.",
|
|
59759
60163
|
{
|
|
59760
60164
|
...common,
|
|
59761
60165
|
items: external_exports.array(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apple-notes-mcp",
|
|
3
|
-
"version": "2.15.
|
|
3
|
+
"version": "2.15.5",
|
|
4
4
|
"description": "MCP server for Apple Notes - read, search, create, edit, organize, and export notes on macOS via Claude and other AI assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|