Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | 9x 9x 9x 9x 87x 80x 80x 80x 80x 80x 51x 51x 51x 80x 29x 80x 80x 51x 51x 51x 51x 32x 32x 32x 2x 2x 2x 32x 19x 19x 19x 19x 13x 13x 13x 5x 4x 4x 4x 9x 9x 2x 1x 6x 5x 1x 12x 2x | import {Model, type NodeBuilder} from 'json-joy/lib/json-crdt';
import type {BlockId, LocalRepo} from '../local/types';
import {EditSession} from './EditSession';
import {timeout} from 'thingies/lib/timeout';
export interface EditSessionFactoryOpts {
readonly sid: number;
readonly repo: LocalRepo;
}
export class EditSessionFactory {
constructor(protected readonly opts: EditSessionFactoryOpts) {}
/**
* Creates a new editing session synchronously (immediately). If the block
* with a given ID already exists, it asynchronously synchronizes the local
* and remote state.
*/
public make(opts: EditSessionMakeOpts): {session: EditSession; sync?: Promise<void>} {
const {id, schema, pull = true} = opts;
const factoryOpts = this.opts;
const model = Model.create(void 0, factoryOpts.sid);
const session = new EditSession(factoryOpts.repo, id, model, undefined, opts.session);
if (schema) {
const sessionModel = session.model;
sessionModel.setSchema(schema);
sessionModel.api.flush();
}
let sync: Promise<void> | undefined;
if (pull && !session.log.patches.size()) {
sync = session
.sync()
.then(() => {})
.catch(() => {});
}
session.log.end.api.autoFlush();
return {session, sync};
}
/**
* Load block from the local repo. Creates a new editing session
* asynchronously from an existing local block.
*
* It is also possible to block on remote state check in case the block does
* not exist locally. When `pull` is set, it will also refresh the latest
* state from the remote in the background after returning local state.
*/
public async load(opts: EditSessionLoadOpts): Promise<EditSession> {
const id = opts.id;
const repo = this.opts.repo;
try {
const {model, cursor} = await repo.get({id});
const session = new EditSession(repo, id, model, cursor, opts.session);
session.log.end.api.autoFlush();
if (opts.pull) {
void repo
.pull(id)
.then(async ({cursor}) => {
session.cursor = cursor;
await session.load();
})
.catch(() => {});
}
return session;
} catch (error) {
const errorCode =
!!error && typeof error === 'object'
? (error as Record<string, unknown>).code || (error as Record<string, unknown>).message || ''
: '';
Eif (errorCode === 'NOT_FOUND') {
const remote = opts.remote;
if (remote) {
const timeoutMs = remote.timeout;
try {
const {model, cursor} = await (typeof timeoutMs === 'number'
? timeout(timeoutMs, repo.pull(id))
: repo.pull(id));
if (remote.throwIf === 'exists') throw new Error('EXISTS');
const session = new EditSession(repo, id, model, cursor, opts.session);
session.log.end.api.autoFlush();
return session;
} catch (error) {
const errorCode =
!!error && typeof error === 'object'
? (error as Record<string, unknown>).code || (error as Record<string, unknown>).message || ''
: '';
switch (errorCode) {
case 'TIMEOUT': {
if (!opts.make) throw error;
break;
}
case 'NOT_FOUND': {
if (remote.throwIf === 'missing') throw error;
break;
}
default: {
throw error;
}
}
}
}
if (opts.make) return this.make({session: opts.session, ...opts.make, id}).session;
}
throw error;
}
}
}
/**
* Constructs a new editing session synchronously.
*/
export interface EditSessionMakeOpts {
/** Block ID. */
id: BlockId;
/** The new block schema, if any. */
schema?: NodeBuilder;
/**
* Whether to asynchronously pull for any existing local block state, if a
* block with the same ID already exists. Defaults to `true`.
*/
pull?: boolean;
/**
* Internal unique session ID.
*/
session?: number;
}
/**
* Constructs and editing session asynchronously from an existing block. In
* case the block does not exist, it is possible to create one or throw an
* error.
*/
export interface EditSessionLoadOpts {
/** Block ID. */
id: BlockId;
/**
* If specified, will create a new block, if one does not already exist. Will
* use these `make` options and provide them to the `make()` call.
*/
make?: Omit<EditSessionMakeOpts, 'id'>;
// /** The new block schema, if any. */
// schema?: NodeBuilder;
/**
* Internal unique session ID.
*/
session?: number;
/**
* Whether to refresh the latest state from the remote in the background
* after loading an existing local block.
*/
pull?: boolean;
remote?: {
/**
* Time in milliseconds to wait for the remote to respond. If the remote
* does not respond in time, the call will proceed with the local state.
*
* If upsert `make` option is not provided, the call will throw a "TIMEOUT"
* error.
*/
timeout?: number;
/**
* Defaults to an empty string. Otherwise, if "missing", will throw a
* "NOT_FOUND" error if the block does not exist remotely. If "exists", will
* a "CONFLICT" error if the block exists remotely.
*/
throwIf?: '' | 'missing' | 'exists';
};
}
|