support other SQLite wrappers, and various hooks needed by grist-static (#516)

This commit is contained in:
Paul Fitzpatrick
2023-05-23 15:17:28 -04:00
committed by GitHub
parent bd474a382f
commit 7be0ee289d
42 changed files with 684 additions and 249 deletions

View File

@@ -198,7 +198,8 @@ export function encodeUrl(gristConfig: Partial<GristLoadConfig>,
options: {
// make an api url - warning: just barely works, and
// only for documents
api?: boolean
api?: boolean,
tweaks?: UrlTweaks,
} = {}): string {
const url = new URL(baseLocation.href);
const parts = ['/'];
@@ -269,8 +270,10 @@ export function encodeUrl(gristConfig: Partial<GristLoadConfig>,
}
}
const queryStr = encodeQueryParams(queryParams);
url.pathname = parts.join('');
url.search = queryStr;
if (state.hash) {
// Project tests use hashes, so only set hash if there is an anchor.
url.hash = hashParts.join('.');
@@ -285,13 +288,23 @@ export function encodeUrl(gristConfig: Partial<GristLoadConfig>,
} else {
url.hash = '';
}
options.tweaks?.postEncode?.({
url,
parts,
state,
baseLocation,
});
return url.href;
}
/**
* Parse a URL location into an IGristUrlState object. See encodeUrl() documentation.
*/
export function decodeUrl(gristConfig: Partial<GristLoadConfig>, location: Location | URL): IGristUrlState {
export function decodeUrl(gristConfig: Partial<GristLoadConfig>, location: Location | URL, options?: {
tweaks?: UrlTweaks,
}): IGristUrlState {
location = new URL(location.href); // Make sure location is a URL.
options?.tweaks?.preDecode?.({ url: location });
const parts = location.pathname.slice(1).split('/');
const map = new Map<string, string>();
for (let i = 0; i < parts.length; i += 2) {
@@ -871,3 +884,28 @@ export function getSlugIfNeeded(doc: {id: string, urlId: string|null, name: stri
if (!shouldIncludeSlug(doc)) { return; }
return nameToSlug(doc.name);
}
/**
* It is possible we want to remap Grist URLs in some way - specifically,
* grist-static does this. We allow for a hook that is called after
* encoding state as a URL, and a hook that is called before decoding
* state from a URL.
*/
export interface UrlTweaks {
/**
* Tweak an encoded URL. Operates on the URL directly, in place.
*/
postEncode?(options: {
url: URL,
parts: string[],
state: IGristUrlState,
baseLocation: Location | URL,
}): void;
/**
* Tweak a URL prior to decoding it. Operates on the URL directly, in place.
*/
preDecode?(options: {
url: URL,
}): void;
}

View File

@@ -33,6 +33,13 @@ import * as util from 'util';
export interface MarshalOptions {
stringToBuffer?: boolean;
version?: number;
// True if we want keys in dicts to be buffers.
// It is convenient to have some freedom here to simplify implementation
// of marshaling for some SQLite wrappers. This flag was initially
// introduced for a fork of Grist using better-sqlite3, and I don't
// remember exactly what the issues were.
keysAreBuffers?: boolean;
}
export interface UnmarshalOptions {
@@ -129,11 +136,13 @@ export class Marshaller {
private _memBuf: MemBuffer;
private readonly _floatCode: number;
private readonly _stringCode: number;
private readonly _keysAreBuffers: boolean;
constructor(options?: MarshalOptions) {
this._memBuf = new MemBuffer(undefined);
this._floatCode = options && options.version && options.version >= 2 ? marshalCodes.BFLOAT : marshalCodes.FLOAT;
this._stringCode = options && options.stringToBuffer ? marshalCodes.STRING : marshalCodes.UNICODE;
this._keysAreBuffers = Boolean(options?.keysAreBuffers);
}
public dump(): Uint8Array {
@@ -261,7 +270,7 @@ export class Marshaller {
const keys = Object.keys(obj);
keys.sort();
for (const key of keys) {
this.marshal(key);
this.marshal(this._keysAreBuffers ? Buffer.from(key) : key);
this.marshal(obj[key]);
}
this._memBuf.writeUint8(marshalCodes.NULL);