التخزين البعيد
إن إرسال حالة المحرر كاملة مع كل تغيير أمر غير فعّال.
ولهذه الحالة، يوفر المحرر واجهة برمجة تطبيقات للنسخ المتماثل للبيانات في الوقت الفعلي.
التثبيت
npm install @dineug/erd-editor
يوجد مخزن النسخ المتماثل في المسار الفرعي @dineug/erd-editor/engine.js، لا في جذر الحزمة.
لا تمس نقطة الدخول هذه DOM إطلاقًا، لذا تعمل في Web Worker أو في أي مكان آخر خارج الخيط الرئيسي.
أما استيراد جذر الحزمة بدلًا منها فيحتاج إلى مستند DOM: فمنذ الإصدار 5.0.0 لا يعرّف أي عنصر، لكن تقييمه يبني فئتَي العنصرين. وقبل 5.0.0 كان يسجّل العنصر المخصص <erd-editor>.
الاستخدام
type ReplicationStore = {
readonly value: string;
readonly runtimeValue: string;
on: (
listeners: Partial<{ change: (change: ReplicationChange) => void }>
) => Unsubscribe;
setInitialValue: (value: string) => void;
dispatch: (actions: Array<AnyAction> | AnyAction) => void;
dispatchSync: (actions: Array<AnyAction> | AnyAction) => void;
destroy: () => void;
};
type ReplicationChange = {
value: string;
changed: boolean;
};
type InjectEngineContext = {
toWidth: (text: string) => number;
};
type CreateReplicationStore = (
context: InjectEngineContext
) => ReplicationStore;
// example
import { createReplicationStore } from '@dineug/erd-editor/engine.js';
const replicationStore = createReplicationStore({
toWidth: text => text.length * 10,
});
النسخ المتماثل لمحرر قيد التشغيل
تأتي الإجراءات من المخزن المشترك لمحرر قيد التشغيل.
راجع التحرير التعاوني.
import { createReplicationStore } from '@dineug/erd-editor/engine.js';
const replicationStore = createReplicationStore({ toWidth });
replicationStore.setInitialValue(editor.runtimeValue);
replicationStore.on({
change: ({ value, changed }) => {
if (changed) save(value);
},
});
const sharedStore = editor.getSharedStore();
sharedStore.subscribe(actions => {
replicationStore.dispatch(actions);
});
لا يعبر الحد الفاصل إلا التغيير، ولا يعبره المستند كاملًا أبدًا.
تطبّقه النسخة المتماثلة وتحوّل الناتج إلى سلسلة من جهتها.
يسلّمك الحدث change هذا الناتج في value، ويخبرك الحقل changed متى يستحق أن يُكتب.
منذ الإصدار 4.0.0، هيّئ النسخة المتماثلة من runtimeValue للمحرر كما سبق، لا من الملف المخزّن ولا من value للمحرر.
فالملف يفتقر إلى التعديلات التي أُجريت منذ حفظه، وvalue يفتقر إلى الكيانات التي أُزيلت خلال الجلسة.
والنسخة المتماثلة التي هُيّئت دون جدول مُزال تعيده بلا اسمه ولا أعمدته حين يتراجع محرر عن إزالته، وتحفظه على هذه الحال.
منذ الإصدار 4.2.0، ينطبق الأمر نفسه على الكلمة أو المصطلح أو المجال المُزال من إحدى أوراق القاموس.
فحين يتراجع محرر عن الإزالة، تُدرج النسخة المتماثلة التي هُيّئت دون الصف هذا الصفَّ من جديد فارغًا، ولأن value يستبعد الصف الفارغ، تحفظ النسخة المتماثلة المستند من دونه.
وفي حالة المجال، تبقى أيضًا الأعمدة والمصطلحات التي كانت مرتبطة به غير مرتبطة بأي مجال في تلك النسخة المتماثلة، لأنها لم تحمل تلك الروابط قط ولا يرسلها التراجع.
منذ الإصدار 5.1.0، يستطيع @dineug/erd-editor-yjs أيضًا إبقاء المحررات متزامنة عبر غرفة Yjs.
عندما تُغذَّى النسخة المتماثلة كما سبق من عنصر مربوط بغرفة، فإنها لا تتلقى إلا التعديلات التي أُجريت على ذلك العنصر، ولا تتلقى أبدًا ما تجلبه الغرفة: فهذه تصل إلى العنصر على أنها تعديلات محرر آخر، ولا يمرّر المخزن المشترك أبدًا ما تلقاه.
لتخزين مستند غرفة، احفظ value للعنصر المربوط، أو لمخزن نظير مربوط، كما يوضح ما تحتويه الغرفة.
يخزّن مزوّد التخزين الدائم لـ Yjs الغرفة نفسها، التي تحمل قيمة وقت التشغيل والتعديلات التي أُجريت بعدها لا ملفًا؛ راجع الحدود.
InjectEngineContext
toWidth
تقيس سلسلة نصية بالبكسل.
ليس للمخزن DOM، لذا لا يستطيع قياس النص بنفسه، ويعيد حساب عرض الأعمدة بهذه الدالة في كل مرة يتغير فيها اسم أو نوع بيانات أو قيمة افتراضية أو تعليق.
منذ الإصدار 4.2.0، يشمل ذلك الاسم المنطقي للجدول أو العمود.
ويقيس خلية المجال في العمود، التي تعرض اسم المجال المرتبط به العمود، كلما رُبط العمود بمجال أو فُك ربطه بالمجال، وكلما أُضيف ذلك المجال أو أُعيدت تسميته أو أُزيل.
ويعيد أيضًا قياس كل جدول وعمود كلما حمّلت الدالة setInitialValue مستندًا.
منذ الإصدار 4.0.0، لا يُحتفظ بقيم العرض إلا في runtimeValue، ولا تُحفظ في value أبدًا.
لذا يعطي المستند الذي حفظه هذا الإصدار عند قراءته مجددًا value نفسه بايتًا ببايت، أينما حُفظ وكيفما قيس النص هناك.
قِس كما يقيس المحرر — 400 12px بقائمة خطوط المحرر، مع التقريب، وإضافة بكسلين من الحشو.
وإلا ابتعدت قيم العرض في runtimeValue عن تلك الظاهرة على الشاشة.
const TEXT_PADDING = 2;
const FONT =
"400 12px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, " +
"'Helvetica Neue', 'Open Sans', system-ui, sans-serif, " +
"'Apple Color Emoji', 'Segoe UI Emoji'";
let context = null;
function getContext() {
if (context) return context;
try {
context = new OffscreenCanvas(0, 0).getContext('2d');
if (context) context.font = FONT;
} catch {
// no canvas available
}
return context;
}
function toWidth(text) {
const context = getContext();
const width = context ? context.measureText(text).width : text.length * 10;
return Math.round(width) + TEXT_PADDING;
}
const replicationStore = createReplicationStore({ toWidth });
التعبير text.length * 10 هو البديل الاحتياطي حين لا يتوفر canvas.
وهو يكفي لمخزن يُخزَّن مستنده فقط ولا يُعرض أبدًا.
ReplicationStore
value getter
يحوّل حالة المحرر الحالية إلى سلسلة.
ويُرجع سلسلة نصية بصيغة JSON جاهزة للتخزين كما هي — لا كائنًا محلَّلًا.
const data = replicationStore.value;
منذ الإصدار 4.0.0، يمثل value صيغة التخزين: المستند كما يحمله الملف، وهو النص نفسه الذي تُرجعه الخاصية value للعنصر.
ولا يحمل سوى الكيانات القائمة، بترتيب ثابت، دون الكيانات المُزالة ولا ترتيب التراص ولا أي شيء يعيد التحميل حسابه، مثل قيم العرض التي تقيسها الدالة toWidth.
منذ الإصدار 4.2.0، يحمل أيضًا الأسماء المنطقية، وما إذا كان كل اسم مشتقًا من القواميس، وروابط المجالات، والكلمات والمصطلحات والمجالات في علامة تبويب القاموس.
ومنذ الإصدار 4.2.0 يستبعد أيضًا كل حقل نصي فارغ في كيان، وكل قائمة في doc وكل مجموعة كيانات لا تحمل شيئًا، وcollections نفسه حين تكون كل مجموعات الكيانات فارغة.
ويُكتب بإزاحة مسافتين، مع وضع كل صف من صفوف القواميس في سطر خاص به، وينتهي بسطر جديد، لذا يعطي المستند نفسه دائمًا البايتات نفسها.
لمعرفة ما يحمله، راجع صيغة التخزين.
يُكتب كل إعداد مفعّل عليه القفل بالقيمة التي قُفل عليها، وكل إعداد آخر بقيمته الحالية.
يشمل القفل Viewport أصل العرض ومستوى التكبير معًا، ولكل من علامة التبويب ولغة منشئ التعليمات البرمجية ونمطَي التسمية كليهما ونوع علامات الاقتباس قفله الخاص.
ومنذ الإصدار 4.2.0 يملك عرض الأسماء قفله الخاص أيضًا، وهو القفل Name View المفعّل في المستند الجديد.
تحل الأقفال محل الحقل ignoreSaveSettings في المستند، وهو حقل لم يعد يُقرأ ولا يُكتب منذ الإصدار 4.0.0.
قبل الإصدار 4.0.0، كان value هو المستند كله كما يحمله المخزن، مع تطبيق بتّات التمرير والتكبير في ذلك الحقل.
runtimeValue getter
منذ الإصدار 4.0.0، يحوّل المستند إلى سلسلة كما تحمله النسخة المتماثلة، بما في ذلك الكيانات المُزالة وكل ما يعيد التحميل حسابه.
ويُكتب هنا أيضًا كل إعداد مقفل بقيمته المقفلة.
const snapshot = replicationStore.runtimeValue;
استخدمه لتهيئة محرر أو مخزن آخر ينضم لاحقًا، عبر الدالة setInitialValue الخاصة به؛ راجع التحرير التعاوني.
ليس للنسخة المتماثلة getLWW ولا getVersion، لذا لا تسلّم مسجّلات تعاون مع runtimeValue: فالمحرر الذي يُهيَّأ منها يأخذها من المحررات العاملة، كما يوضح تهيئة متعاون.
لا تخزّنه أبدًا على أنه الملف: فهو يحمل الكيانات المُزالة، ويرفضه JSON Schema المنشور.
وهذه الكيانات المُزالة هي ما يتيح لتراجع عن إزالة، يُرسَل لاحقًا، أن يعيد الكيان كاملًا في المخزن الذي هُيّئ منه.
وتبقى إلى أن يُفتح المستند في المرة التالية من ملفه، الذي تستبعدها صيغته.
setInitialValue
تحمّل حالة المحرر المخزّنة سابقًا.
replicationStore.setInitialValue('json...');
تقبل صيغة التخزين أو قيمة وقت تشغيل أو مستندًا حفظه إصدار سابق، بما في ذلك مستند v2.
وتحمّل السلسلة الفارغة، أو أي شيء ليس سلسلة نصية، مستندًا جديدًا فارغًا بدلًا من إثارة خطأ.
ويبدأ المخزن الذي لم يتلقَّ قيمة قط بالمستند الجديد نفسه.
منذ الإصدار 4.0.0، تكون جميع الأقفال مفعّلة في ذلك المستند، لذا يحتفظ value الخاص به بالعرض وعلامة التبويب ولغة منشئ التعليمات البرمجية وأنماط التسمية ونوع علامات الاقتباس التي بدأ بها، بينما تمرّر المحررات التي ينسخها العرض أو تغيّر مستوى التكبير أو تبدّل علامات التبويب أو تعدّل تلك الإعدادات. في الإصدار 3.10.0 لم يكن يحفظ التمرير ولا التكبير، وقبل ذلك كان يحفظهما كليهما.
ومنذ الإصدار 4.2.0، يحتفظ أيضًا بعرض الأسماء الخاص به، وهو العرض Physical Names، بينما تبدّل المحررات عروضها.
ويحتفظ المستند المخزّن بالأقفال التي يذكرها.
أما المستند الذي لا يذكر أيًّا منها، وهو أي مستند حُفظ قبل الإصدار 4.0.0، بما في ذلك مستند v2، فيُحمَّل وجميع الأقفال مفعّلة: أصل العرض عند 0, 0 ومستوى التكبير عند 1 وعلامة التبويب على ERD، أيًّا كان ما حفظه، ولغة منشئ التعليمات البرمجية وأنماط التسمية ونوع علامات الاقتباس كما حفظها.
ولا يذكر المستند المحفوظ قبل الإصدار 4.2.0 القفل Name View، لذا يُحمَّل وذلك القفل مفعّل وعلى العرض Physical Names، حتى لو حُفظ وكل الأقفال الأخرى معطّلة.
منذ الإصدار 4.0.0، تتخلص الدالة setInitialValue أيضًا من مسجّلات التعاون المحفوظة للمستند السابق، كما تفعل الدالة setInitialValue للعنصر.
ولا يثير النص الذي ليس JSON، مثل ملف بقيت فيه علامات تعارض الدمج، خطأً هو الآخر: إذ يُسجَّل خطأ التحليل في وحدة التحكم فحسب، ويُفرَّغ المستند، وتبقى الإعدادات على حالها.
منذ الإصدار 4.0.0، لا يُجري التحميل أي جمع للبيانات المهملة، لذا تبقى الكيانات المُزالة في runtimeValue.
وقبل ذلك، كان يتخلص من الكيانات التي لم تعد تظهر في doc ولم تُلمس منذ أربعة أيام أو أكثر.
تكتمل عمليات إعادة الكتابة التي يجريها التحميل قبل أن تعود الدالة setInitialValue: علامات المفاتيح على الأعمدة، مقروءةً من خيارات كل عمود ومن العلاقات؛ وعرض كل جدول وعمود، مُقاسًا من جديد بالدالة toWidth؛ والنقاط التي يلتقي فيها كل خط ربط بالجداول التي يربطها؛ وعلامة كون كل علاقة مُعرِّفة، والرمز عند طرف الجدول الأصل منها.
ليس أيٌّ منها جزءًا من value، لكن replicationStore.runtimeValue، إذا قُرئ مباشرة بعد الاستدعاء، يحملها كلها بالفعل.
أما الإجراء الذي يصل خلف التحميل مباشرة، مثل تمرير يُعاد تشغيله، فيُقارَن بالمستند المحمَّل، لذا لا يُبلَّغ أبدًا عن التحميل نفسه على أنه changed.
لا يُطلق التحميل ولا أيٌّ من عمليات إعادة الكتابة هذه الحدث change.
اقرأ replicationStore.value بنفسك بعد أن تعود الدالة setInitialValue إذا أردت أن يُكتب المستند المحمَّل من جديد بصيغة التخزين.
on@change
تشترك في التغييرات التي تطرأ على حالة المحرر.
const unsubscribe = replicationStore.on({
change: ({ value, changed }) => {
if (changed) {
// save value...
}
},
});
يُؤخَّر إطلاق الحدث change بمقدار 200 مللي ثانية، لذا تصل الإجراءات المتلاحقة في إشعار واحد.
ولا يُطلَق إلا لإجراءات من نوع يمكنه تغيير المستند، إضافة إلى settings.changeIgnoreSaveSettings الذي قد لا يزال يرسله محرر أقدم (راجع dispatch, dispatchSync).
ولا يُطلقه أبدًا إجراء حضور مُمرَّر ولا editor.mergeLWW.
استدعِ الدالة المُرجَعة لإلغاء الاشتراك.
منذ الإصدار 3.10.0، يتلقى المستمع ReplicationChange، وهو نوع يصدّره @dineug/erd-editor/engine.js إلى جانب ReplicationStore.
قبل الإصدار 3.10.0 لم يكن يتلقى شيئًا، والمستمع الذي يتجاهل الوسيطة يظل يعمل.
الحقل value هو المستند محوَّلًا إلى سلسلة بصيغة التخزين، وهو السلسلة نفسها التي يُرجعها replicationStore.value في تلك اللحظة، فلا حاجة إلى تحويله إلى سلسلة مرة أخرى.
وتكون قيمة changed هي false عندما تترك الإجراءات التي وقعت منذ change السابق، أو منذ التحميل، value على حاله بايتًا ببايت.
والتغيير الذي يستبعده القفل من value يظل يُطلق change، مع تعيين changed إلى false، لذا لا تكتب إلا عندما تكون قيمة changed هي true.
ويعني ذلك التمرير أو التكبير أثناء قفل Viewport، وتبديل علامة التبويب أثناء قفل Canvas Type، وتغيير لغة منشئ التعليمات البرمجية أو نمط تسمية أو نوع علامات الاقتباس أثناء تفعيل قفله الخاص، وهو تغيير لم يعد يرسله، منذ الإصدار 5.1.0، إلا محرر أقدم من الإصدار 5.1.0 (راجع dispatch, dispatchSync).
ومنذ الإصدار 4.2.0 يشمل ذلك أيضًا تبديل عرض الأسماء أثناء قفل Name View: فالمحرر يظل يرسله، لكن النسخة المتماثلة تُسقطه، كما تفعل المحررات الأخرى.
ويُسقَط بالطريقة نفسها تبديل عرض الأسماء الذي يُجرى في محرر للقراءة فقط، أيًّا كانت حالة قفله.
يستبعد الحدث change للعنصر مثل هذا التغيير، لكن النسخة المتماثلة تظل تبلّغ عنه.
تكون جميع الأقفال مفعّلة في المستند الجديد وفي المستند المحفوظ قبل الإصدار 4.0.0، ويكون Name View مقفلًا في أي مستند حُفظ قبل الإصدار 4.2.0.
وعند إلغاء قفل Viewport يغيّر التمرير أو التكبير value، وعند إلغاء قفل Canvas Type يغيّره كذلك فتح علامة تبويب أخرى من علامات تبويب المحرر، وعند إلغاء قفل Name View يغيّره كذلك تبديل عرض الأسماء من محرر ليس للقراءة فقط.
ويغيّر تفعيل قفل أو تعطيله value أيضًا.
منذ الإصدار 4.0.0، يقارن changed صيغة التخزين، التي لا تحمل الكيانات المُزالة ولا ترتيب التراص.
لذا فإن الجدول الذي يُضاف ثم يُزال مجددًا ضمن change واحد، والتعديل الذي يقع على كيان أزاله متعاون آخر، والتحميل المُمرَّر الذي لا يغيّر سوى ترتيب التراص، كلها تنتهي أيضًا بتعيين changed إلى false.
ومنذ الإصدار 4.2.0 ينطبق ذلك أيضًا على الصف الذي يُضاف إلى إحدى أوراق القاموس ويُترك فارغًا، لأن صيغة التخزين تستبعد الصف الذي لا يحمل شيئًا.
لا يقارن changed أبدًا بملفك المخزّن، الذي قد يختلف بالفعل عن value بمجرد تحميله: فالملف الذي حفظه إصدار سابق يختلف، بما في ذلك ملف v2، وكذلك الملف الذي يبدأ بعلامة ترتيب البايتات والملف المُعدَّل يدويًا.
ومنذ الإصدار 4.2.0 يشمل ذلك الملف الذي حفظه الإصدار 4.0.0 أو 4.1.0، لأن value يستبعد الحقول النصية الفارغة في كياناته وقوائم doc ومجموعات الكيانات الفارغة التي يحملها مثل هذا الملف.
ومقارنة البايتات بمثل هذا الملف ستعدّ حتى التمرير الذي يستبعده القفل تعديلًا.
وأول change تكون فيه قيمة changed هي true يعيد مثل هذا الملف بصيغة التخزين، لذا فإن كتابته تعيد كتابة الملف مرة واحدة.
أما الملف الذي حفظه هذا الإصدار فلا يختلف، أينما حُفظ، كما يوضح القسم toWidth.
وإذا نُفّذت الدالة setInitialValue بينما لا يزال change معلّقًا، ولم يُرسَل شيء قبل موعده، فإن ذلك change يصل على أي حال، حاملًا value المحمَّل وchanged بالقيمة false.
dispatch, dispatchSync
تطبّق تغييرات المحرر البعيد على replicationStore.
replicationStore.dispatch(actions); // async
replicationStore.dispatchSync(actions); // sync
تؤجّل الدالة dispatch التطبيق إلى مهمة دقيقة، بينما تطبّق الدالة dispatchSync الدفعة فورًا.
يمكنك تسليم دفق المخزن المشترك كما هو دون تصفية.
لا تحتفظ الدالتان إلا بالإجراءات من نوع يمكنه تغيير المستند، إضافة إلى الإجراءين الموصوفين في نهاية هذا القسم، وتُسقطان الباقي، لذا تمر بيانات الحضور التي تحملها الجلسة التعاونية — مؤشر الماوس لكل مستخدم وتركيزه وتحديده ومربع السحب لديه — دون أي أثر.
منذ الإصدار 3.10.0، تشمل الإجراءات المحتفظ بها relationship.changeOnDelete وrelationship.changeOnUpdate، ويحمل relationship.add الحقلين onDelete وonUpdate.
والمخزن من إصدار سابق يُسقط تلك الإجراءات ويتجاهل تلك الحقول، فيفقد إجراءات ON DELETE وON UPDATE للعلاقة.
منذ الإصدار 4.0.0، تشمل الإجراءات المحتفظ بها أيضًا table.changeGroup، وسبعة من إجراءات tableGroup الثمانية (tableGroup.add وtableGroup.move وtableGroup.moveTo وtableGroup.remove وtableGroup.resize وtableGroup.changeName وtableGroup.changeColor، أي كلها عدا تغيير ترتيب التراص tableGroup.changeZIndex)، وrelationship.changeColumns وsettings.changeLockSettings وsettings.changeDDLScript.
لم يعد settings.changeRelationshipOptimization موجودًا، لذا تُسقطه الدالتان كلتاهما.
والمخزن من إصدار سابق يُسقط الإجراءات الجديدة، فيفقد مجموعات الجداول والأقفال وسكربتات مخطط SQL والأعمدة التي يمنحها تعيين الأعمدة لعلاقة موجودة.
شغّل النسخة المتماثلة على الإصدار نفسه الذي تعمل عليه المحررات، أو على إصدار أحدث.
لا يضيف الإصدار 4.1.0 أي نوع إجراء، ويترك @dineug/erd-editor/engine.js كما كان، لذا تحتفظ نسخة متماثلة من الإصدار 4.0.0 بكل تغيير يرسله محرر من الإصدار 4.1.0، تمامًا كما تفعل نسخة متماثلة من الإصدار 4.1.0.
منذ الإصدار 4.1.0، يعيد Replace وReplace All في البحث والاستبدال تسمية الفهارس ومجموعات الجداول أيضًا، عبر الإجراءين index.changeName وtableGroup.changeName اللذين تحتفظ بهما نسخة متماثلة من الإصدار 4.0.0 أصلًا.
لا تُرسَل حالة المستكشف الخاصة به أبدًا، أما تبديل علامة التبويب أو التمرير الذي يجريه فيصل كما يصل أي تبديل آخر لعلامة التبويب أو تمرير آخر؛ راجع التحرير التعاوني.
منذ الإصدار 4.2.0، تشمل الإجراءات المحتفظ بها أيضًا table.changeLogicalName وtable.changeNameDerived وcolumn.changeLogicalName وcolumn.changeDomain وcolumn.changeNameDerived وsettings.changeNameViewMode، وdictionary.add وdictionary.update وdictionary.remove، التي تحمل كل تعديل وفرز واستيراد CSV في أوراق القاموس.
ومن عمليات تبديل عرض الأسماء، لا تطبّق النسخة المتماثلة إلا ما يجريه محرر ليس للقراءة فقط أثناء إلغاء قفل Name View، وتُسقط البقية (راجع on@change).
والمخزن من إصدار سابق يُسقط كل هذه الإجراءات، فيفقد الأسماء المنطقية، وروابط المجالات، وما إذا كان كل اسم مأخوذًا من القواميس، والكلمات والمصطلحات والمجالات، وعرض الأسماء.
ولا يزال بإمكان محرر سابق للإصدار 4.2.0 في الجلسة أن يُفقد نسخة متماثلة من الإصدار 4.2.0 بعضًا من ذلك.
فاستيراد ملف مخطط SQL أو GraphQL أو DBML أو AML عبر Import في ذلك المحرر، لا عبر Import and Add، يُفرغ الكلمات والمصطلحات والمجالات في النسخة المتماثلة.
وتراجعه عن تحميل أو عن Import أو عن clear() يحمّل نسخته الخاصة من المستند في النسخة المتماثلة، بلا أسماء منطقية ولا روابط مجالات ولا قواميس؛ راجع الجمع بين إصدارات مختلفة.
ويصدّر @dineug/erd-editor/engine.js الدالة createReplicationStore والنوعين ReplicationStore وReplicationChange كما كان من قبل، وتقبل الدالة createReplicationStore السياق نفسه.
لا يضيف الإصدار 5.1.0 أي نوع إجراء ولا يغيّر أي تصدير من @dineug/erd-editor/engine.js، لذا تحتفظ نسخة متماثلة من الإصدار 4.2.0 فصاعدًا بكل تغيير يرسله محرر من الإصدار 5.1.0.
منذ الإصدار 5.1.0، تحسم النسخة المتماثلة التعديلات التي تتم في اللحظة نفسها كما تحسمها محررات الإصدار 5.1.0؛ راجع التعديلات المتزامنة.
تحتفظ برقم نسخة لموضع الجدول، ولموضع الملاحظة ومجموعة الجداول وحجمهما، ولأصل العرض ومستوى التكبير.
يرسل محرر الإصدار 5.1.0 السحب أو تحريك العرض أو التكبير على أنه الموضع الذي ينتهي عنده، عبر table.moveTo وmemo.moveTo وtableGroup.moveTo وsettings.scrollTo وsettings.changeZoomLevel، وتأخذ النسخة المتماثلة كلًّا من هذه، وكل memo.resize وtableGroup.resize، فقط عندما لا يكون رقم نسخته أدنى من الرقم الذي تحمله.
لذا عندما يحرّك محرران من هذه المحررات جدولًا أو ملاحظة أو مجموعة جداول واحدة، أو يغيّران حجمها، في اللحظة نفسها، تحفظها النسخة المتماثلة عند الموضع والحجم اللذين يعرضهما كل منهما، أيًّا كان ترتيب سماعها لهما، ولا يحرّك سحبٌ أو تحريكٌ للعرض أو تكبيرٌ تسلّمه وسيلة النقل مرتين شيئًا مرتين.
مع إلغاء قفل Viewport، تحفظ النسخة المتماثلة أصل العرض ومستوى التكبير كلًّا منهما من الكتابة ذات أكبر رقم نسخة، أي حيث كان المحرر الذي أجراها.
يحرّك التكبير الأصل أيضًا، لذا قد يُحفظ ما يجريه محرران في اللحظة نفسها من تحريك للعرض وتكبير على أنه أصل أحد المحررين مع مستوى تكبير الآخر.
قبل الإصدار 5.1.0 كانت النسخة المتماثلة تضيف خطوات تحريك العرض والتكبير لدى كل محرر إلى عرض واحد.
تظل المسافات التي يرسلها محرر أقدم من الإصدار 5.1.0 للسحب أو تحريك العرض أو التكبير (table.move وmemo.move وtableGroup.move وsettings.streamScrollTo وsettings.streamZoomLevel) تُضاف إلى ما تحمله النسخة المتماثلة، خارج أرقام النسخ.
ولا يزال تغيير اللون يصل دون رقم نسخة: فتختمه النسخة المتماثلة، كما يفعل كل محرر، برقم نسخة خاص بها وتحتفظ باللون الذي سمعته أخيرًا، لذا قد يُحفظ تغييرا لون لجدول أو ملاحظة أو مجموعة جداول واحدة تمّا في اللحظة نفسها على نحو مختلف عمّا يعرضه المحرر.
النسخة المتماثلة الأقدم من الإصدار 5.1.0 تطبّق المواضع والأحجام بترتيب وصولها، لذا قد تحفظ جدولًا في غير المكان الذي تعرضه محررات الإصدار 5.1.0.
أبقِ كل محرر في الجلسة، والنسخة المتماثلة، على الإصدار 5.1.0 أو أحدث لكي تنتهي التعديلات التي تتم في اللحظة نفسها إلى النتيجة نفسها، عدا الاستثناءات التي يسردها قسم التعديلات المتزامنة.
منذ الإصدار 5.1.0، يرسل المحرر تغيير لغة منشئ التعليمات البرمجية أو نمط التسمية أو نوع علامات الاقتباس فقط ما دام قفل ذلك الإعداد معطّلًا، موسومًا بـ shared: true في حمولته، وتطبّق النسخة المتماثلة كل تغيير موسوم، كما تفعل المحررات؛ راجع التحرير التعاوني.
وتطبّق التغيير غير الموسوم، الذي يرسله محرر أقدم من الإصدار 5.1.0 أيًّا كان القفل، فقط ما دام ذلك الإعداد غير مقفل لديها، وتُسقطه ما دام الإعداد مقفلًا (راجع on@change).
منذ الإصدار 4.0.0، تطبّق الدالتان أيضًا editor.mergeLWW، الذي يحمل مسجّلات التعاون التي يردّ بها محرر عندما ينضم محرر آخر إلى الجلسة، لذا تحسم النسخة المتماثلة التعديلات المتزامنة بالطريقة التي تحسمها بها المحررات.
ولا يغيّر أي بايت من value ولا يُطلق أي change.
أما settings.changeIgnoreSaveSettings، الذي قد لا يزال يرسله محرر صدر قبل الإصدار 4.0.0، فيُحتفظ به لكنه لا يغيّر شيئًا: إذ يُطلق change مع تعيين changed إلى false.
destroy
تدمّر مثيل replicationStore بالكامل.
replicationStore.destroy();
بعد ذلك يتوقف المخزن عن إطلاق change ويتجاهل أي إرسال لاحق.
ويتجمّد المستند عند آخر قيمة له بدلًا من إثارة خطأ.
أنشئ مخزنًا جديدًا للبدء من جديد.