@mulmoclaude/core 3.14.0 → 3.15.0
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/dist/collection/registry/server/index.cjs +23 -6
- package/dist/collection/registry/server/index.cjs.map +1 -1
- package/dist/collection/registry/server/index.js +24 -7
- package/dist/collection/registry/server/index.js.map +1 -1
- package/dist/collection/server/appViews.d.ts +40 -1
- package/dist/collection/server/index.cjs +2 -1
- package/dist/collection/server/index.d.ts +1 -1
- package/dist/collection/server/index.js +2 -2
- package/dist/collection/server/publishProject.d.ts +31 -3
- package/dist/collection-watchers/index.cjs +1 -1
- package/dist/collection-watchers/index.js +1 -1
- package/dist/feeds/server/index.cjs +1 -1
- package/dist/feeds/server/index.js +1 -1
- package/dist/{server-DknYbXBo.cjs → server-DLzQ4jFu.cjs} +136 -24
- package/dist/server-DLzQ4jFu.cjs.map +1 -0
- package/dist/{server-CQSF5fHe.js → server-jm7aGA3g.js} +131 -25
- package/dist/server-jm7aGA3g.js.map +1 -0
- package/package.json +1 -1
- package/dist/server-CQSF5fHe.js.map +0 -1
- package/dist/server-DknYbXBo.cjs.map +0 -1
|
@@ -170,6 +170,90 @@ function participantScope(app, cid, participantRead) {
|
|
|
170
170
|
* prefixes, so a single `match` covers the projection and every view. */
|
|
171
171
|
var viewDocId = (stage, viewId) => `${stage}:${viewId}`;
|
|
172
172
|
var VIEW_CONFIG_ID = "config";
|
|
173
|
+
/** The role a member holds on one collection, by the rules' own resolution:
|
|
174
|
+
* the per-collection entry, else the `*` fallback, else none. */
|
|
175
|
+
function roleOn(app, address, cid) {
|
|
176
|
+
const held = app.members[address];
|
|
177
|
+
if (held === void 0) return void 0;
|
|
178
|
+
return held[cid] ?? held["*"];
|
|
179
|
+
}
|
|
180
|
+
/** The addresses holding one of `roles` on `cid`.
|
|
181
|
+
*
|
|
182
|
+
* Sorted, for the same reason `memberEmails` is: a second publish of an
|
|
183
|
+
* unchanged declaration must produce an unchanged document. */
|
|
184
|
+
function holdersOf(app, cid, roles) {
|
|
185
|
+
return Object.keys(app.members).filter((address) => roles.includes(roleOn(app, address, cid) ?? "")).sort();
|
|
186
|
+
}
|
|
187
|
+
/** Who may write every row of `cid`, and who may write only their own.
|
|
188
|
+
*
|
|
189
|
+
* WHY ADDRESSES ARE PUBLISHED AT ALL. One `member/config` document is read by
|
|
190
|
+
* everyone the tier admits, and the tier only establishes that somebody holds
|
|
191
|
+
* SOME role SOMEWHERE — so a `viewer`, or a stylist scoped to another
|
|
192
|
+
* collection, reads the same entry as the front desk. Without these lists the
|
|
193
|
+
* page would draw approve and reassign for all of them and the rules would
|
|
194
|
+
* refuse when pressed, which is the declaration/enforcement mismatch this
|
|
195
|
+
* whole mechanism exists to prevent.
|
|
196
|
+
*
|
|
197
|
+
* It cannot be answered per principal instead: the document is written once
|
|
198
|
+
* at publish and read by many, and the reader cannot look their own role up —
|
|
199
|
+
* `apps/{aid}` is `readerOf(a, '*')`, and a stylist carrying only
|
|
200
|
+
* `{bookings: "editor"}` holds no `*` role. So the ROSTER'S ANSWER travels
|
|
201
|
+
* with the declaration and the page compares its own address to it.
|
|
202
|
+
*
|
|
203
|
+
* The cost is that staff addresses are visible to staff. That is already true
|
|
204
|
+
* of the approval mail they send each other, and participants read the
|
|
205
|
+
* `roster` tier, which never carries these.
|
|
206
|
+
*
|
|
207
|
+
* A SNAPSHOT, like everything else published: a member added since the last
|
|
208
|
+
* publish is absent until the next one. The rules are the authority either
|
|
209
|
+
* way — this only decides which buttons are drawn. */
|
|
210
|
+
function writersOf(app, cid) {
|
|
211
|
+
return holdersOf(app, cid, ["owner", "editor"]);
|
|
212
|
+
}
|
|
213
|
+
/** The transition half: which table applies, and the field it moves.
|
|
214
|
+
*
|
|
215
|
+
* Both halves or neither. A status field with no table would offer every
|
|
216
|
+
* value; a table with no field has nothing to write it to. */
|
|
217
|
+
function transitionPart(app, audience, cid) {
|
|
218
|
+
const config = app.collections?.[cid];
|
|
219
|
+
const transitions = audience === "member" ? config?.transitions : app.public?.submit?.[cid]?.selfTransitions;
|
|
220
|
+
if (config?.statusField === void 0 || transitions === void 0) return {};
|
|
221
|
+
const part = {
|
|
222
|
+
statusField: config.statusField,
|
|
223
|
+
transitions
|
|
224
|
+
};
|
|
225
|
+
if (audience === "member" && config.mail !== void 0) part.mail = config.mail;
|
|
226
|
+
return part;
|
|
227
|
+
}
|
|
228
|
+
/** The assignment half. `member` only — see {@link writersOf}.
|
|
229
|
+
*
|
|
230
|
+
* `rowWriters` rides here rather than beside `writers`, because the
|
|
231
|
+
* `assignee` role grants nothing at all without a field to compare against
|
|
232
|
+
* (`isAssigned` in the rules requires one, and publish refuses the pair). */
|
|
233
|
+
function assignPart(app, audience, cid) {
|
|
234
|
+
const assigneeField = app.collections?.[cid]?.assigneeField;
|
|
235
|
+
if (audience !== "member" || assigneeField === void 0) return {};
|
|
236
|
+
return {
|
|
237
|
+
assigneeField,
|
|
238
|
+
rowWriters: holdersOf(app, cid, ["assignee"])
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/** What `audience` may change about `cid`, or null when the answer is nothing.
|
|
242
|
+
*
|
|
243
|
+
* The two audiences differ in WHICH transition table applies, in whether
|
|
244
|
+
* assignment exists at all, and in whether the roster's answer travels with
|
|
245
|
+
* it; they agree that the status field is the collection's, since the rules
|
|
246
|
+
* read one field either way. */
|
|
247
|
+
function writeFor(app, audience, cid) {
|
|
248
|
+
const write = {
|
|
249
|
+
cid,
|
|
250
|
+
...transitionPart(app, audience, cid),
|
|
251
|
+
...assignPart(app, audience, cid)
|
|
252
|
+
};
|
|
253
|
+
if (Object.keys(write).length === 1) return null;
|
|
254
|
+
if (audience === "member") write.writers = writersOf(app, cid);
|
|
255
|
+
return write;
|
|
256
|
+
}
|
|
173
257
|
//#endregion
|
|
174
258
|
//#region src/collection/server/publishManifest.ts
|
|
175
259
|
/** A collection id / app id, held to the one name rule (`SAFE_SLUG_PATTERN`)
|
|
@@ -804,37 +888,59 @@ function scopeFor(authored, audience, cid, participantRead) {
|
|
|
804
888
|
scope: "all"
|
|
805
889
|
} : participantScope(authored, cid, participantRead);
|
|
806
890
|
}
|
|
807
|
-
/**
|
|
891
|
+
/** What this audience may CHANGE, per collection it draws.
|
|
808
892
|
*
|
|
809
|
-
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
893
|
+
* The `collections` config is the PROMOTED one where there is one: at publish
|
|
894
|
+
* the rules run against what deploy staged, so projecting the manifest's
|
|
895
|
+
* would advertise transitions the live rules deny. */
|
|
896
|
+
function tierWrites(authored, audience, cids, promoted) {
|
|
897
|
+
const effective = promoted.collections === void 0 ? authored : {
|
|
898
|
+
...authored,
|
|
899
|
+
collections: promoted.collections
|
|
900
|
+
};
|
|
901
|
+
return cids.map((cid) => writeFor(effective, audience, cid)).filter((entry) => entry !== null);
|
|
902
|
+
}
|
|
903
|
+
/** What this audience may READ, and how to query for it.
|
|
904
|
+
*
|
|
905
|
+
* A collection with no scope is dropped rather than published as unreachable:
|
|
906
|
+
* the gate has already refused the declaration, so reaching here with one is
|
|
907
|
+
* a programming error, and a page that queries it is denied. */
|
|
908
|
+
function tierViews(authored, audience, views, participantRead) {
|
|
909
|
+
return views.map((view) => ({
|
|
910
|
+
id: view.id,
|
|
911
|
+
collections: view.collections.map((cid) => scopeFor(authored, audience, cid, participantRead)).filter((scope) => scope !== null)
|
|
912
|
+
}));
|
|
913
|
+
}
|
|
914
|
+
/** One tier's projection: what this audience may read, and what it may change. */
|
|
915
|
+
function tierConfig(authored, audience, views, stamp, promoted) {
|
|
916
|
+
const cids = [...new Set(views.flatMap((view) => view.collections))];
|
|
917
|
+
const config = {
|
|
918
|
+
write: tierWrites(authored, audience, cids, promoted),
|
|
919
|
+
views: tierViews(authored, audience, views, promoted.participantRead ?? authored.participantRead ?? []),
|
|
920
|
+
submit: tierSubmit(authored, cids),
|
|
921
|
+
publishedAt: stamp.publishedAt
|
|
922
|
+
};
|
|
923
|
+
if (authored.name !== void 0) config.name = authored.name;
|
|
924
|
+
return config;
|
|
925
|
+
}
|
|
926
|
+
/** The submit declarations for the collections these views draw, so a page can
|
|
927
|
+
* show what may be sent rather than discovering it from a denial. */
|
|
928
|
+
function tierSubmit(authored, cids) {
|
|
929
|
+
const declared = authored.public?.submit ?? {};
|
|
930
|
+
return Object.fromEntries(cids.flatMap((cid) => {
|
|
931
|
+
const spec = declared[cid];
|
|
932
|
+
return spec === void 0 ? [] : [[cid, projectSubmit(spec)]];
|
|
933
|
+
}));
|
|
934
|
+
}
|
|
935
|
+
function projectAppViews(authored, stamp, promoted = {}) {
|
|
815
936
|
const normalized = normalizeViews(authored);
|
|
816
937
|
if (!normalized.ok) throw new Error(`publish: views declaration is not publishable (${normalized.problems.join(" ")})`);
|
|
817
938
|
return ["member", "participant"].map((audience) => {
|
|
818
939
|
const views = normalized.views.filter((view) => view.audience === audience);
|
|
819
|
-
const cids = [...new Set(views.flatMap((view) => view.collections))];
|
|
820
|
-
const declaredSubmit = authored.public?.submit ?? {};
|
|
821
|
-
const submit = Object.fromEntries(cids.flatMap((cid) => {
|
|
822
|
-
const spec = declaredSubmit[cid];
|
|
823
|
-
return spec === void 0 ? [] : [[cid, projectSubmit(spec)]];
|
|
824
|
-
}));
|
|
825
|
-
const config = {
|
|
826
|
-
views: views.map((view) => ({
|
|
827
|
-
id: view.id,
|
|
828
|
-
collections: view.collections.map((cid) => scopeFor(authored, audience, cid, participantRead)).filter((scope) => scope !== null)
|
|
829
|
-
})),
|
|
830
|
-
submit,
|
|
831
|
-
publishedAt: stamp.publishedAt
|
|
832
|
-
};
|
|
833
|
-
if (authored.name !== void 0) config.name = authored.name;
|
|
834
940
|
return {
|
|
835
941
|
tier: VIEW_TIER[audience],
|
|
836
942
|
audience,
|
|
837
|
-
config,
|
|
943
|
+
config: tierConfig(authored, audience, views, stamp, promoted),
|
|
838
944
|
views
|
|
839
945
|
};
|
|
840
946
|
});
|
|
@@ -3552,6 +3658,6 @@ function makeManageCollectionTool(deps = {}) {
|
|
|
3552
3658
|
};
|
|
3553
3659
|
}
|
|
3554
3660
|
//#endregion
|
|
3555
|
-
export { stagedRuleConfig as $, runQueryOverRows as A, APPS_COLLECTION as B, STORE_UNREADABLE as C, recordFieldProblem as D, compileRecordZ as E, readCustomViewI18n as F, appSlugDoc as G, PUBLIC_CONFIG_DOC as H, readSkillTemplate as I, projectApp as J, appStagingPath as K, bindsSubmitterIdentity as L, buildCollectionActionSeedPrompt as M, promptPathsFor as N, runCollectionQuery as O, readCustomViewHtml as P, promoteSchema as Q, promotedRoleProblems as R, MAX_RECORD_ISSUES as S, validateRecordObject as T, appConfigPath as U, APP_SLUGS_COLLECTION as V, appSchemasPath as W, projectDeploy as X, projectAppViews as Y, projectPublish as Z, computeCollectionIcon as _, deleteCollection as a, RESERVED_VIEW_IDS as at, applyMutateAction as b, computeSuccessor as c, VIEW_ID_PATTERN as ct, isTriggerDue as d, participantScope as dt, viewConfigDocId as et, maybeSpawnSuccessor as f, viewDocId as ft, ONE_SECOND_MS as g, successorId as h, deleteCustomView as i, PUBLIC_VIEW_ID as it, buildActionSeedPrompt as j, enrichItems as k, daysInMonth as l, VIEW_TIER as lt, resolveEvery as m, MAX_UNSELECTIVE_ITEMS as n, AuthoredAppZ as nt, deleteCollectionRefusalMessage as o, VIEW_AUDIENCES as ot, parseCivil as p, appViewTierPath as q, makeManageCollectionTool as r, parseAuthoredApp as rt, advanceTriggerDate as s, VIEW_CONFIG_ID as st, MAX_SCHEMA_ISSUES as t, APP_ROLES as tt, formatCivil as u, normalizeViews as ut, buildWorkspaceOntology as v, validateCollectionRecords as w, firstMutateParamProblem as x, schemaRelations as y, publishProblems as z };
|
|
3661
|
+
export { stagedRuleConfig as $, runQueryOverRows as A, APPS_COLLECTION as B, STORE_UNREADABLE as C, recordFieldProblem as D, compileRecordZ as E, readCustomViewI18n as F, appSlugDoc as G, PUBLIC_CONFIG_DOC as H, readSkillTemplate as I, projectApp as J, appStagingPath as K, bindsSubmitterIdentity as L, buildCollectionActionSeedPrompt as M, promptPathsFor as N, runCollectionQuery as O, readCustomViewHtml as P, promoteSchema as Q, promotedRoleProblems as R, MAX_RECORD_ISSUES as S, validateRecordObject as T, appConfigPath as U, APP_SLUGS_COLLECTION as V, appSchemasPath as W, projectDeploy as X, projectAppViews as Y, projectPublish as Z, computeCollectionIcon as _, deleteCollection as a, RESERVED_VIEW_IDS as at, applyMutateAction as b, computeSuccessor as c, VIEW_ID_PATTERN as ct, isTriggerDue as d, participantScope as dt, viewConfigDocId as et, maybeSpawnSuccessor as f, viewDocId as ft, ONE_SECOND_MS as g, successorId as h, deleteCustomView as i, PUBLIC_VIEW_ID as it, buildActionSeedPrompt as j, enrichItems as k, daysInMonth as l, VIEW_TIER as lt, resolveEvery as m, MAX_UNSELECTIVE_ITEMS as n, AuthoredAppZ as nt, deleteCollectionRefusalMessage as o, VIEW_AUDIENCES as ot, parseCivil as p, writeFor as pt, appViewTierPath as q, makeManageCollectionTool as r, parseAuthoredApp as rt, advanceTriggerDate as s, VIEW_CONFIG_ID as st, MAX_SCHEMA_ISSUES as t, APP_ROLES as tt, formatCivil as u, normalizeViews as ut, buildWorkspaceOntology as v, validateCollectionRecords as w, firstMutateParamProblem as x, schemaRelations as y, publishProblems as z };
|
|
3556
3662
|
|
|
3557
|
-
//# sourceMappingURL=server-
|
|
3663
|
+
//# sourceMappingURL=server-jm7aGA3g.js.map
|