@captiveorigins/payload-plugin-onephylogeny 0.1.0-alpha.3 → 0.1.0-alpha.4

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/README.md CHANGED
@@ -23,3 +23,5 @@ It refuses, in plain words, a file whose text doesn't match its SHA-256, a file
23
23
 
24
24
  `assignWaiting(payload, { evaluate })` is the sweep. It tests each waiting sequence against the panel for its marker and species, using the `evaluate` function you pass (OnePhylogeny's `testSample` in a Node job), and records a result pending review. A sequence with no panel stays waiting, and one the panel's checks refuse is marked `not-assessable`, with the reason. Results reach readers only after a reviewer approves them.
25
25
 
26
+ On a multi-site host, set `tenantField` if the site field isn't called `tenant`. A result, sample or sequence keeps the site it was made for, so nobody can move it to another site's readers.
27
+
package/dist/index.d.ts CHANGED
@@ -127,6 +127,11 @@ type OnePhylogenyPluginOptions = {
127
127
  * sequences. Left out, neither collection is added. Results then relate to these sequences.
128
128
  */
129
129
  records?: RecordsOptions;
130
+ /**
131
+ * The host's tenant (site) field on results, if it has one, such as a multi-tenant plugin adds. A result's site is set
132
+ * when it is made and can't change, so a reviewer can't move it to a site whose people would then see it. Default `tenant`.
133
+ */
134
+ tenantField?: string;
130
135
  };
131
136
 
132
137
  /**
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ function recordCollections(o, base) {
33
33
  const manage = base.manage;
34
34
  const read = anyOf(manage, o.readAccess);
35
35
  const access = { read, create: manage, update: manage, delete: () => false };
36
+ const tenantField = o.tenantField ?? "tenant";
36
37
  const sampleCollection = {
37
38
  slug: samples,
38
39
  labels: { singular: "Sample", plural: "Samples" },
@@ -47,7 +48,7 @@ function recordCollections(o, base) {
47
48
  { name: "collectedAt", type: "date" },
48
49
  { name: "consent", type: "text", admin: { description: "The keeper's consent, as recorded with the kit or upload." } }
49
50
  ],
50
- hooks: { beforeChange: [locked(["subject", "kit", "source"], "A sample's animal, kit and source can't change. Record a new sample instead.")] }
51
+ hooks: { beforeChange: [locked(["subject", "kit", "source", tenantField], "A sample's animal, kit, source and site can't change. Record a new sample instead.")] }
51
52
  };
52
53
  const sequenceCollection = {
53
54
  slug: sequences,
@@ -66,14 +67,14 @@ function recordCollections(o, base) {
66
67
  { name: "status", type: "select", required: true, defaultValue: "received", index: true, options: ["received", "assigned", "not-assessable"] },
67
68
  { name: "statusReason", type: "text", admin: { description: "Why it couldn't be assessed, in plain words." } }
68
69
  ],
69
- hooks: { beforeChange: [locked(["sample", "marker", "file", "sha256", "residues", "recordId", "length"], "A sequence can't be edited. Register the corrected file instead.")] }
70
+ hooks: { beforeChange: [locked(["sample", "marker", "file", "sha256", "residues", "recordId", "length", tenantField], "A sequence can't be edited. Register the corrected file instead.")] }
70
71
  };
71
72
  const config = {
72
73
  samples,
73
74
  sequences,
74
75
  panels: base.panels,
75
76
  assignments: base.assignments,
76
- tenantField: o.tenantField ?? "tenant",
77
+ tenantField,
77
78
  markers: o.markers,
78
79
  subject: base.subject,
79
80
  hasTaxa: Boolean(base.taxa),
@@ -269,7 +270,7 @@ var supersedeOthers = (panels) => async ({ doc, previousDoc, req }) => {
269
270
  for (const other of others.docs) await req.payload.update({ collection: panels, id: other.id, data: { status: "superseded" }, req, overrideAccess: true });
270
271
  return doc;
271
272
  };
272
- var assignmentBeforeChange = (panels, againstShown) => async ({ data, originalDoc, operation, req }) => {
273
+ var assignmentBeforeChange = (panels, againstShown, tenantField) => async ({ data, originalDoc, operation, req }) => {
273
274
  if (operation === "create") {
274
275
  if ((data.reviewStatus ?? "pending") !== "pending") fail2("A new result starts as pending review.");
275
276
  const panel = await req.payload.findByID({ collection: panels, id: idOf2(data.panel), depth: 0, req, overrideAccess: true });
@@ -292,6 +293,7 @@ var assignmentBeforeChange = (panels, againstShown) => async ({ data, originalDo
292
293
  for (const f of ["result", "panel", "sequence", "subject", "summary", "importRun"]) {
293
294
  if (f in data && (f === "result" || f === "summary" ? !same(data[f], originalDoc[f]) : idOf2(data[f]) !== idOf2(originalDoc[f]))) fail2("A result can't be edited. Make a new one instead.");
294
295
  }
296
+ if (tenantField in data && idOf2(data[tenantField]) !== idOf2(originalDoc[tenantField])) fail2("A result stays on the site it was made for.");
295
297
  for (const f of ["panelStatus", "panelShownAsExploratory"]) {
296
298
  if (f in data && !same(data[f], originalDoc[f])) fail2("The panel status a result was made against is set when it is made.");
297
299
  }
@@ -313,7 +315,8 @@ var checkedWhenMade = { validate: (value, options) => options.operation === "upd
313
315
  var onePhylogeny = (options = {}) => (config) => {
314
316
  const panels = options.slugs?.panels ?? "panels";
315
317
  const assignments = options.slugs?.assignments ?? "assignments";
316
- const records = options.records ? recordCollections(options.records, { panels, assignments, subject: options.relations?.subject, taxa: options.relations?.taxa, files: options.relations?.files, manage: options.manageAccess ?? (() => false), againstShown: options.resultsAgainst === "activeOrShown" }) : void 0;
318
+ const tenantField = options.tenantField ?? options.records?.tenantField ?? "tenant";
319
+ const records = options.records ? recordCollections({ tenantField, ...options.records }, { panels, assignments, subject: options.relations?.subject, taxa: options.relations?.taxa, files: options.relations?.files, manage: options.manageAccess ?? (() => false), againstShown: options.resultsAgainst === "activeOrShown" }) : void 0;
317
320
  const r = { ...options.relations, ...records && !options.relations?.sequence ? { sequence: records.config.sequences } : {} };
318
321
  const group = options.group ?? "Genetics";
319
322
  const manage = options.manageAccess ?? (() => false);
@@ -380,7 +383,7 @@ var onePhylogeny = (options = {}) => (config) => {
380
383
  { name: "panelStatus", type: "text", admin: { readOnly: true, description: "The panel's status when this result was made." } },
381
384
  { name: "panelShownAsExploratory", type: "checkbox", admin: { readOnly: true } }
382
385
  ],
383
- hooks: { beforeChange: [assignmentBeforeChange(panels, againstShown)] }
386
+ hooks: { beforeChange: [assignmentBeforeChange(panels, againstShown, tenantField)] }
384
387
  };
385
388
  const released = {
386
389
  path: "/onephylogeny/released",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@captiveorigins/payload-plugin-onephylogeny",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "OnePhylogeny for Payload: immutable reference panels and released results on your own collections, with server-enforced review, safe endpoints and no coordinates.",
5
5
  "keywords": [
6
6
  "phylogenetics",
@@ -35,7 +35,7 @@
35
35
  "node": ">=22"
36
36
  },
37
37
  "dependencies": {
38
- "@captiveorigins/onephylogeny-core": "^0.1.0-alpha.3"
38
+ "@captiveorigins/onephylogeny-core": "^0.1.0-alpha.4"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "payload": "^3.80.0"