Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion build/lib/compendium-pack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { CompendiumActorUUID, CompendiumItemUUID, ItemUUID } from "@client/
import type { CompendiumDocumentType } from "@client/utils/_module.d.mts";
import type { ImageFilePath } from "@common/constants.d.mts";
import type { DocumentStatsData, DocumentStatsSchema, SourceFromSchema } from "@common/data/fields.d.mts";
import type { JournalEntrySource } from "@common/documents/journal-entry.d.mts";
import type { ItemSourcePF2e, MeleeSource } from "@item/base/data/index.ts";
import { FEAT_OR_FEATURE_CATEGORIES } from "@item/feat/values.ts";
import { itemIsOfType } from "@item/helpers.ts";
Expand Down Expand Up @@ -390,9 +391,15 @@ class CompendiumPack {
if (remap) CompendiumPack.convertUUIDs(docSource, { to: "ids", map: CompendiumPack.#namesToIds.Item });
docSource._stats.compendiumSource = this.#sourceIdOf(docSource._id ?? "", { docType: "Item" });
} else if ("pages" in docSource) {
for (const page of docSource.pages) {
const journal = docSource as JournalEntrySource;
for (const page of journal.pages) {
page._stats = { ...partialStats };
}
if (Array.isArray(journal.categories)) {
for (const category of journal.categories) {
if (R.isPlainObject(category)) category._stats = { ...partialStats };
}
}
}

const replace = (match: string, packId: string, docType: string, docName: string): string => {
Expand Down
94 changes: 52 additions & 42 deletions build/lib/level-database.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { CompendiumDocumentType } from "@client/utils/_module.d.mts";
import type { SourceFromSchema } from "@common/data/fields.d.mts";
import type { TableResultSource } from "@common/documents/_module.d.mts";
import type { JournalEntryCategorySource } from "@common/documents/journal-entry-category.d.mts";
import type { JournalEntryPageSchema } from "@common/documents/journal-entry-page.d.mts";
import type { ItemSourcePF2e } from "@item/base/data/index.ts";
import { tupleHasValue } from "@util";
Expand All @@ -12,31 +13,37 @@ import { PackEntry, PackManifest } from "./types.ts";

const DB_KEYS = ["actors", "items", "journal", "macros", "tables"] as const;

const EMBEDDED_KEYS: Record<DBKey, EmbeddedKey[]> = {
actors: ["items"],
items: [],
journal: ["pages", "categories"],
macros: [],
tables: ["results"],
};

class LevelDatabase extends ClassicLevel<string, DBEntry> {
constructor(location: string, options: LevelDatabaseOptions) {
const dbOptions = options.dbOptions ?? { keyEncoding: "utf8", valueEncoding: "json" };
super(location, dbOptions);
this.#manifest = options.manifest;
const { dbKey, embeddedKey } = this.#getDBKeys(options.packName);
this.#dbkey = dbKey;
this.#embeddedKey = embeddedKey;
const { dbKey, embeddedKeys } = this.#getDBKeys(options.packName);
this.#embeddedKeys = embeddedKeys;

this.#documentDb = this.sublevel(dbKey, dbOptions);
this.#foldersDb = this.sublevel("folders", dbOptions) as unknown as Sublevel<DBFolder>;
if (this.#embeddedKey) {
this.#embeddedDb = this.sublevel(
`${this.#dbkey}.${this.#embeddedKey}`,
dbOptions,
) as unknown as Sublevel<EmbeddedEntry>;
for (const embeddedKey of embeddedKeys) {
this.#embeddedDbs.set(
embeddedKey,
this.sublevel(`${dbKey}.${embeddedKey}`, dbOptions) as unknown as Sublevel<EmbeddedEntry>,
);
}
}

#dbkey: DBKey;
#embeddedKey: EmbeddedKey | null;
#embeddedKeys: EmbeddedKey[];

#documentDb: Sublevel<DBEntry>;
#foldersDb: Sublevel<DBFolder>;
#embeddedDb: Sublevel<EmbeddedEntry> | null = null;
#embeddedDbs = new Map<EmbeddedKey, Sublevel<EmbeddedEntry>>();

#manifest: PackManifest;

Expand All @@ -51,14 +58,16 @@ class LevelDatabase extends ClassicLevel<string, DBEntry> {
return R.isPlainObject(source) && "_id" in source;
};
const docBatch = this.#documentDb.batch();
const embeddedBatch = this.#embeddedDb?.batch();
const embeddedBatches = new Map(this.#embeddedKeys.map((key) => [key, this.#embeddedDbs.get(key)?.batch()]));

for (const source of docSources) {
if (this.#embeddedKey) {
const embeddedDocs = source[this.#embeddedKey];
if (Array.isArray(embeddedDocs)) {
for (const embeddedKey of this.#embeddedKeys) {
const embeddedDocs = source[embeddedKey];
const embeddedBatch = embeddedBatches.get(embeddedKey);
if (Array.isArray(embeddedDocs) && embeddedBatch) {
for (let i = 0; i < embeddedDocs.length; i++) {
const doc = embeddedDocs[i];
if (isDoc(doc) && embeddedBatch) {
if (isDoc(doc)) {
embeddedBatch.put(`${source._id}.${doc._id}`, doc);
embeddedDocs[i] = doc._id ?? "";
}
Expand All @@ -67,10 +76,14 @@ class LevelDatabase extends ClassicLevel<string, DBEntry> {
}
docBatch.put(source._id ?? "", source);
}

await docBatch.write();
if (embeddedBatch?.length) {
await embeddedBatch.write();
}
await Promise.all(
[...embeddedBatches.values()]
.filter((batch): batch is NonNullable<typeof batch> => !!batch?.length)
.map((batch) => batch.write()),
);

if (folders.length) {
const folderBatch = this.#foldersDb.batch();
for (const folder of folders) {
Expand All @@ -85,12 +98,14 @@ class LevelDatabase extends ClassicLevel<string, DBEntry> {
async getEntries(): Promise<{ packSources: PackEntry[]; folders: DBFolder[] }> {
const packSources: PackEntry[] = [];
for await (const [docId, source] of this.#documentDb.iterator()) {
const embeddedKey = this.#embeddedKey;
if (embeddedKey && source[embeddedKey] && this.#embeddedDb) {
const embeddedDocs = await this.#embeddedDb.getMany(
source[embeddedKey]?.map((embeddedId) => `${docId}.${embeddedId}`) ?? [],
);
source[embeddedKey] = embeddedDocs.filter(R.isTruthy);
for (const embeddedKey of this.#embeddedKeys) {
const embeddedDb = this.#embeddedDbs.get(embeddedKey);
if (embeddedDb && source[embeddedKey]) {
const embeddedDocs = await embeddedDb.getMany(
source[embeddedKey]?.map((embeddedId) => `${docId}.${embeddedId}`) ?? [],
);
source[embeddedKey] = embeddedDocs.filter(R.isTruthy);
}
}
packSources.push(source as PackEntry);
}
Expand All @@ -111,7 +126,7 @@ class LevelDatabase extends ClassicLevel<string, DBEntry> {
};
}

#getDBKeys(packName: string): { dbKey: DBKey; embeddedKey: EmbeddedKey | null } {
#getDBKeys(packName: string): { dbKey: DBKey; embeddedKeys: EmbeddedKey[] } {
const metadata = this.#manifest.packs.find((p: { path: string }) => p.path.endsWith(packName));
if (!metadata) {
throw PackError(
Expand All @@ -132,33 +147,28 @@ class LevelDatabase extends ClassicLevel<string, DBEntry> {
}
}
})();
const embeddedKey = ((): EmbeddedKey | null => {
switch (dbKey) {
case "actors":
return "items";
case "journal":
return "pages";
case "tables":
return "results";
default:
return null;
}
})();
return { dbKey, embeddedKey };

return { dbKey, embeddedKeys: EMBEDDED_KEYS[dbKey] };
}
}

type DBKey = (typeof DB_KEYS)[number];
type EmbeddedKey = "items" | "pages" | "results";
type EmbeddedKey = "items" | "pages" | "results" | "categories";

type Sublevel<T> = AbstractSublevel<ClassicLevel<string, T>, string | Buffer | Uint8Array, string, T>;

type EmbeddedEntry = ItemSourcePF2e | SourceFromSchema<JournalEntryPageSchema> | TableResultSource;
type DBEntry = Omit<PackEntry, "pages" | "items" | "results"> & {
type EmbeddedEntry =
| ItemSourcePF2e
| SourceFromSchema<JournalEntryPageSchema>
| JournalEntryCategorySource
| TableResultSource;

type DBEntry = Omit<PackEntry, "pages" | "items" | "results" | "categories"> & {
folder?: string | null;
items?: (EmbeddedEntry | string)[];
pages?: (EmbeddedEntry | string)[];
results?: (EmbeddedEntry | string)[];
categories?: (EmbeddedEntry | string)[];
};

interface DBFolder {
Expand Down
30 changes: 25 additions & 5 deletions packs/pf2e/journals/ancestries.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
{
"_id": "45SK8rdbbxvEHfMn",
"categories": [
"JKYLwIn53bs8zQbU",
"s2iWckA2wS32kB7e",
"eY5gsE0P1zQxGxhk",
"IwcrxO5mhTmJddDe",
"3NQ1qQzcLeiG2xAG"
{
"_id": "JKYLwIn53bs8zQbU",
"name": "Index",
"sort": -187500
},
{
"_id": "s2iWckA2wS32kB7e",
"name": "Common",
"sort": -175000
},
{
"_id": "eY5gsE0P1zQxGxhk",
"name": "Uncommon",
"sort": -14843
},
{
"_id": "IwcrxO5mhTmJddDe",
"name": "Rare",
"sort": 100000
},
{
"_id": "3NQ1qQzcLeiG2xAG",
"name": "Versatile Heritages",
"sort": 4900000
}
],
"name": "Ancestries",
"ownership": {
Expand Down
24 changes: 20 additions & 4 deletions packs/pf2e/journals/classes.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
{
"_id": "kzxu2dI7tFxv6Ix6",
"categories": [
"2m1uNKgr9PZSerje",
"6pIceksbaX5LlxTg",
"okLKpZprx8STrJnJ",
"z7ZwuD7y5RmLbj8W"
{
"_id": "2m1uNKgr9PZSerje",
"name": "Common",
"sort": 100000
},
{
"_id": "6pIceksbaX5LlxTg",
"name": "Uncommon",
"sort": 1000000
},
{
"_id": "okLKpZprx8STrJnJ",
"name": "Rare",
"sort": 800000
},
{
"_id": "z7ZwuD7y5RmLbj8W",
"name": "Features",
"sort": 2150000
}
],
"name": "Classes",
"ownership": {
Expand Down
30 changes: 25 additions & 5 deletions packs/pf2e/journals/gm-screen.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
{
"_id": "S55aqwWIzpQRFhcq",
"categories": [
"fPlT3IX0WJt1wXG6",
"UETWXG7yvlN3eIbb",
"XjPJozSumWKRXBSJ",
"kitbT36EV0VRh7nG",
"iRqU6roy9zfUsShV"
{
"_id": "fPlT3IX0WJt1wXG6",
"name": "GM Screen",
"sort": -1100000
},
{
"_id": "UETWXG7yvlN3eIbb",
"name": "Player Screen",
"sort": -1000000
},
{
"_id": "XjPJozSumWKRXBSJ",
"name": "Playing the Game",
"sort": 2100000
},
{
"_id": "kitbT36EV0VRh7nG",
"name": "Running the Game",
"sort": -812500
},
{
"_id": "iRqU6roy9zfUsShV",
"name": "Subsystems and Variant Rules",
"sort": 3600000
}
],
"name": "GM Screen",
"ownership": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ type JournalEntryCategorySchema = {
_stats: fields.DocumentStatsField;
};

export {};
export type JournalEntryCategorySource = fields.SourceFromSchema<JournalEntryCategorySchema>;
6 changes: 5 additions & 1 deletion types/foundry/common/documents/journal-entry.d.mts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Document, DocumentMetadata, EmbeddedCollection } from "../abstract/_module.mjs";
import * as fields from "../data/fields.mjs";
import { BaseFolder, BaseJournalEntryPage } from "./_module.mjs";
import { BaseFolder, BaseJournalEntryCategory, BaseJournalEntryPage } from "./_module.mjs";

/** The JournalEntry document model. */
export default class BaseJournalEntry extends Document<null, JournalEntrySchema> {
Expand All @@ -12,6 +12,7 @@ export default class BaseJournalEntry extends Document<null, JournalEntrySchema>
export default interface BaseJournalEntry
extends Document<null, JournalEntrySchema>, fields.ModelPropsFromSchema<JournalEntrySchema> {
readonly pages: EmbeddedCollection<BaseJournalEntryPage<this>>;
readonly categories: EmbeddedCollection<BaseJournalEntryCategory<this>>;

get documentName(): JournalEntryMetadata["name"];
}
Expand All @@ -23,6 +24,7 @@ interface JournalEntryMetadata extends DocumentMetadata {
compendiumIndexFields: ["_id", "name", "sort"];
embedded: {
JournalEntryPage: "pages";
JournalEntryCategory: "categories";
};
label: "DOCUMENT.JournalEntry";
labelPlural: "DOCUMENT.JournalEntries";
Expand All @@ -39,6 +41,8 @@ type JournalEntrySchema = {
name: fields.StringField<string, string, true, false, false>;
/** The pages contained within this JournalEntry document */
pages: fields.EmbeddedCollectionField<BaseJournalEntryPage<BaseJournalEntry>>;
/** The categories contained within this JournalEntry document */
categories: fields.EmbeddedCollectionField<BaseJournalEntryCategory<BaseJournalEntry>>;
/** The _id of a Folder which contains this JournalEntry */
folder: fields.ForeignDocumentField<BaseFolder>;
/** The numeric sort value which orders this JournalEntry relative to its siblings */
Expand Down
Loading