kempo 4.2.40 → 4.3.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.
Files changed (33) hide show
  1. package/README.md +7 -0
  2. package/app-public/default.template.html +1 -1
  3. package/dist/admin/nav.fragment.html +5 -0
  4. package/dist/admin/realtime/index.page.html +81 -0
  5. package/dist/kempo/api/realtime/WS.js +2 -0
  6. package/dist/kempo/api/realtime/WS.js.map +1 -0
  7. package/dist/kempo/api/realtime/connections/GET.js +2 -0
  8. package/dist/kempo/api/realtime/connections/GET.js.map +1 -0
  9. package/dist/kempo/icons/credit_card.svg +1 -0
  10. package/dist/kempo/icons/sensors.svg +1 -0
  11. package/dist/kempo/realtime.js +2 -0
  12. package/dist/kempo/realtime.js.map +1 -0
  13. package/dist/kempo/sdk.js +1 -1
  14. package/dist/kempo/sdk.js.map +1 -1
  15. package/middleware/kempo.js +30 -15
  16. package/package.json +2 -2
  17. package/scripts/init-db.js +4 -1
  18. package/server/db/schema.js +30 -1
  19. package/server/sdk.js +5 -0
  20. package/server/utils/extensions/contentDirs.js +27 -0
  21. package/server/utils/realtime/Hub.js +676 -0
  22. package/server/utils/realtime/channels.js +247 -0
  23. package/server/utils/realtime/closeConnection.js +4 -0
  24. package/server/utils/realtime/constants.js +20 -0
  25. package/server/utils/realtime/duration.js +18 -0
  26. package/server/utils/realtime/getHub.js +15 -0
  27. package/server/utils/realtime/index.js +14 -0
  28. package/server/utils/realtime/listConnections.js +4 -0
  29. package/server/utils/realtime/listSubscribers.js +4 -0
  30. package/server/utils/realtime/pruneMessages.js +45 -0
  31. package/server/utils/realtime/publish.js +86 -0
  32. package/server/utils/realtime/sendToConnection.js +4 -0
  33. package/server/utils/routing/serveUnmatched.js +8 -1
package/README.md CHANGED
@@ -168,6 +168,8 @@ Kempo provides these tables out of the box:
168
168
  | `permission` | Named permissions (e.g. `system:admin:access`) |
169
169
  | `groupPermission` | Group-to-permission assignments |
170
170
  | `setting` | Key-value settings with type, visibility, and ownership |
171
+ | `realtimeMessage` | Messages on channels that persist, so a reconnecting client can replay what it missed |
172
+ | `realtimeChannel` | How far each realtime channel has been pruned, to tell a client it missed messages |
171
173
 
172
174
  ## Frontend SDK
173
175
 
@@ -322,6 +324,7 @@ The `init-db.js` script seeds these permissions:
322
324
  | `system:settings:read` | View private settings |
323
325
  | `system:settings:update` | Update settings |
324
326
  | `system:custom-settings:manage` | Create and delete custom settings |
327
+ | `system:realtime:read` | View realtime connections and channels |
325
328
 
326
329
  Two default groups are created:
327
330
 
@@ -341,6 +344,10 @@ Two default groups are created:
341
344
 
342
345
  Settings can be managed through the admin panel at `/admin/settings` or the SDK.
343
346
 
347
+ ## Realtime
348
+
349
+ Extensions can push live updates to browsers: declare a channel, publish to it from any server code, and every subscribed browser on any kempo process receives it. Channels can persist messages so a client that reconnects replays what it missed, and a browser client handles connecting and reconnecting. Browsers can also send to a channel, where the extension's own handler receives and answers it, and extensions observe connections through hooks and act on them through SDK functions, so interactive features (chat, collaboration, games) can be built as extensions without touching a socket. Fast traffic can use an in-memory channel that skips the database, and connections are limited and backpressured. It uses a Postgres `LISTEN`/`NOTIFY` bus, so it needs no extra service. See [docs/realtime.md](docs/realtime.md).
350
+
344
351
  ## Email
345
352
 
346
353
  Kempo uses [Resend](https://resend.com/) for sending emails. Set `RESEND_API_KEY` and `EMAIL_FROM` in your `.env` file.
@@ -16,7 +16,7 @@
16
16
  </head>
17
17
  <body>
18
18
  <fragment name="nav" />
19
- <main>
19
+ <main id="main">
20
20
  <location />
21
21
  </main>
22
22
  <location name="scripts" />
@@ -72,6 +72,11 @@
72
72
  href="/admin/extensions"
73
73
  no-expand
74
74
  >Extensions</k-aside-item>
75
+ <k-aside-item
76
+ icon="sensors"
77
+ href="/admin/realtime"
78
+ no-expand
79
+ >Realtime</k-aside-item>
75
80
  <hr>
76
81
  <location name="admin-nav-extensions"></location>
77
82
  <k-aside-spacer></k-aside-spacer>
@@ -0,0 +1,81 @@
1
+ <!--
2
+ locked: true
3
+ -->
4
+ <page title="Realtime - Admin">
5
+ <content>
6
+ <script type="module" src="/kempo-ui/components/Table.js"></script>
7
+ <script type="module" src="/kempo-ui/components/Icon.js"></script>
8
+
9
+ <div class="d-f mb">
10
+ <h1 class="flex mb0">Realtime</h1>
11
+ <button id="refresh"><k-icon name="replay"></k-icon> Refresh</button>
12
+ </div>
13
+
14
+ <p class="tc-muted" id="scope">Loading&hellip;</p>
15
+
16
+ <h2>Connections</h2>
17
+ <k-table id="connectionsTable" placeholder="No one is connected to this process."></k-table>
18
+
19
+ <h2>Channels</h2>
20
+ <k-table id="channelsTable" placeholder="Nothing is subscribed to any channel on this process."></k-table>
21
+
22
+ <p class="small tc-muted">
23
+ Sockets are held by the process that accepted them, so with several kempo processes behind a load balancer this
24
+ page shows only the one that answered this request. Messages still reach every process.
25
+ </p>
26
+
27
+ </content>
28
+ <content location="scripts">
29
+ <script type="module">
30
+ import { listRealtimeConnections } from '/kempo/sdk.js';
31
+
32
+ const connectionsTable = document.getElementById('connectionsTable');
33
+ const channelsTable = document.getElementById('channelsTable');
34
+ const scope = document.getElementById('scope');
35
+ const formatTime = value => new Date(value).toLocaleString();
36
+
37
+ const refresh = async () => {
38
+ const [error, data] = await listRealtimeConnections();
39
+
40
+ if(error){
41
+ scope.textContent = error.code === 403
42
+ ? 'You do not have permission to view realtime connections.'
43
+ : `Could not load connections: ${error.msg}`;
44
+ return;
45
+ }
46
+
47
+ scope.textContent = `${data.connections.length} connection${data.connections.length === 1 ? '' : 's'} on process ${data.process}.`;
48
+
49
+ connectionsTable.setData({
50
+ records: data.connections.map(connection => ({
51
+ id: connection.id,
52
+ user: connection.userName || connection.userId,
53
+ path: connection.path,
54
+ channels: connection.channels.join(', ') || 'none',
55
+ connectedAt: formatTime(connection.connectedAt),
56
+ lastActivity: formatTime(connection.lastActivity)
57
+ })),
58
+ fields: [
59
+ { name: 'user', label: 'User' },
60
+ { name: 'path', label: 'Path' },
61
+ { name: 'channels', label: 'Channels' },
62
+ { name: 'connectedAt', label: 'Connected' },
63
+ { name: 'lastActivity', label: 'Last activity' }
64
+ ]
65
+ });
66
+
67
+ channelsTable.setData({
68
+ records: data.channels.map(channel => ({ id: channel.channel, channel: channel.channel, subscribers: channel.subscribers })),
69
+ fields: [
70
+ { name: 'channel', label: 'Channel' },
71
+ { name: 'subscribers', label: 'Subscribers' }
72
+ ]
73
+ });
74
+ };
75
+
76
+ document.getElementById('refresh').addEventListener('click', refresh);
77
+ await refresh();
78
+ setInterval(refresh, 5000);
79
+ </script>
80
+ </content>
81
+ </page>
@@ -0,0 +1,2 @@
1
+ import e from"../../../../server/utils/auth/getSession.js";import r from"../../../../server/utils/realtime/getHub.js";import{TOO_MANY_CONNECTIONS_CODE as n}from"../../../../server/utils/realtime/constants.js";export default async(t,s)=>{const c=t.cookies.session_token,[a,o]=await e({token:c});if(a)return 500===a.code?s.reject(500,"Failed to verify session"):s.reject(401,"Authentication required");const i=r(),u=(e,r)=>s.send(JSON.stringify(e),r);let d=null;s.on("open",()=>{const[e,r]=i.addSubscriber({token:c,user:o.user,path:t.path,deliver:u,close:(e,r)=>s.close(e,r)});e?s.close(429===e.code?n:1011,e.msg):(d=r.id,s.data.userId=o.user.id,u({type:"ready",userId:o.user.id}))}),s.on("message",async(e,r)=>{if(!d)return;let n;try{n=r?null:JSON.parse(e)}catch(e){n=null}if(!n||"object"!=typeof n)return u({type:"error",code:400,msg:"Frames must be JSON objects"});if(i.touch({id:d}),"subscribe"===n.type){const[e]=await i.subscribe({id:d,channel:n.channel,since:n.since});return void(e&&u({type:"error",channel:n.channel,code:e.code,msg:e.msg}))}if("send"===n.type){const[e,r]=await i.handleMessage({id:d,channel:n.channel,data:n.data});if(e)return u({type:"error",channel:n.channel,ref:n.ref,code:e.code,msg:e.msg});if(void 0===n.ref)return;try{return u({type:"ack",channel:n.channel,ref:n.ref,data:r})}catch(e){return u({type:"error",channel:n.channel,ref:n.ref,code:500,msg:"The handler returned something that cannot be sent"})}}if("unsubscribe"===n.type)return i.unsubscribe({id:d,channel:n.channel}),u({type:"unsubscribed",channel:n.channel});u({type:"error",code:400,msg:`Unknown frame type "${n.type}"`})}),s.on("close",e=>{d&&i.removeSubscriber({id:d,reason:e})})};
2
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\kempo\api\realtime\WS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\kempo\\api\\realtime\\WS.js","names":["getSession","getHub","TOO_MANY_CONNECTIONS_CODE","async","request","socket","token","cookies","session_token","error","sessionData","code","reject","hub","send","frame","options","JSON","stringify","subscriberId","on","addError","added","addSubscriber","user","path","deliver","close","reason","msg","id","data","userId","type","isBinary","parse","parseError","touch","subscribeError","subscribe","channel","since","sendError","result","handleMessage","ref","undefined","serializeError","unsubscribe","removeSubscriber"],"sources":["0"],"sourcesContent":["import getSession from '../../../../server/utils/auth/getSession.js';\nimport getHub from '../../../../server/utils/realtime/getHub.js';\nimport { TOO_MANY_CONNECTIONS_CODE } from '../../../../server/utils/realtime/constants.js';\n\n/*\n The realtime socket, served at /kempo/api/realtime.\n\n This is only the HTTP layer: it turns the session cookie into a user, hands frames to the hub, and\n hands the hub's frames back to the socket. Channels, authorization, delivery and replay all live in\n server/utils/realtime.\n\n Client to server: { type: 'subscribe', channel, since? }, { type: 'unsubscribe', channel } and\n { type: 'send', channel, data, ref? }. A send is handed to the channel's own handler; with a `ref` the\n handler's return value comes back as an ack carrying the same ref.\n Server to client: ready, subscribed, unsubscribed, message, direct, ack, gap and error frames.\n*/\nexport default async (request, socket) => {\n const token = request.cookies.session_token;\n const [error, sessionData] = await getSession({ token });\n\n if(error){\n return error.code === 500\n ? socket.reject(500, 'Failed to verify session')\n : socket.reject(401, 'Authentication required');\n }\n\n const hub = getHub();\n const send = (frame, options) => socket.send(JSON.stringify(frame), options);\n let subscriberId = null;\n\n /*\n Registered on open rather than here. This route runs before the handshake completes, so a client\n that disconnects in between would otherwise leave a subscriber that no close event ever removes.\n */\n socket.on('open', () => {\n const [addError, added] = hub.addSubscriber({\n token,\n user: sessionData.user,\n path: request.path,\n deliver: send,\n close: (code, reason) => socket.close(code, reason)\n });\n\n if(addError){\n // The user is at their connection limit: retrying would be refused again, so tell the client to stop\n socket.close(addError.code === 429 ? TOO_MANY_CONNECTIONS_CODE : 1011, addError.msg);\n return;\n }\n\n subscriberId = added.id;\n socket.data.userId = sessionData.user.id;\n send({ type: 'ready', userId: sessionData.user.id });\n });\n\n socket.on('message', async (data, isBinary) => {\n if(!subscriberId) return;\n\n let frame;\n try {\n frame = isBinary ? null : JSON.parse(data);\n } catch(parseError) {\n frame = null;\n }\n if(!frame || typeof frame !== 'object'){\n return send({ type: 'error', code: 400, msg: 'Frames must be JSON objects' });\n }\n\n hub.touch({ id: subscriberId });\n\n if(frame.type === 'subscribe'){\n const [subscribeError] = await hub.subscribe({ id: subscriberId, channel: frame.channel, since: frame.since });\n if(subscribeError){\n send({ type: 'error', channel: frame.channel, code: subscribeError.code, msg: subscribeError.msg });\n }\n return;\n }\n\n if(frame.type === 'send'){\n const [sendError, result] = await hub.handleMessage({ id: subscriberId, channel: frame.channel, data: frame.data });\n\n if(sendError){\n return send({ type: 'error', channel: frame.channel, ref: frame.ref, code: sendError.code, msg: sendError.msg });\n }\n if(frame.ref === undefined) return;\n\n try {\n return send({ type: 'ack', channel: frame.channel, ref: frame.ref, data: result });\n } catch(serializeError) {\n return send({ type: 'error', channel: frame.channel, ref: frame.ref, code: 500, msg: 'The handler returned something that cannot be sent' });\n }\n }\n\n if(frame.type === 'unsubscribe'){\n hub.unsubscribe({ id: subscriberId, channel: frame.channel });\n return send({ type: 'unsubscribed', channel: frame.channel });\n }\n\n send({ type: 'error', code: 400, msg: `Unknown frame type \"${frame.type}\"` });\n });\n\n socket.on('close', (code) => {\n if(subscriberId) hub.removeSubscriber({ id: subscriberId, reason: code });\n });\n};\n"],"mappings":"OAAOA,MAAgB,qDAChBC,MAAY,kFACVC,MAAiC,gEAc3BC,MAAOC,EAASC,KAC7B,MAAMC,EAAQF,EAAQG,QAAQC,eACvBC,EAAOC,SAAqBV,EAAW,CAAEM,UAEhD,GAAGG,EACD,OAAsB,MAAfA,EAAME,KACTN,EAAOO,OAAO,IAAK,4BACnBP,EAAOO,OAAO,IAAK,2BAGzB,MAAMC,EAAMZ,IACNa,EAAO,CAACC,EAAOC,IAAYX,EAAOS,KAAKG,KAAKC,UAAUH,GAAQC,GACpE,IAAIG,EAAe,KAMnBd,EAAOe,GAAG,OAAQ,KAChB,MAAOC,EAAUC,GAAST,EAAIU,cAAc,CAC1CjB,QACAkB,KAAMd,EAAYc,KAClBC,KAAMrB,EAAQqB,KACdC,QAASZ,EACTa,MAAO,CAAChB,EAAMiB,IAAWvB,EAAOsB,MAAMhB,EAAMiB,KAG3CP,EAEDhB,EAAOsB,MAAwB,MAAlBN,EAASV,KAAeT,EAA4B,KAAMmB,EAASQ,MAIlFV,EAAeG,EAAMQ,GACrBzB,EAAO0B,KAAKC,OAAStB,EAAYc,KAAKM,GACtChB,EAAK,CAAEmB,KAAM,QAASD,OAAQtB,EAAYc,KAAKM,QAGjDzB,EAAOe,GAAG,UAAWjB,MAAO4B,EAAMG,KAChC,IAAIf,EAAc,OAElB,IAAIJ,EACJ,IACEA,EAAQmB,EAAW,KAAOjB,KAAKkB,MAAMJ,EACvC,CAAE,MAAMK,GACNrB,EAAQ,IACV,CACA,IAAIA,GAA0B,iBAAVA,EAClB,OAAOD,EAAK,CAAEmB,KAAM,QAAStB,KAAM,IAAKkB,IAAK,gCAK/C,GAFAhB,EAAIwB,MAAM,CAAEP,GAAIX,IAEE,cAAfJ,EAAMkB,KAAqB,CAC5B,MAAOK,SAAwBzB,EAAI0B,UAAU,CAAET,GAAIX,EAAcqB,QAASzB,EAAMyB,QAASC,MAAO1B,EAAM0B,QAItG,YAHGH,GACDxB,EAAK,CAAEmB,KAAM,QAASO,QAASzB,EAAMyB,QAAS7B,KAAM2B,EAAe3B,KAAMkB,IAAKS,EAAeT,MAGjG,CAEA,GAAkB,SAAfd,EAAMkB,KAAgB,CACvB,MAAOS,EAAWC,SAAgB9B,EAAI+B,cAAc,CAAEd,GAAIX,EAAcqB,QAASzB,EAAMyB,QAAST,KAAMhB,EAAMgB,OAE5G,GAAGW,EACD,OAAO5B,EAAK,CAAEmB,KAAM,QAASO,QAASzB,EAAMyB,QAASK,IAAK9B,EAAM8B,IAAKlC,KAAM+B,EAAU/B,KAAMkB,IAAKa,EAAUb,MAE5G,QAAiBiB,IAAd/B,EAAM8B,IAAmB,OAE5B,IACE,OAAO/B,EAAK,CAAEmB,KAAM,MAAOO,QAASzB,EAAMyB,QAASK,IAAK9B,EAAM8B,IAAKd,KAAMY,GAC3E,CAAE,MAAMI,GACN,OAAOjC,EAAK,CAAEmB,KAAM,QAASO,QAASzB,EAAMyB,QAASK,IAAK9B,EAAM8B,IAAKlC,KAAM,IAAKkB,IAAK,sDACvF,CACF,CAEA,GAAkB,gBAAfd,EAAMkB,KAEP,OADApB,EAAImC,YAAY,CAAElB,GAAIX,EAAcqB,QAASzB,EAAMyB,UAC5C1B,EAAK,CAAEmB,KAAM,eAAgBO,QAASzB,EAAMyB,UAGrD1B,EAAK,CAAEmB,KAAM,QAAStB,KAAM,IAAKkB,IAAK,uBAAuBd,EAAMkB,YAGrE5B,EAAOe,GAAG,QAAUT,IACfQ,GAAcN,EAAIoC,iBAAiB,CAAEnB,GAAIX,EAAcS,OAAQjB","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ import s from"../../../../../server/utils/permissions/currentUserHasPermission.js";import r from"../../../../../server/utils/realtime/listConnections.js";export default async(e,t)=>{const o=e.cookies.session_token,[i,n]=await s(o,"system:realtime:read");if(i)return t.status(i.code).json({error:i.msg});if(!n)return t.status(403).json({error:"Insufficient permissions"});const[m,a]=r();if(m)return t.status(m.code).json({error:m.msg});t.json(a)};
2
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\kempo\api\realtime\connections\GET.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\kempo\\api\\realtime\\connections\\GET.js","names":["currentUserHasPermission","listConnections","async","request","response","token","cookies","session_token","permError","hasPermission","status","code","json","error","msg","data"],"sources":["0"],"sourcesContent":["import currentUserHasPermission from '../../../../../server/utils/permissions/currentUserHasPermission.js';\nimport listConnections from '../../../../../server/utils/realtime/listConnections.js';\n\nexport default async (request, response) => {\n const token = request.cookies.session_token;\n const [permError, hasPermission] = await currentUserHasPermission(token, 'system:realtime:read');\n\n if(permError){\n return response.status(permError.code).json({ error: permError.msg });\n }\n\n if(!hasPermission){\n return response.status(403).json({ error: 'Insufficient permissions' });\n }\n\n const [error, data] = listConnections();\n\n if(error){\n return response.status(error.code).json({ error: error.msg });\n }\n\n response.json(data);\n};\n"],"mappings":"OAAOA,MAA8B,6EAC9BC,MAAqB,yEAEbC,MAAOC,EAASC,KAC7B,MAAMC,EAAQF,EAAQG,QAAQC,eACvBC,EAAWC,SAAuBT,EAAyBK,EAAO,wBAEzE,GAAGG,EACD,OAAOJ,EAASM,OAAOF,EAAUG,MAAMC,KAAK,CAAEC,MAAOL,EAAUM,MAGjE,IAAIL,EACF,OAAOL,EAASM,OAAO,KAAKE,KAAK,CAAEC,MAAO,6BAG5C,MAAOA,EAAOE,GAAQd,IAEtB,GAAGY,EACD,OAAOT,EAASM,OAAOG,EAAMF,MAAMC,KAAK,CAAEC,MAAOA,EAAMC,MAGzDV,EAASQ,KAAKG","ignoreList":[]}
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M880-720v480q0 33-23.5 56.5T800-160H160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720ZM160-640h640v-80H160v80Zm0 160v240h640v-240H160Zm0 240v-480 480Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M197-197q-54-55-85.5-127.5T80-480q0-84 31.5-156.5T197-763l57 57q-44 44-69 102t-25 124q0 67 25 125t69 101l-57 57Zm113-113q-32-33-51-76.5T240-480q0-51 19-94.5t51-75.5l57 57q-22 22-34.5 51T320-480q0 33 12.5 62t34.5 51l-57 57Zm113.5-113.5Q400-447 400-480t23.5-56.5Q447-560 480-560t56.5 23.5Q560-513 560-480t-23.5 56.5Q513-400 480-400t-56.5-23.5ZM650-310l-57-57q22-22 34.5-51t12.5-62q0-33-12.5-62T593-593l57-57q32 32 51 75.5t19 94.5q0 50-19 93.5T650-310Zm113 113-57-57q44-44 69-102t25-124q0-67-25-125t-69-101l57-57q54 54 85.5 126.5T880-480q0 83-31.5 155.5T763-197Z"/></svg>
@@ -0,0 +1,2 @@
1
+ const e=1001,t=async()=>{try{const e=await fetch("/kempo/api/auth/session");if(!e.ok)return!0;const t=await e.json();return Boolean(t.user)}catch(e){return!0}};export class RealtimeClient{#e;#t;#s;#i;#n;#r;#o=null;#a="idle";#d=0;#c=null;#h=!1;#l=new Map;#u={status:new Set,ready:new Set,direct:new Set};#p=new Map;#f=1;userId=null;constructor({url:e,WebSocket:s=globalThis.WebSocket,checkSession:i=t,backoff:n={},random:r=Math.random}={}){this.#e=e,this.#t=s,this.#s=i,this.#i=n.base??1e3,this.#n=n.max??3e4,this.#r=r}get status(){return this.#a}onStatus=e=>(this.#u.status.add(e),()=>this.#u.status.delete(e));onReady=e=>(this.#u.ready.add(e),this.userId&&e(this.userId),()=>this.#u.ready.delete(e));onDirect=e=>(this.#u.direct.add(e),()=>this.#u.direct.delete(e));send=(e,t,{timeout:s=1e4}={})=>new Promise((i,n)=>{if(1!==this.#o?.readyState)return void n({code:503,msg:"Not connected"});const r=this.#f++,o=setTimeout(()=>{this.#p.delete(r),n({code:504,msg:"No reply from the server"})},s);this.#p.set(r,{resolve:i,reject:n,timer:o}),this.#m({type:"send",channel:e,data:t,ref:r})});subscribe=(e,t,{since:s,onGap:i,onError:n}={})=>{let r=this.#l.get(e);return r||(r={handlers:new Set,gapHandlers:new Set,errorHandlers:new Set,lastId:s??null,failed:!1},this.#l.set(e,r)),r.handlers.add(t),i&&r.gapHandlers.add(i),n&&r.errorHandlers.add(n),1===r.handlers.size&&this.#g(e,r),()=>{r.handlers.delete(t),i&&r.gapHandlers.delete(i),n&&r.errorHandlers.delete(n),r.handlers.size||(this.#l.delete(e),this.#m({type:"unsubscribe",channel:e}))}};start=()=>{this.#o||this.#h||this.#y()};close=()=>{this.#h=!0,clearTimeout(this.#c),this.#c=null,this.#S({code:503,msg:"Client closed"}),this.#b("closed"),this.#o?.close(1e3),this.#o=null};#y=()=>{this.#b(this.#d>0?"reconnecting":"connecting");let e=!1,t=!1;const s=new this.#t(this.#e??`${"https:"===location.protocol?"wss:":"ws:"}//${location.host}/kempo/api/realtime`);this.#o=s;const i=i=>{t||(t=!0,this.#o===s&&(this.#o=null),this.#S({code:503,msg:"Connection lost"}),this.#k(i,e))};s.addEventListener("open",()=>{e=!0,this.#d=0,this.#b("open");for(const[e,t]of this.#l)this.#g(e,t)}),s.addEventListener("message",e=>this.#w(e.data)),s.addEventListener("close",e=>i(e.code)),s.addEventListener("error",()=>{e||i(1006)})};#k=async(t,s)=>{if(this.#h)return;if(4401===t)return this.#h=!0,void this.#b("unauthenticated");if(4429===t)return this.#h=!0,void this.#b("refused");if(!s&&t!==e){const e=await this.#s();if(this.#h)return;if(!1===e)return this.#h=!0,void this.#b("unauthenticated")}this.#b("reconnecting"),this.#d+=t===e?0:1;const i=Math.min(this.#n,this.#i*2**Math.max(0,this.#d-1)),n=t===e?Math.min(250,this.#i):i*(.5+.5*this.#r());this.#c=setTimeout(()=>{this.#c=null,this.#h||this.#y()},n)};#g=(e,t)=>{if(t.failed)return;const s={type:"subscribe",channel:e};null!==t.lastId&&(s.since=t.lastId),this.#m(s)};#m=e=>{1===this.#o?.readyState&&this.#o.send(JSON.stringify(e))};#w=e=>{let t;try{t=JSON.parse(e)}catch(e){return}if("ready"===t.type){this.userId=t.userId;for(const e of this.#u.ready)e(t.userId);return}if("direct"===t.type){for(const e of this.#u.direct)this.#v(()=>e(t.data));return}if(("ack"===t.type||"error"===t.type)&&void 0!==t.ref){const e=this.#p.get(t.ref);if(!e)return;return this.#p.delete(t.ref),clearTimeout(e.timer),void("ack"===t.type?e.resolve(t.data):e.reject({code:t.code,msg:t.msg}))}const s=this.#l.get(t.channel);if(s)if("message"!==t.type)if("gap"!==t.type){if("error"===t.type){s.failed=!0;for(const e of s.errorHandlers)this.#v(()=>e({channel:t.channel,code:t.code,msg:t.msg}))}}else for(const e of s.gapHandlers)this.#v(()=>e({channel:t.channel}));else{void 0!==t.id&&(s.lastId=t.id);for(const e of s.handlers)this.#v(()=>e(t.data,{channel:t.channel,id:t.id}))}};#S=e=>{for(const[t,s]of this.#p)clearTimeout(s.timer),s.reject(e),this.#p.delete(t)};#v=e=>{try{e()}catch(e){console.error("[kempo realtime] a handler threw:",e)}};#b=e=>{if(this.#a!==e){this.#a=e;for(const t of this.#u.status)this.#v(()=>t(e))}}}export const connect=e=>{const t=new RealtimeClient(e);return t.start(),t};
2
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\kempo\realtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\kempo\\realtime.js","names":["GOING_AWAY","defaultCheckSession","async","response","fetch","ok","body","json","Boolean","user","error","RealtimeClient","url","WebSocketImpl","checkSession","base","max","random","socket","status","attempts","timer","stopped","subscriptions","Map","listeners","Set","ready","direct","pending","nextRef","userId","constructor","WebSocket","globalThis","backoff","Math","this","onStatus","listener","add","delete","onReady","onDirect","send","channel","data","timeout","Promise","resolve","reject","readyState","code","msg","ref","setTimeout","set","type","subscribe","handler","since","onGap","onError","subscription","get","handlers","gapHandlers","errorHandlers","lastId","failed","size","sendSubscribe","start","open","close","clearTimeout","rejectPending","setStatus","opened","finished","location","protocol","host","finish","closed","addEventListener","event","received","signedIn","exponential","min","delay","frame","JSON","stringify","raw","parse","safely","undefined","id","fn","console","connect","options","client"],"sources":["0"],"sourcesContent":["/*\n Browser client for kempo's realtime socket.\n\n import { connect } from '/kempo/realtime.js';\n\n const realtime = connect();\n const stop = realtime.subscribe('my-extension:status', (data, { id }) => { ... }, {\n onGap: () => refetchEverything()\n });\n\n // Send to the channel's handler on the server, and wait for its reply\n const reply = await realtime.send('my-extension:status', { action: 'ping' });\n\n A browser does not reconnect a WebSocket that closes, so this does: with backoff, resubscribing to\n every channel and asking for what it missed since the last message it saw. It stops for good when the\n session has ended, since retrying could never succeed.\n*/\n\nconst DEFAULT_PATH = '/kempo/api/realtime';\nconst SESSION_ENDED = 4401;\nconst TOO_MANY_CONNECTIONS = 4429;\nconst GOING_AWAY = 1001;\n\nconst defaultUrl = () => `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}${DEFAULT_PATH}`;\n\n/*\n Distinguishes \"signed out\" from \"the network is down\". A failed handshake looks identical either way\n to a browser (a bare close with no status), so this asks the session endpoint. Only a definite answer\n that there is no session stops the retrying; anything unclear keeps trying.\n*/\nconst defaultCheckSession = async () => {\n try {\n const response = await fetch('/kempo/api/auth/session');\n if(!response.ok) return true;\n const body = await response.json();\n return Boolean(body.user);\n } catch(error) {\n return true;\n }\n};\n\nexport class RealtimeClient {\n #url;\n #WebSocketImpl;\n #checkSession;\n #base;\n #max;\n #random;\n #socket = null;\n #status = 'idle';\n #attempts = 0;\n #timer = null;\n #stopped = false;\n #subscriptions = new Map();\n #listeners = { status: new Set(), ready: new Set(), direct: new Set() };\n #pending = new Map();\n #nextRef = 1;\n\n userId = null;\n\n constructor({\n url,\n WebSocket: WebSocketImpl = globalThis.WebSocket,\n checkSession = defaultCheckSession,\n backoff = {},\n random = Math.random\n } = {}){\n this.#url = url;\n this.#WebSocketImpl = WebSocketImpl;\n this.#checkSession = checkSession;\n this.#base = backoff.base ?? 1000;\n this.#max = backoff.max ?? 30000;\n this.#random = random;\n }\n\n /*\n State\n */\n\n get status(){\n return this.#status;\n }\n\n onStatus = (listener) => {\n this.#listeners.status.add(listener);\n return () => this.#listeners.status.delete(listener);\n };\n\n onReady = (listener) => {\n this.#listeners.ready.add(listener);\n if(this.userId) listener(this.userId);\n return () => this.#listeners.ready.delete(listener);\n };\n\n // Messages the server sent to this connection alone, rather than to a channel\n onDirect = (listener) => {\n this.#listeners.direct.add(listener);\n return () => this.#listeners.direct.delete(listener);\n };\n\n /*\n Sending\n\n A message goes to the channel's handler on the server, and the promise settles with what the handler\n returned, or rejects with { code, msg }. It is not queued while disconnected: a message a game or an\n editor sends is stale by the time the connection comes back, so the caller is told it did not go.\n */\n\n send = (channel, data, { timeout = 10000 } = {}) => new Promise((resolve, reject) => {\n if(this.#socket?.readyState !== 1){\n reject({ code: 503, msg: 'Not connected' });\n return;\n }\n\n const ref = this.#nextRef++;\n const timer = setTimeout(() => {\n this.#pending.delete(ref);\n reject({ code: 504, msg: 'No reply from the server' });\n }, timeout);\n\n this.#pending.set(ref, { resolve, reject, timer });\n this.#send({ type: 'send', channel, data, ref });\n });\n\n /*\n Subscriptions\n\n Several handlers can share one channel; the server is only told when the first arrives and when the\n last leaves. `since` lets a caller that remembers the last id it processed (across a page load, say)\n resume from there rather than from now.\n */\n\n subscribe = (channel, handler, { since, onGap, onError } = {}) => {\n let subscription = this.#subscriptions.get(channel);\n\n if(!subscription){\n subscription = { handlers: new Set(), gapHandlers: new Set(), errorHandlers: new Set(), lastId: since ?? null, failed: false };\n this.#subscriptions.set(channel, subscription);\n }\n\n subscription.handlers.add(handler);\n if(onGap) subscription.gapHandlers.add(onGap);\n if(onError) subscription.errorHandlers.add(onError);\n\n // A repeat handler on a channel the server already confirmed needs no frame; a first one does\n if(subscription.handlers.size === 1) this.#sendSubscribe(channel, subscription);\n\n return () => {\n subscription.handlers.delete(handler);\n if(onGap) subscription.gapHandlers.delete(onGap);\n if(onError) subscription.errorHandlers.delete(onError);\n\n if(!subscription.handlers.size){\n this.#subscriptions.delete(channel);\n this.#send({ type: 'unsubscribe', channel });\n }\n };\n };\n\n /*\n Lifecycle\n */\n\n start = () => {\n if(this.#socket || this.#stopped) return;\n this.#open();\n };\n\n close = () => {\n this.#stopped = true;\n clearTimeout(this.#timer);\n this.#timer = null;\n this.#rejectPending({ code: 503, msg: 'Client closed' });\n this.#setStatus('closed');\n this.#socket?.close(1000);\n this.#socket = null;\n };\n\n #open = () => {\n this.#setStatus(this.#attempts > 0 ? 'reconnecting' : 'connecting');\n\n let opened = false;\n let finished = false;\n const socket = new this.#WebSocketImpl(this.#url ?? defaultUrl());\n this.#socket = socket;\n\n // One attempt ends once, however many of error and close report it\n const finish = (code) => {\n if(finished) return;\n finished = true;\n if(this.#socket === socket) this.#socket = null;\n this.#rejectPending({ code: 503, msg: 'Connection lost' });\n this.#closed(code, opened);\n };\n\n socket.addEventListener('open', () => {\n opened = true;\n this.#attempts = 0;\n this.#setStatus('open');\n for(const [channel, subscription] of this.#subscriptions){\n this.#sendSubscribe(channel, subscription);\n }\n });\n\n socket.addEventListener('message', event => this.#received(event.data));\n\n socket.addEventListener('close', event => finish(event.code));\n\n /*\n A browser follows a failed connection with a close event; Node's WebSocket sends only the error. A\n client that waited for close would then never retry, so a failure before the socket opened ends the\n attempt on its own. After it opened, an error is always followed by a close carrying the real code.\n */\n socket.addEventListener('error', () => {\n if(!opened) finish(1006);\n });\n };\n\n #closed = async (code, opened) => {\n if(this.#stopped) return;\n\n if(code === SESSION_ENDED){\n this.#stopped = true;\n this.#setStatus('unauthenticated');\n return;\n }\n\n // The user is at their connection limit; reconnecting would only be refused again\n if(code === TOO_MANY_CONNECTIONS){\n this.#stopped = true;\n this.#setStatus('refused');\n return;\n }\n\n /*\n A handshake that never completed is either a network problem or a refused session, and the browser\n cannot say which, so ask. A restart (1001) is neither, so it skips the check and comes straight back.\n */\n if(!opened && code !== GOING_AWAY){\n const signedIn = await this.#checkSession();\n if(this.#stopped) return;\n if(signedIn === false){\n this.#stopped = true;\n this.#setStatus('unauthenticated');\n return;\n }\n }\n\n this.#setStatus('reconnecting');\n this.#attempts += code === GOING_AWAY ? 0 : 1;\n\n const exponential = Math.min(this.#max, this.#base * 2 ** Math.max(0, this.#attempts - 1));\n // Jitter, so a restart does not bring every client back in the same instant\n const delay = code === GOING_AWAY ? Math.min(250, this.#base) : exponential * (0.5 + this.#random() * 0.5);\n\n this.#timer = setTimeout(() => {\n this.#timer = null;\n if(!this.#stopped) this.#open();\n }, delay);\n };\n\n /*\n Frames\n */\n\n #sendSubscribe = (channel, subscription) => {\n if(subscription.failed) return;\n const frame = { type: 'subscribe', channel };\n if(subscription.lastId !== null) frame.since = subscription.lastId;\n this.#send(frame);\n };\n\n #send = (frame) => {\n if(this.#socket?.readyState === 1) this.#socket.send(JSON.stringify(frame));\n };\n\n #received = (raw) => {\n let frame;\n try {\n frame = JSON.parse(raw);\n } catch(error) {\n return;\n }\n\n if(frame.type === 'ready'){\n this.userId = frame.userId;\n for(const listener of this.#listeners.ready) listener(frame.userId);\n return;\n }\n\n if(frame.type === 'direct'){\n for(const listener of this.#listeners.direct) this.#safely(() => listener(frame.data));\n return;\n }\n\n // A reply to a send(), matched by its ref before anything is routed by channel\n if((frame.type === 'ack' || frame.type === 'error') && frame.ref !== undefined){\n const pending = this.#pending.get(frame.ref);\n if(!pending) return;\n this.#pending.delete(frame.ref);\n clearTimeout(pending.timer);\n if(frame.type === 'ack') pending.resolve(frame.data);\n else pending.reject({ code: frame.code, msg: frame.msg });\n return;\n }\n\n const subscription = this.#subscriptions.get(frame.channel);\n if(!subscription) return;\n\n if(frame.type === 'message'){\n if(frame.id !== undefined) subscription.lastId = frame.id;\n for(const handler of subscription.handlers) this.#safely(() => handler(frame.data, { channel: frame.channel, id: frame.id }));\n return;\n }\n\n if(frame.type === 'gap'){\n for(const handler of subscription.gapHandlers) this.#safely(() => handler({ channel: frame.channel }));\n return;\n }\n\n if(frame.type === 'error'){\n // A refused subscription would be refused again on every reconnect, so it is not retried\n subscription.failed = true;\n for(const handler of subscription.errorHandlers) this.#safely(() => handler({ channel: frame.channel, code: frame.code, msg: frame.msg }));\n }\n };\n\n #rejectPending = (error) => {\n for(const [ref, pending] of this.#pending){\n clearTimeout(pending.timer);\n pending.reject(error);\n this.#pending.delete(ref);\n }\n };\n\n // One handler throwing must not stop the others, or the frames behind it\n #safely = (fn) => {\n try {\n fn();\n } catch(error) {\n console.error('[kempo realtime] a handler threw:', error);\n }\n };\n\n #setStatus = (status) => {\n if(this.#status === status) return;\n this.#status = status;\n for(const listener of this.#listeners.status) this.#safely(() => listener(status));\n };\n}\n\nexport const connect = (options) => {\n const client = new RealtimeClient(options);\n client.start();\n return client;\n};\n"],"mappings":"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","ignoreList":[]}
package/dist/kempo/sdk.js CHANGED
@@ -1,2 +1,2 @@
1
- import{get as e,post as s,put as o,patch as n,del as t}from"./fetch.js";const r="/kempo/api";export const register=async({email:e,password:o,name:n})=>s(`${r}/auth/register/email`,{email:e,password:o,name:n});export const login=async({email:e,password:o})=>s(`${r}/auth/login/email`,{email:e,password:o});export const logout=async()=>s(`${r}/auth/logout`,{});export const getSession=async()=>e(`${r}/auth/session`);export const forgotPassword=async({email:e})=>s(`${r}/forgot-password`,{email:e});export const resetPassword=async({token:e,password:o,logoutAll:n=!1})=>s(`${r}/auth/reset-password`,{token:e,newPassword:o,logoutAll:n});export const changePassword=async({currentPassword:e,newPassword:o,logoutAll:n})=>s(`${r}/auth/change-password`,{currentPassword:e,newPassword:o,logoutAll:n});export const verifyEmail=async({token:e})=>s(`${r}/auth/verify-email`,{token:e});export const currentUserHasPermission=async({permission:s})=>e(`${r}/user/current/permissions`,{permission:s});export const currentUserHasAllPermissions=async({permissions:s})=>e(`${r}/user/current/permissions/all`,{permissions:s});export const currentUserHasSomePermissions=async({permissions:s})=>e(`${r}/user/current/permissions/some`,{permissions:s});export const getAllCurrentUserPermissions=async()=>e(`${r}/user/current/permissions/list`);export const userHasPermission=async({userid:s,permission:o})=>e(`${r}/user/${s}/permissions`,{permission:o});export const userHasAllPermissions=async({userid:s,permissions:o})=>e(`${r}/user/${s}/permissions/all`,{permissions:o});export const userHasSomePermissions=async({userid:s,permissions:o})=>e(`${r}/user/${s}/permissions/some`,{permissions:o});export const getAllUserPermissions=async({userid:s})=>e(`${r}/user/${s}/permissions/list`);export const getUsers=async({limit:s,offset:o}={})=>e(`${r}/user`,{limit:s,offset:o});export const searchUsers=async({q:s,limit:o,offset:n,inGroup:t,notInGroup:a}={})=>e(`${r}/user/search`,{q:s,limit:o,offset:n,inGroup:t,notInGroup:a});export const createUser=async({name:e,email:o,password:n,emailVerified:t})=>s(`${r}/user/create`,{name:e,email:o,password:n,emailVerified:t});export const deleteUsers=async e=>t(`${r}/user`,{ids:e});export const updateUser=async({id:e,name:s,email:o,emailVerified:t,createdAt:a})=>n(`${r}/user`,{id:e,name:s,email:o,emailVerified:t,createdAt:a});export const getUser=async s=>e(`${r}/user/${s}`);export const getUserSessions=async(s,{limit:o,offset:n}={})=>e(`${r}/user/${s}/sessions`,{limit:o,offset:n});export const deleteUserSession=async(e,s)=>t(`${r}/user/${e}/sessions`,{sessionToken:s});export const deleteExpiredUserSessions=async e=>t(`${r}/user/${e}/sessions/expired`);export const getUserGroups=async(s,{limit:o,offset:n}={})=>e(`${r}/user/${s}/groups`,{limit:o,offset:n});export const addUserToGroup=async(e,o)=>s(`${r}/user/${e}/groups`,{groupName:o});export const removeUserFromGroup=async(e,s)=>t(`${r}/user/${e}/groups`,{groupName:s});export const listGroups=async({limit:s,offset:o,owner:n}={})=>e(`${r}/groups`,{limit:s,offset:o,owner:n});export const createGroup=async({name:e,description:o})=>s(`${r}/groups`,{name:e,description:o});export const updateGroup=async({name:e,newName:s,description:o})=>n(`${r}/groups`,{name:e,newName:s,description:o});export const deleteGroups=async e=>t(`${r}/groups`,{names:e});export const getGroup=async s=>e(`${r}/groups/${encodeURIComponent(s)}`);export const getGroupMembers=async(s,{limit:o,offset:n}={})=>e(`${r}/groups/${encodeURIComponent(s)}/members`,{limit:o,offset:n});export const addMemberToGroup=async(e,o)=>s(`${r}/groups/${encodeURIComponent(e)}/members`,{userId:o});export const removeMemberFromGroup=async(e,s)=>t(`${r}/groups/${encodeURIComponent(e)}/members`,{userId:s});export const getGroupPermissions=async s=>e(`${r}/groups/${encodeURIComponent(s)}/permissions`);export const addPermissionToGroup=async(e,o)=>s(`${r}/groups/${encodeURIComponent(e)}/permissions`,{permissionName:o});export const removePermissionFromGroup=async(e,s)=>t(`${r}/groups/${encodeURIComponent(e)}/permissions`,{permissionName:s});export const listPermissions=async({limit:s,offset:o,owner:n}={})=>e(`${r}/permissions`,{limit:s,offset:o,owner:n});export const createPermissions=async e=>s(`${r}/permissions`,{permissions:e});export const updatePermission=async({name:e,description:s})=>n(`${r}/permissions`,{name:e,description:s});export const deletePermissions=async e=>t(`${r}/permissions`,{names:e});export const getSetting=async(s,o)=>e(`${r}/settings/${encodeURIComponent(s)}/${encodeURIComponent(o)}`);export const listSettings=async({owner:s,limit:o,offset:n}={})=>e(`${r}/settings/${encodeURIComponent(s)}`,{limit:o,offset:n});export const setSetting=async(e,o,n)=>s(`${r}/settings/${encodeURIComponent(e)}/${encodeURIComponent(o)}`,n);export const deleteSetting=async(e,s)=>t(`${r}/settings/${encodeURIComponent(e)}/${encodeURIComponent(s)}`);export const listPages=async({owner:s,showHidden:o,offset:n,limit:t}={})=>e(`${r}/pages`,{owner:s,showHidden:o,offset:n,limit:t});export const rescanPages=async()=>s(`${r}/pages/rescan`,{});export const listTemplates=async()=>e(`${r}/templates`);export const listDirectories=async()=>e(`${r}/pages/directories`);export const getPage=async s=>e(`${r}/pages/file`,{file:s});export const createPage=async({directory:e,name:o,template:n})=>s(`${r}/pages`,{directory:e,name:o,template:n});export const updatePage=async({file:e,name:s,title:n,description:t,author:a,template:i,contents:c,extraMetadata:l})=>o(`${r}/pages/file`,{file:e,name:s,title:n,description:t,author:a,template:i,contents:c,extraMetadata:l});export const deletePages=async e=>t(`${r}/pages`,{files:e});export const disablePage=async e=>o(`${r}/pages/disable`,{file:e});export const enablePage=async e=>o(`${r}/pages/enable`,{file:e});export const movePage=async({file:e,newFile:s})=>n(`${r}/pages/file`,{file:e,newFile:s});export const searchPagesByMetadata=async s=>e(`${r}/pages/search`,{query:JSON.stringify(s)});export const setPageLocked=async({file:e,locked:s})=>o(`${r}/pages/lock`,{file:e,locked:s});export const getTemplate=async s=>e(`${r}/templates/file`,{file:s});export const createTemplate=async({directory:e,name:o,copyFrom:n})=>s(`${r}/templates`,{directory:e,name:o,copyFrom:n});export const updateTemplate=async({file:e,name:s,author:n,markup:t})=>o(`${r}/templates/file`,{file:e,name:s,author:n,markup:t});export const deleteTemplates=async e=>t(`${r}/templates`,{files:e});export const disableTemplate=async e=>o(`${r}/templates/disable`,{file:e});export const enableTemplate=async e=>o(`${r}/templates/enable`,{file:e});export const moveTemplate=async({file:e,newFile:s})=>n(`${r}/templates/file`,{file:e,newFile:s});export const setTemplateLocked=async({file:e,locked:s})=>o(`${r}/templates/lock`,{file:e,locked:s});export const listFragments=async()=>e(`${r}/fragments`);export const getFragment=async s=>e(`${r}/fragments/file`,{file:s});export const createFragment=async({directory:e,name:o})=>s(`${r}/fragments`,{directory:e,name:o});export const updateFragment=async({file:e,author:s,markup:n})=>o(`${r}/fragments/file`,{file:e,author:s,markup:n});export const deleteFragments=async e=>t(`${r}/fragments`,{files:e});export const moveFragment=async({file:e,newFile:s})=>n(`${r}/fragments/file`,{file:e,newFile:s});export const disableFragment=async e=>o(`${r}/fragments/disable`,{file:e});export const enableFragment=async e=>o(`${r}/fragments/enable`,{file:e});export const setFragmentLocked=async({file:e,locked:s})=>o(`${r}/fragments/lock`,{file:e,locked:s});export const listGlobalContent=async()=>e(`${r}/globals`);export const getGlobalContent=async s=>e(`${r}/globals/entry`,{id:s});export const createGlobalContent=async({name:e,location:o,priority:n})=>s(`${r}/globals`,{name:e,location:o,priority:n});export const updateGlobalContent=async({id:e,name:s,location:n,priority:t,markup:a})=>o(`${r}/globals/entry`,{id:e,name:s,location:n,priority:t,markup:a});export const deleteGlobalContent=async e=>t(`${r}/globals`,{ids:e});export const disableGlobalContent=async e=>o(`${r}/globals/disable`,{id:e});export const enableGlobalContent=async e=>o(`${r}/globals/enable`,{id:e});export const setGlobalContentLocked=async({id:e,locked:s})=>o(`${r}/globals/lock`,{id:e,locked:s});export const listExtensions=async({limit:s,offset:o}={})=>e(`${r}/extensions`,{limit:s,offset:o});export const listAvailableExtensions=async()=>e(`${r}/extensions/available`);export const listKnownExtensions=async()=>e(`${r}/extensions/known`);export const installExtension=async e=>s(`${r}/extensions`,{name:e});export const uninstallExtension=async(e,s=!1)=>t(`${r}/extensions`,{name:e,purgeData:s});export const enableExtension=async e=>s(`${r}/extensions/${encodeURIComponent(e)}/enable`,{});export const disableExtension=async e=>s(`${r}/extensions/${encodeURIComponent(e)}/disable`,{});export const listAdminGlobalContent=async()=>e(`${r}/admin-globals`);export const getAdminGlobalContent=async s=>e(`${r}/admin-globals/${encodeURIComponent(s)}`);export const createAdminGlobalContent=async({name:e,location:o,priority:n,owner:t,markup:a})=>s(`${r}/admin-globals`,{name:e,location:o,priority:n,owner:t,markup:a});export const updateAdminGlobalContent=async(e,{name:s,location:n,priority:t,markup:a})=>o(`${r}/admin-globals/${encodeURIComponent(e)}`,{name:s,location:n,priority:t,markup:a});export const deleteAdminGlobalContent=async e=>t(`${r}/admin-globals`,{ids:e});export const enableAdminGlobalContent=async e=>o(`${r}/admin-globals/${encodeURIComponent(e)}/enable`,{});export const disableAdminGlobalContent=async e=>o(`${r}/admin-globals/${encodeURIComponent(e)}/disable`,{});
1
+ import{get as e,post as s,put as o,patch as n,del as t}from"./fetch.js";const r="/kempo/api";export const register=async({email:e,password:o,name:n})=>s(`${r}/auth/register/email`,{email:e,password:o,name:n});export const login=async({email:e,password:o})=>s(`${r}/auth/login/email`,{email:e,password:o});export const logout=async()=>s(`${r}/auth/logout`,{});export const getSession=async()=>e(`${r}/auth/session`);export const forgotPassword=async({email:e})=>s(`${r}/forgot-password`,{email:e});export const resetPassword=async({token:e,password:o,logoutAll:n=!1})=>s(`${r}/auth/reset-password`,{token:e,newPassword:o,logoutAll:n});export const changePassword=async({currentPassword:e,newPassword:o,logoutAll:n})=>s(`${r}/auth/change-password`,{currentPassword:e,newPassword:o,logoutAll:n});export const verifyEmail=async({token:e})=>s(`${r}/auth/verify-email`,{token:e});export const currentUserHasPermission=async({permission:s})=>e(`${r}/user/current/permissions`,{permission:s});export const currentUserHasAllPermissions=async({permissions:s})=>e(`${r}/user/current/permissions/all`,{permissions:s});export const currentUserHasSomePermissions=async({permissions:s})=>e(`${r}/user/current/permissions/some`,{permissions:s});export const getAllCurrentUserPermissions=async()=>e(`${r}/user/current/permissions/list`);export const userHasPermission=async({userid:s,permission:o})=>e(`${r}/user/${s}/permissions`,{permission:o});export const userHasAllPermissions=async({userid:s,permissions:o})=>e(`${r}/user/${s}/permissions/all`,{permissions:o});export const userHasSomePermissions=async({userid:s,permissions:o})=>e(`${r}/user/${s}/permissions/some`,{permissions:o});export const getAllUserPermissions=async({userid:s})=>e(`${r}/user/${s}/permissions/list`);export const getUsers=async({limit:s,offset:o}={})=>e(`${r}/user`,{limit:s,offset:o});export const searchUsers=async({q:s,limit:o,offset:n,inGroup:t,notInGroup:a}={})=>e(`${r}/user/search`,{q:s,limit:o,offset:n,inGroup:t,notInGroup:a});export const createUser=async({name:e,email:o,password:n,emailVerified:t})=>s(`${r}/user/create`,{name:e,email:o,password:n,emailVerified:t});export const deleteUsers=async e=>t(`${r}/user`,{ids:e});export const updateUser=async({id:e,name:s,email:o,emailVerified:t,createdAt:a})=>n(`${r}/user`,{id:e,name:s,email:o,emailVerified:t,createdAt:a});export const getUser=async s=>e(`${r}/user/${s}`);export const getUserSessions=async(s,{limit:o,offset:n}={})=>e(`${r}/user/${s}/sessions`,{limit:o,offset:n});export const deleteUserSession=async(e,s)=>t(`${r}/user/${e}/sessions`,{sessionToken:s});export const deleteExpiredUserSessions=async e=>t(`${r}/user/${e}/sessions/expired`);export const getUserGroups=async(s,{limit:o,offset:n}={})=>e(`${r}/user/${s}/groups`,{limit:o,offset:n});export const addUserToGroup=async(e,o)=>s(`${r}/user/${e}/groups`,{groupName:o});export const removeUserFromGroup=async(e,s)=>t(`${r}/user/${e}/groups`,{groupName:s});export const listGroups=async({limit:s,offset:o,owner:n}={})=>e(`${r}/groups`,{limit:s,offset:o,owner:n});export const createGroup=async({name:e,description:o})=>s(`${r}/groups`,{name:e,description:o});export const updateGroup=async({name:e,newName:s,description:o})=>n(`${r}/groups`,{name:e,newName:s,description:o});export const deleteGroups=async e=>t(`${r}/groups`,{names:e});export const getGroup=async s=>e(`${r}/groups/${encodeURIComponent(s)}`);export const getGroupMembers=async(s,{limit:o,offset:n}={})=>e(`${r}/groups/${encodeURIComponent(s)}/members`,{limit:o,offset:n});export const addMemberToGroup=async(e,o)=>s(`${r}/groups/${encodeURIComponent(e)}/members`,{userId:o});export const removeMemberFromGroup=async(e,s)=>t(`${r}/groups/${encodeURIComponent(e)}/members`,{userId:s});export const getGroupPermissions=async s=>e(`${r}/groups/${encodeURIComponent(s)}/permissions`);export const addPermissionToGroup=async(e,o)=>s(`${r}/groups/${encodeURIComponent(e)}/permissions`,{permissionName:o});export const removePermissionFromGroup=async(e,s)=>t(`${r}/groups/${encodeURIComponent(e)}/permissions`,{permissionName:s});export const listPermissions=async({limit:s,offset:o,owner:n}={})=>e(`${r}/permissions`,{limit:s,offset:o,owner:n});export const createPermissions=async e=>s(`${r}/permissions`,{permissions:e});export const updatePermission=async({name:e,description:s})=>n(`${r}/permissions`,{name:e,description:s});export const deletePermissions=async e=>t(`${r}/permissions`,{names:e});export const getSetting=async(s,o)=>e(`${r}/settings/${encodeURIComponent(s)}/${encodeURIComponent(o)}`);export const listSettings=async({owner:s,limit:o,offset:n}={})=>e(`${r}/settings/${encodeURIComponent(s)}`,{limit:o,offset:n});export const setSetting=async(e,o,n)=>s(`${r}/settings/${encodeURIComponent(e)}/${encodeURIComponent(o)}`,n);export const deleteSetting=async(e,s)=>t(`${r}/settings/${encodeURIComponent(e)}/${encodeURIComponent(s)}`);export const listPages=async({owner:s,showHidden:o,offset:n,limit:t}={})=>e(`${r}/pages`,{owner:s,showHidden:o,offset:n,limit:t});export const rescanPages=async()=>s(`${r}/pages/rescan`,{});export const listTemplates=async()=>e(`${r}/templates`);export const listDirectories=async()=>e(`${r}/pages/directories`);export const getPage=async s=>e(`${r}/pages/file`,{file:s});export const createPage=async({directory:e,name:o,template:n})=>s(`${r}/pages`,{directory:e,name:o,template:n});export const updatePage=async({file:e,name:s,title:n,description:t,author:a,template:i,contents:c,extraMetadata:l})=>o(`${r}/pages/file`,{file:e,name:s,title:n,description:t,author:a,template:i,contents:c,extraMetadata:l});export const deletePages=async e=>t(`${r}/pages`,{files:e});export const disablePage=async e=>o(`${r}/pages/disable`,{file:e});export const enablePage=async e=>o(`${r}/pages/enable`,{file:e});export const movePage=async({file:e,newFile:s})=>n(`${r}/pages/file`,{file:e,newFile:s});export const searchPagesByMetadata=async s=>e(`${r}/pages/search`,{query:JSON.stringify(s)});export const setPageLocked=async({file:e,locked:s})=>o(`${r}/pages/lock`,{file:e,locked:s});export const getTemplate=async s=>e(`${r}/templates/file`,{file:s});export const createTemplate=async({directory:e,name:o,copyFrom:n})=>s(`${r}/templates`,{directory:e,name:o,copyFrom:n});export const updateTemplate=async({file:e,name:s,author:n,markup:t})=>o(`${r}/templates/file`,{file:e,name:s,author:n,markup:t});export const deleteTemplates=async e=>t(`${r}/templates`,{files:e});export const disableTemplate=async e=>o(`${r}/templates/disable`,{file:e});export const enableTemplate=async e=>o(`${r}/templates/enable`,{file:e});export const moveTemplate=async({file:e,newFile:s})=>n(`${r}/templates/file`,{file:e,newFile:s});export const setTemplateLocked=async({file:e,locked:s})=>o(`${r}/templates/lock`,{file:e,locked:s});export const listFragments=async()=>e(`${r}/fragments`);export const getFragment=async s=>e(`${r}/fragments/file`,{file:s});export const createFragment=async({directory:e,name:o})=>s(`${r}/fragments`,{directory:e,name:o});export const updateFragment=async({file:e,author:s,markup:n})=>o(`${r}/fragments/file`,{file:e,author:s,markup:n});export const deleteFragments=async e=>t(`${r}/fragments`,{files:e});export const moveFragment=async({file:e,newFile:s})=>n(`${r}/fragments/file`,{file:e,newFile:s});export const disableFragment=async e=>o(`${r}/fragments/disable`,{file:e});export const enableFragment=async e=>o(`${r}/fragments/enable`,{file:e});export const setFragmentLocked=async({file:e,locked:s})=>o(`${r}/fragments/lock`,{file:e,locked:s});export const listGlobalContent=async()=>e(`${r}/globals`);export const getGlobalContent=async s=>e(`${r}/globals/entry`,{id:s});export const createGlobalContent=async({name:e,location:o,priority:n})=>s(`${r}/globals`,{name:e,location:o,priority:n});export const updateGlobalContent=async({id:e,name:s,location:n,priority:t,markup:a})=>o(`${r}/globals/entry`,{id:e,name:s,location:n,priority:t,markup:a});export const deleteGlobalContent=async e=>t(`${r}/globals`,{ids:e});export const disableGlobalContent=async e=>o(`${r}/globals/disable`,{id:e});export const enableGlobalContent=async e=>o(`${r}/globals/enable`,{id:e});export const setGlobalContentLocked=async({id:e,locked:s})=>o(`${r}/globals/lock`,{id:e,locked:s});export const listExtensions=async({limit:s,offset:o}={})=>e(`${r}/extensions`,{limit:s,offset:o});export const listAvailableExtensions=async()=>e(`${r}/extensions/available`);export const listKnownExtensions=async()=>e(`${r}/extensions/known`);export const listRealtimeConnections=async()=>e(`${r}/realtime/connections`);export const installExtension=async e=>s(`${r}/extensions`,{name:e});export const uninstallExtension=async(e,s=!1)=>t(`${r}/extensions`,{name:e,purgeData:s});export const enableExtension=async e=>s(`${r}/extensions/${encodeURIComponent(e)}/enable`,{});export const disableExtension=async e=>s(`${r}/extensions/${encodeURIComponent(e)}/disable`,{});export const listAdminGlobalContent=async()=>e(`${r}/admin-globals`);export const getAdminGlobalContent=async s=>e(`${r}/admin-globals/${encodeURIComponent(s)}`);export const createAdminGlobalContent=async({name:e,location:o,priority:n,owner:t,markup:a})=>s(`${r}/admin-globals`,{name:e,location:o,priority:n,owner:t,markup:a});export const updateAdminGlobalContent=async(e,{name:s,location:n,priority:t,markup:a})=>o(`${r}/admin-globals/${encodeURIComponent(e)}`,{name:s,location:n,priority:t,markup:a});export const deleteAdminGlobalContent=async e=>t(`${r}/admin-globals`,{ids:e});export const enableAdminGlobalContent=async e=>o(`${r}/admin-globals/${encodeURIComponent(e)}/enable`,{});export const disableAdminGlobalContent=async e=>o(`${r}/admin-globals/${encodeURIComponent(e)}/disable`,{});
2
2
  //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\kempo\sdk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"src\\kempo\\sdk.js","names":["get","post","put","patch","del","API_BASE","register","async","email","password","name","login","logout","getSession","forgotPassword","resetPassword","token","logoutAll","newPassword","changePassword","currentPassword","verifyEmail","currentUserHasPermission","permission","currentUserHasAllPermissions","permissions","currentUserHasSomePermissions","getAllCurrentUserPermissions","userHasPermission","userid","userHasAllPermissions","userHasSomePermissions","getAllUserPermissions","getUsers","limit","offset","searchUsers","q","inGroup","notInGroup","createUser","emailVerified","deleteUsers","ids","updateUser","id","createdAt","getUser","getUserSessions","deleteUserSession","sessionToken","deleteExpiredUserSessions","getUserGroups","addUserToGroup","groupName","removeUserFromGroup","listGroups","owner","createGroup","description","updateGroup","newName","deleteGroups","names","getGroup","encodeURIComponent","getGroupMembers","addMemberToGroup","userId","removeMemberFromGroup","getGroupPermissions","addPermissionToGroup","permissionName","removePermissionFromGroup","listPermissions","createPermissions","updatePermission","deletePermissions","getSetting","listSettings","setSetting","data","deleteSetting","listPages","showHidden","rescanPages","listTemplates","listDirectories","getPage","file","createPage","directory","template","updatePage","title","author","contents","extraMetadata","deletePages","files","disablePage","enablePage","movePage","newFile","searchPagesByMetadata","query","JSON","stringify","setPageLocked","locked","getTemplate","createTemplate","copyFrom","updateTemplate","markup","deleteTemplates","disableTemplate","enableTemplate","moveTemplate","setTemplateLocked","listFragments","getFragment","createFragment","updateFragment","deleteFragments","moveFragment","disableFragment","enableFragment","setFragmentLocked","listGlobalContent","getGlobalContent","createGlobalContent","location","priority","updateGlobalContent","deleteGlobalContent","disableGlobalContent","enableGlobalContent","setGlobalContentLocked","listExtensions","listAvailableExtensions","listKnownExtensions","installExtension","uninstallExtension","purgeData","enableExtension","disableExtension","listAdminGlobalContent","getAdminGlobalContent","createAdminGlobalContent","updateAdminGlobalContent","deleteAdminGlobalContent","enableAdminGlobalContent","disableAdminGlobalContent"],"sources":["0"],"sourcesContent":["import { get, post, put, patch, del } from './fetch.js';\r\nconst API_BASE = '/kempo/api';\r\n\r\nexport const register = async ({ email, password, name }) =>\r\n post(`${API_BASE}/auth/register/email`, { email, password, name });\r\n\r\nexport const login = async ({ email, password }) =>\r\n post(`${API_BASE}/auth/login/email`, { email, password });\r\n\r\nexport const logout = async () => post(`${API_BASE}/auth/logout`, {});\r\n\r\nexport const getSession = async () => get(`${API_BASE}/auth/session`);\r\n\r\nexport const forgotPassword = async ({ email }) =>\r\n post(`${API_BASE}/forgot-password`, { email });\r\n\r\nexport const resetPassword = async ({ token, password, logoutAll = false }) =>\r\n post(`${API_BASE}/auth/reset-password`, { token, newPassword: password, logoutAll });\r\n \r\nexport const changePassword = async ({ currentPassword, newPassword, logoutAll }) => \r\n post(`${API_BASE}/auth/change-password`, { currentPassword, newPassword, logoutAll });\r\n\r\nexport const verifyEmail = async ({ token }) =>\r\n post(`${API_BASE}/auth/verify-email`, { token });\r\n\r\nexport const currentUserHasPermission = async ({ permission }) =>\r\n get(`${API_BASE}/user/current/permissions`, { permission });\r\n\r\nexport const currentUserHasAllPermissions = async ({ permissions }) =>\r\n get(`${API_BASE}/user/current/permissions/all`, { permissions });\r\n\r\nexport const currentUserHasSomePermissions = async ({ permissions }) =>\r\n get(`${API_BASE}/user/current/permissions/some`, { permissions });\r\n\r\nexport const getAllCurrentUserPermissions = async () =>\r\n get(`${API_BASE}/user/current/permissions/list`);\r\n\r\nexport const userHasPermission = async ({ userid, permission }) =>\r\n get(`${API_BASE}/user/${userid}/permissions`, { permission });\r\n\r\nexport const userHasAllPermissions = async ({ userid, permissions }) =>\r\n get(`${API_BASE}/user/${userid}/permissions/all`, { permissions });\r\n\r\nexport const userHasSomePermissions = async ({ userid, permissions }) =>\r\n get(`${API_BASE}/user/${userid}/permissions/some`, { permissions });\r\n\r\nexport const getAllUserPermissions = async ({ userid }) =>\r\n get(`${API_BASE}/user/${userid}/permissions/list`);\r\n\r\nexport const getUsers = async ({ limit, offset } = {}) =>\r\n get(`${API_BASE}/user`, { limit, offset });\r\n\r\nexport const searchUsers = async ({ q, limit, offset, inGroup, notInGroup } = {}) =>\r\n get(`${API_BASE}/user/search`, { q, limit, offset, inGroup, notInGroup });\r\n\r\nexport const createUser = async ({ name, email, password, emailVerified }) =>\r\n post(`${API_BASE}/user/create`, { name, email, password, emailVerified });\r\n\r\nexport const deleteUsers = async (ids) =>\r\n del(`${API_BASE}/user`, { ids });\r\n\r\nexport const updateUser = async ({ id, name, email, emailVerified, createdAt }) =>\r\n patch(`${API_BASE}/user`, { id, name, email, emailVerified, createdAt });\r\n\r\nexport const getUser = async (userid) =>\r\n get(`${API_BASE}/user/${userid}`);\r\n\r\nexport const getUserSessions = async (userid, { limit, offset } = {}) =>\r\n get(`${API_BASE}/user/${userid}/sessions`, { limit, offset });\r\n\r\nexport const deleteUserSession = async (userid, sessionToken) =>\r\n del(`${API_BASE}/user/${userid}/sessions`, { sessionToken });\r\n\r\nexport const deleteExpiredUserSessions = async (userid) =>\r\n del(`${API_BASE}/user/${userid}/sessions/expired`);\r\n\r\nexport const getUserGroups = async (userid, { limit, offset } = {}) =>\r\n get(`${API_BASE}/user/${userid}/groups`, { limit, offset });\r\n\r\nexport const addUserToGroup = async (userid, groupName) =>\r\n post(`${API_BASE}/user/${userid}/groups`, { groupName });\r\n\r\nexport const removeUserFromGroup = async (userid, groupName) =>\r\n del(`${API_BASE}/user/${userid}/groups`, { groupName });\r\n\r\nexport const listGroups = async ({ limit, offset, owner } = {}) =>\r\n get(`${API_BASE}/groups`, { limit, offset, owner });\r\n\r\nexport const createGroup = async ({ name, description }) =>\r\n post(`${API_BASE}/groups`, { name, description });\r\n\r\nexport const updateGroup = async ({ name, newName, description }) =>\r\n patch(`${API_BASE}/groups`, { name, newName, description });\r\n\r\nexport const deleteGroups = async (names) =>\r\n del(`${API_BASE}/groups`, { names });\r\n\r\nexport const getGroup = async (name) =>\r\n get(`${API_BASE}/groups/${encodeURIComponent(name)}`);\r\n\r\nexport const getGroupMembers = async (name, { limit, offset } = {}) =>\r\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { limit, offset });\r\n\r\nexport const addMemberToGroup = async (name, userId) =>\r\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\r\n\r\nexport const removeMemberFromGroup = async (name, userId) =>\r\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\r\n\r\nexport const getGroupPermissions = async (name) =>\r\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`);\r\n\r\nexport const addPermissionToGroup = async (name, permissionName) =>\r\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\r\n\r\nexport const removePermissionFromGroup = async (name, permissionName) =>\r\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\r\n\r\nexport const listPermissions = async ({ limit, offset, owner } = {}) =>\r\n get(`${API_BASE}/permissions`, { limit, offset, owner });\r\n\r\nexport const createPermissions = async (permissions) =>\r\n post(`${API_BASE}/permissions`, { permissions });\r\n\r\nexport const updatePermission = async ({ name, description }) =>\r\n patch(`${API_BASE}/permissions`, { name, description });\r\n\r\nexport const deletePermissions = async (names) =>\r\n del(`${API_BASE}/permissions`, { names });\r\n\r\nexport const getSetting = async (owner, name) =>\r\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\r\n\r\nexport const listSettings = async ({ owner, limit, offset } = {}) =>\r\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}`, { limit, offset });\r\n\r\nexport const setSetting = async (owner, name, data) =>\r\n post(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`, data);\r\n\r\nexport const deleteSetting = async (owner, name) =>\r\n del(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\r\n\r\n/*\r\n Pages\r\n*/\r\n\r\nexport const listPages = async ({ owner, showHidden, offset, limit } = {}) =>\r\n get(`${API_BASE}/pages`, { owner, showHidden, offset, limit });\r\n\r\nexport const rescanPages = async () =>\r\n post(`${API_BASE}/pages/rescan`, {});\r\n\r\nexport const listTemplates = async () =>\r\n get(`${API_BASE}/templates`);\r\n\r\nexport const listDirectories = async () =>\r\n get(`${API_BASE}/pages/directories`);\r\n\r\nexport const getPage = async (file) =>\r\n get(`${API_BASE}/pages/file`, { file });\r\n\r\nexport const createPage = async ({ directory, name, template }) =>\r\n post(`${API_BASE}/pages`, { directory, name, template });\r\n\r\nexport const updatePage = async ({ file, name, title, description, author, template, contents, extraMetadata }) =>\r\n put(`${API_BASE}/pages/file`, { file, name, title, description, author, template, contents, extraMetadata });\r\n\r\nexport const deletePages = async (files) =>\r\n del(`${API_BASE}/pages`, { files });\r\n\r\nexport const disablePage = async (file) =>\r\n put(`${API_BASE}/pages/disable`, { file });\r\n\r\nexport const enablePage = async (file) =>\r\n put(`${API_BASE}/pages/enable`, { file });\r\n\r\nexport const movePage = async ({ file, newFile }) =>\r\n patch(`${API_BASE}/pages/file`, { file, newFile });\r\n\r\nexport const searchPagesByMetadata = async (query) =>\r\n get(`${API_BASE}/pages/search`, { query: JSON.stringify(query) });\r\n\r\nexport const setPageLocked = async ({ file, locked }) =>\r\n put(`${API_BASE}/pages/lock`, { file, locked });\r\n\r\n/*\r\n Templates\r\n*/\r\n\r\nexport const getTemplate = async (file) =>\r\n get(`${API_BASE}/templates/file`, { file });\r\n\r\nexport const createTemplate = async ({ directory, name, copyFrom }) =>\r\n post(`${API_BASE}/templates`, { directory, name, copyFrom });\r\n\r\nexport const updateTemplate = async ({ file, name, author, markup }) =>\r\n put(`${API_BASE}/templates/file`, { file, name, author, markup });\r\n\r\nexport const deleteTemplates = async (files) =>\r\n del(`${API_BASE}/templates`, { files });\r\n\r\nexport const disableTemplate = async (file) =>\r\n put(`${API_BASE}/templates/disable`, { file });\r\n\r\nexport const enableTemplate = async (file) =>\r\n put(`${API_BASE}/templates/enable`, { file });\r\n\r\nexport const moveTemplate = async ({ file, newFile }) =>\r\n patch(`${API_BASE}/templates/file`, { file, newFile });\r\n\r\nexport const setTemplateLocked = async ({ file, locked }) =>\r\n put(`${API_BASE}/templates/lock`, { file, locked });\r\n\r\n/*\r\n Fragments\r\n*/\r\n\r\nexport const listFragments = async () =>\r\n get(`${API_BASE}/fragments`);\r\n\r\nexport const getFragment = async (file) =>\r\n get(`${API_BASE}/fragments/file`, { file });\r\n\r\nexport const createFragment = async ({ directory, name }) =>\r\n post(`${API_BASE}/fragments`, { directory, name });\r\n\r\nexport const updateFragment = async ({ file, author, markup }) =>\r\n put(`${API_BASE}/fragments/file`, { file, author, markup });\r\n\r\nexport const deleteFragments = async (files) =>\r\n del(`${API_BASE}/fragments`, { files });\r\n\r\nexport const moveFragment = async ({ file, newFile }) =>\r\n patch(`${API_BASE}/fragments/file`, { file, newFile });\r\n\r\nexport const disableFragment = async (file) =>\r\n put(`${API_BASE}/fragments/disable`, { file });\r\n\r\nexport const enableFragment = async (file) =>\r\n put(`${API_BASE}/fragments/enable`, { file });\r\n\r\nexport const setFragmentLocked = async ({ file, locked }) =>\r\n put(`${API_BASE}/fragments/lock`, { file, locked });\r\n\r\n/*\r\n Global Content\r\n*/\r\n\r\nexport const listGlobalContent = async () =>\r\n get(`${API_BASE}/globals`);\r\n\r\nexport const getGlobalContent = async (id) =>\r\n get(`${API_BASE}/globals/entry`, { id });\r\n\r\nexport const createGlobalContent = async ({ name, location, priority }) =>\r\n post(`${API_BASE}/globals`, { name, location, priority });\r\n\r\nexport const updateGlobalContent = async ({ id, name, location, priority, markup }) =>\r\n put(`${API_BASE}/globals/entry`, { id, name, location, priority, markup });\r\n\r\nexport const deleteGlobalContent = async (ids) =>\r\n del(`${API_BASE}/globals`, { ids });\r\n\r\nexport const disableGlobalContent = async (id) =>\r\n put(`${API_BASE}/globals/disable`, { id });\r\n\r\nexport const enableGlobalContent = async (id) =>\r\n put(`${API_BASE}/globals/enable`, { id });\r\n\r\nexport const setGlobalContentLocked = async ({ id, locked }) =>\r\n put(`${API_BASE}/globals/lock`, { id, locked });\r\n\r\n/*\r\n Extensions\r\n*/\r\n\r\nexport const listExtensions = async ({ limit, offset } = {}) =>\r\n get(`${API_BASE}/extensions`, { limit, offset });\r\n\r\nexport const listAvailableExtensions = async () =>\r\n get(`${API_BASE}/extensions/available`);\r\n\r\nexport const listKnownExtensions = async () =>\r\n get(`${API_BASE}/extensions/known`);\r\n\r\nexport const installExtension = async (name) =>\r\n post(`${API_BASE}/extensions`, { name });\r\n\r\nexport const uninstallExtension = async (name, purgeData = false) =>\r\n del(`${API_BASE}/extensions`, { name, purgeData });\r\n\r\nexport const enableExtension = async (name) =>\r\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/enable`, {});\r\n\r\nexport const disableExtension = async (name) =>\r\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/disable`, {});\r\n\r\n/*\r\n Admin Global Content\r\n*/\r\n\r\nexport const listAdminGlobalContent = async () =>\r\n get(`${API_BASE}/admin-globals`);\r\n\r\nexport const getAdminGlobalContent = async (id) =>\r\n get(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`);\r\n\r\nexport const createAdminGlobalContent = async ({ name, location, priority, owner, markup }) =>\r\n post(`${API_BASE}/admin-globals`, { name, location, priority, owner, markup });\r\n\r\nexport const updateAdminGlobalContent = async (id, { name, location, priority, markup }) =>\r\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`, { name, location, priority, markup });\r\n\r\nexport const deleteAdminGlobalContent = async (ids) =>\r\n del(`${API_BASE}/admin-globals`, { ids });\r\n\r\nexport const enableAdminGlobalContent = async (id) =>\r\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/enable`, {});\r\n\r\nexport const disableAdminGlobalContent = async (id) =>\r\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/disable`, {});\r\n\r\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"src\\kempo\\sdk.js","names":["get","post","put","patch","del","API_BASE","register","async","email","password","name","login","logout","getSession","forgotPassword","resetPassword","token","logoutAll","newPassword","changePassword","currentPassword","verifyEmail","currentUserHasPermission","permission","currentUserHasAllPermissions","permissions","currentUserHasSomePermissions","getAllCurrentUserPermissions","userHasPermission","userid","userHasAllPermissions","userHasSomePermissions","getAllUserPermissions","getUsers","limit","offset","searchUsers","q","inGroup","notInGroup","createUser","emailVerified","deleteUsers","ids","updateUser","id","createdAt","getUser","getUserSessions","deleteUserSession","sessionToken","deleteExpiredUserSessions","getUserGroups","addUserToGroup","groupName","removeUserFromGroup","listGroups","owner","createGroup","description","updateGroup","newName","deleteGroups","names","getGroup","encodeURIComponent","getGroupMembers","addMemberToGroup","userId","removeMemberFromGroup","getGroupPermissions","addPermissionToGroup","permissionName","removePermissionFromGroup","listPermissions","createPermissions","updatePermission","deletePermissions","getSetting","listSettings","setSetting","data","deleteSetting","listPages","showHidden","rescanPages","listTemplates","listDirectories","getPage","file","createPage","directory","template","updatePage","title","author","contents","extraMetadata","deletePages","files","disablePage","enablePage","movePage","newFile","searchPagesByMetadata","query","JSON","stringify","setPageLocked","locked","getTemplate","createTemplate","copyFrom","updateTemplate","markup","deleteTemplates","disableTemplate","enableTemplate","moveTemplate","setTemplateLocked","listFragments","getFragment","createFragment","updateFragment","deleteFragments","moveFragment","disableFragment","enableFragment","setFragmentLocked","listGlobalContent","getGlobalContent","createGlobalContent","location","priority","updateGlobalContent","deleteGlobalContent","disableGlobalContent","enableGlobalContent","setGlobalContentLocked","listExtensions","listAvailableExtensions","listKnownExtensions","listRealtimeConnections","installExtension","uninstallExtension","purgeData","enableExtension","disableExtension","listAdminGlobalContent","getAdminGlobalContent","createAdminGlobalContent","updateAdminGlobalContent","deleteAdminGlobalContent","enableAdminGlobalContent","disableAdminGlobalContent"],"sources":["0"],"sourcesContent":["import { get, post, put, patch, del } from './fetch.js';\nconst API_BASE = '/kempo/api';\n\nexport const register = async ({ email, password, name }) =>\n post(`${API_BASE}/auth/register/email`, { email, password, name });\n\nexport const login = async ({ email, password }) =>\n post(`${API_BASE}/auth/login/email`, { email, password });\n\nexport const logout = async () => post(`${API_BASE}/auth/logout`, {});\n\nexport const getSession = async () => get(`${API_BASE}/auth/session`);\n\nexport const forgotPassword = async ({ email }) =>\n post(`${API_BASE}/forgot-password`, { email });\n\nexport const resetPassword = async ({ token, password, logoutAll = false }) =>\n post(`${API_BASE}/auth/reset-password`, { token, newPassword: password, logoutAll });\n \nexport const changePassword = async ({ currentPassword, newPassword, logoutAll }) => \n post(`${API_BASE}/auth/change-password`, { currentPassword, newPassword, logoutAll });\n\nexport const verifyEmail = async ({ token }) =>\n post(`${API_BASE}/auth/verify-email`, { token });\n\nexport const currentUserHasPermission = async ({ permission }) =>\n get(`${API_BASE}/user/current/permissions`, { permission });\n\nexport const currentUserHasAllPermissions = async ({ permissions }) =>\n get(`${API_BASE}/user/current/permissions/all`, { permissions });\n\nexport const currentUserHasSomePermissions = async ({ permissions }) =>\n get(`${API_BASE}/user/current/permissions/some`, { permissions });\n\nexport const getAllCurrentUserPermissions = async () =>\n get(`${API_BASE}/user/current/permissions/list`);\n\nexport const userHasPermission = async ({ userid, permission }) =>\n get(`${API_BASE}/user/${userid}/permissions`, { permission });\n\nexport const userHasAllPermissions = async ({ userid, permissions }) =>\n get(`${API_BASE}/user/${userid}/permissions/all`, { permissions });\n\nexport const userHasSomePermissions = async ({ userid, permissions }) =>\n get(`${API_BASE}/user/${userid}/permissions/some`, { permissions });\n\nexport const getAllUserPermissions = async ({ userid }) =>\n get(`${API_BASE}/user/${userid}/permissions/list`);\n\nexport const getUsers = async ({ limit, offset } = {}) =>\n get(`${API_BASE}/user`, { limit, offset });\n\nexport const searchUsers = async ({ q, limit, offset, inGroup, notInGroup } = {}) =>\n get(`${API_BASE}/user/search`, { q, limit, offset, inGroup, notInGroup });\n\nexport const createUser = async ({ name, email, password, emailVerified }) =>\n post(`${API_BASE}/user/create`, { name, email, password, emailVerified });\n\nexport const deleteUsers = async (ids) =>\n del(`${API_BASE}/user`, { ids });\n\nexport const updateUser = async ({ id, name, email, emailVerified, createdAt }) =>\n patch(`${API_BASE}/user`, { id, name, email, emailVerified, createdAt });\n\nexport const getUser = async (userid) =>\n get(`${API_BASE}/user/${userid}`);\n\nexport const getUserSessions = async (userid, { limit, offset } = {}) =>\n get(`${API_BASE}/user/${userid}/sessions`, { limit, offset });\n\nexport const deleteUserSession = async (userid, sessionToken) =>\n del(`${API_BASE}/user/${userid}/sessions`, { sessionToken });\n\nexport const deleteExpiredUserSessions = async (userid) =>\n del(`${API_BASE}/user/${userid}/sessions/expired`);\n\nexport const getUserGroups = async (userid, { limit, offset } = {}) =>\n get(`${API_BASE}/user/${userid}/groups`, { limit, offset });\n\nexport const addUserToGroup = async (userid, groupName) =>\n post(`${API_BASE}/user/${userid}/groups`, { groupName });\n\nexport const removeUserFromGroup = async (userid, groupName) =>\n del(`${API_BASE}/user/${userid}/groups`, { groupName });\n\nexport const listGroups = async ({ limit, offset, owner } = {}) =>\n get(`${API_BASE}/groups`, { limit, offset, owner });\n\nexport const createGroup = async ({ name, description }) =>\n post(`${API_BASE}/groups`, { name, description });\n\nexport const updateGroup = async ({ name, newName, description }) =>\n patch(`${API_BASE}/groups`, { name, newName, description });\n\nexport const deleteGroups = async (names) =>\n del(`${API_BASE}/groups`, { names });\n\nexport const getGroup = async (name) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}`);\n\nexport const getGroupMembers = async (name, { limit, offset } = {}) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { limit, offset });\n\nexport const addMemberToGroup = async (name, userId) =>\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\n\nexport const removeMemberFromGroup = async (name, userId) =>\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\n\nexport const getGroupPermissions = async (name) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`);\n\nexport const addPermissionToGroup = async (name, permissionName) =>\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\n\nexport const removePermissionFromGroup = async (name, permissionName) =>\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\n\nexport const listPermissions = async ({ limit, offset, owner } = {}) =>\n get(`${API_BASE}/permissions`, { limit, offset, owner });\n\nexport const createPermissions = async (permissions) =>\n post(`${API_BASE}/permissions`, { permissions });\n\nexport const updatePermission = async ({ name, description }) =>\n patch(`${API_BASE}/permissions`, { name, description });\n\nexport const deletePermissions = async (names) =>\n del(`${API_BASE}/permissions`, { names });\n\nexport const getSetting = async (owner, name) =>\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\n\nexport const listSettings = async ({ owner, limit, offset } = {}) =>\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}`, { limit, offset });\n\nexport const setSetting = async (owner, name, data) =>\n post(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`, data);\n\nexport const deleteSetting = async (owner, name) =>\n del(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\n\n/*\n Pages\n*/\n\nexport const listPages = async ({ owner, showHidden, offset, limit } = {}) =>\n get(`${API_BASE}/pages`, { owner, showHidden, offset, limit });\n\nexport const rescanPages = async () =>\n post(`${API_BASE}/pages/rescan`, {});\n\nexport const listTemplates = async () =>\n get(`${API_BASE}/templates`);\n\nexport const listDirectories = async () =>\n get(`${API_BASE}/pages/directories`);\n\nexport const getPage = async (file) =>\n get(`${API_BASE}/pages/file`, { file });\n\nexport const createPage = async ({ directory, name, template }) =>\n post(`${API_BASE}/pages`, { directory, name, template });\n\nexport const updatePage = async ({ file, name, title, description, author, template, contents, extraMetadata }) =>\n put(`${API_BASE}/pages/file`, { file, name, title, description, author, template, contents, extraMetadata });\n\nexport const deletePages = async (files) =>\n del(`${API_BASE}/pages`, { files });\n\nexport const disablePage = async (file) =>\n put(`${API_BASE}/pages/disable`, { file });\n\nexport const enablePage = async (file) =>\n put(`${API_BASE}/pages/enable`, { file });\n\nexport const movePage = async ({ file, newFile }) =>\n patch(`${API_BASE}/pages/file`, { file, newFile });\n\nexport const searchPagesByMetadata = async (query) =>\n get(`${API_BASE}/pages/search`, { query: JSON.stringify(query) });\n\nexport const setPageLocked = async ({ file, locked }) =>\n put(`${API_BASE}/pages/lock`, { file, locked });\n\n/*\n Templates\n*/\n\nexport const getTemplate = async (file) =>\n get(`${API_BASE}/templates/file`, { file });\n\nexport const createTemplate = async ({ directory, name, copyFrom }) =>\n post(`${API_BASE}/templates`, { directory, name, copyFrom });\n\nexport const updateTemplate = async ({ file, name, author, markup }) =>\n put(`${API_BASE}/templates/file`, { file, name, author, markup });\n\nexport const deleteTemplates = async (files) =>\n del(`${API_BASE}/templates`, { files });\n\nexport const disableTemplate = async (file) =>\n put(`${API_BASE}/templates/disable`, { file });\n\nexport const enableTemplate = async (file) =>\n put(`${API_BASE}/templates/enable`, { file });\n\nexport const moveTemplate = async ({ file, newFile }) =>\n patch(`${API_BASE}/templates/file`, { file, newFile });\n\nexport const setTemplateLocked = async ({ file, locked }) =>\n put(`${API_BASE}/templates/lock`, { file, locked });\n\n/*\n Fragments\n*/\n\nexport const listFragments = async () =>\n get(`${API_BASE}/fragments`);\n\nexport const getFragment = async (file) =>\n get(`${API_BASE}/fragments/file`, { file });\n\nexport const createFragment = async ({ directory, name }) =>\n post(`${API_BASE}/fragments`, { directory, name });\n\nexport const updateFragment = async ({ file, author, markup }) =>\n put(`${API_BASE}/fragments/file`, { file, author, markup });\n\nexport const deleteFragments = async (files) =>\n del(`${API_BASE}/fragments`, { files });\n\nexport const moveFragment = async ({ file, newFile }) =>\n patch(`${API_BASE}/fragments/file`, { file, newFile });\n\nexport const disableFragment = async (file) =>\n put(`${API_BASE}/fragments/disable`, { file });\n\nexport const enableFragment = async (file) =>\n put(`${API_BASE}/fragments/enable`, { file });\n\nexport const setFragmentLocked = async ({ file, locked }) =>\n put(`${API_BASE}/fragments/lock`, { file, locked });\n\n/*\n Global Content\n*/\n\nexport const listGlobalContent = async () =>\n get(`${API_BASE}/globals`);\n\nexport const getGlobalContent = async (id) =>\n get(`${API_BASE}/globals/entry`, { id });\n\nexport const createGlobalContent = async ({ name, location, priority }) =>\n post(`${API_BASE}/globals`, { name, location, priority });\n\nexport const updateGlobalContent = async ({ id, name, location, priority, markup }) =>\n put(`${API_BASE}/globals/entry`, { id, name, location, priority, markup });\n\nexport const deleteGlobalContent = async (ids) =>\n del(`${API_BASE}/globals`, { ids });\n\nexport const disableGlobalContent = async (id) =>\n put(`${API_BASE}/globals/disable`, { id });\n\nexport const enableGlobalContent = async (id) =>\n put(`${API_BASE}/globals/enable`, { id });\n\nexport const setGlobalContentLocked = async ({ id, locked }) =>\n put(`${API_BASE}/globals/lock`, { id, locked });\n\n/*\n Extensions\n*/\n\nexport const listExtensions = async ({ limit, offset } = {}) =>\n get(`${API_BASE}/extensions`, { limit, offset });\n\nexport const listAvailableExtensions = async () =>\n get(`${API_BASE}/extensions/available`);\n\nexport const listKnownExtensions = async () =>\n get(`${API_BASE}/extensions/known`);\n\nexport const listRealtimeConnections = async () =>\n get(`${API_BASE}/realtime/connections`);\n\nexport const installExtension = async (name) =>\n post(`${API_BASE}/extensions`, { name });\n\nexport const uninstallExtension = async (name, purgeData = false) =>\n del(`${API_BASE}/extensions`, { name, purgeData });\n\nexport const enableExtension = async (name) =>\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/enable`, {});\n\nexport const disableExtension = async (name) =>\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/disable`, {});\n\n/*\n Admin Global Content\n*/\n\nexport const listAdminGlobalContent = async () =>\n get(`${API_BASE}/admin-globals`);\n\nexport const getAdminGlobalContent = async (id) =>\n get(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`);\n\nexport const createAdminGlobalContent = async ({ name, location, priority, owner, markup }) =>\n post(`${API_BASE}/admin-globals`, { name, location, priority, owner, markup });\n\nexport const updateAdminGlobalContent = async (id, { name, location, priority, markup }) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`, { name, location, priority, markup });\n\nexport const deleteAdminGlobalContent = async (ids) =>\n del(`${API_BASE}/admin-globals`, { ids });\n\nexport const enableAdminGlobalContent = async (id) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/enable`, {});\n\nexport const disableAdminGlobalContent = async (id) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/disable`, {});\n\n"],"mappings":"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","ignoreList":[]}
@@ -6,6 +6,7 @@ import getSession from '../server/utils/auth/getSession.js';
6
6
  import currentUserHasPermission from '../server/utils/permissions/currentUserHasPermission.js';
7
7
  import { LEXICAL_BASE, bundleFileName as lexicalBundleFileName } from '../server/utils/lexical/packages.js';
8
8
  import { getEnabledExtensions } from '../server/utils/extensions/scopeCache.js';
9
+ import { extensionAdminDirs, extensionPublicDirs } from '../server/utils/extensions/contentDirs.js';
9
10
  import triggerHook from '../server/utils/hooks/triggerHook.js';
10
11
  import { ADMIN_GLOBALS_DIR } from '../server/utils/admin-global-content/helpers.js';
11
12
 
@@ -112,7 +113,7 @@ const executeRouteFile = async (filePath, request, response, params = {}) => {
112
113
  extensions guard their own private pages. Rendering a page anywhere without this helper silently
113
114
  bypasses those guards.
114
115
  */
115
- const renderGuardedPage = async (pageFilePath, request, response, rootDir, resolveDir, params = {}, extraGlobalDirs = []) => {
116
+ const renderGuardedPage = async (pageFilePath, request, response, rootDir, resolveDir, params = {}, extraGlobalDirs = [], extraFragmentDirs = []) => {
116
117
  try {
117
118
  await triggerHook('middleware:before_page', {
118
119
  url: request.url.split('?')[0],
@@ -127,7 +128,7 @@ const renderGuardedPage = async (pageFilePath, request, response, rootDir, resol
127
128
  return;
128
129
  }
129
130
 
130
- const html = await renderExternalPage(pageFilePath, rootDir, resolveDir, {}, {}, 10, extraGlobalDirs);
131
+ const html = await renderExternalPage(pageFilePath, rootDir, resolveDir, {}, {}, 10, extraGlobalDirs, extraFragmentDirs);
131
132
  response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
132
133
  response.end(html);
133
134
  };
@@ -145,16 +146,22 @@ const renderGuardedPage = async (pageFilePath, request, response, rootDir, resol
145
146
  */
146
147
  export const adminGlobalDirs = async () => [
147
148
  ADMIN_GLOBALS_DIR,
148
- ...(await getEnabledExtensions()).map(ext => join(NODE_MODULES, ext.name, 'admin')),
149
+ ...await extensionAdminDirs(),
149
150
  ];
150
151
 
151
- const serveDir = async (dirPath, method, request, response, resolveDir, rootDir, params = {}, extraGlobalDirs = []) => {
152
+ /*
153
+ Fragments an admin render may pull from extension packages. ADMIN_GLOBALS_DIR is deliberately not
154
+ here: it holds admin-authored global content and never fragments.
155
+ */
156
+ export const adminFragmentDirs = () => extensionAdminDirs();
157
+
158
+ const serveDir = async (dirPath, method, request, response, resolveDir, rootDir, params = {}, extraGlobalDirs = [], extraFragmentDirs = []) => {
152
159
  const candidates = [`${method}.js`, 'index.page.html', 'index.js', 'index.html', 'CATCH.js'];
153
160
  for(const candidate of candidates){
154
161
  const candidatePath = join(dirPath, candidate);
155
162
  try { await stat(candidatePath); } catch { continue; }
156
163
  if(candidate.endsWith('.page.html')){
157
- await renderGuardedPage(candidatePath, request, response, rootDir, resolveDir, params, extraGlobalDirs);
164
+ await renderGuardedPage(candidatePath, request, response, rootDir, resolveDir, params, extraGlobalDirs, extraFragmentDirs);
158
165
  return true;
159
166
  }
160
167
  if(ROUTE_FILES.includes(candidate)){
@@ -271,9 +278,10 @@ export default config => {
271
278
  const segments = url.slice('/admin'.length).replace(/^\//, '').split('/').filter(Boolean);
272
279
  const resolveDir = buildResolveDir(ADMIN_ROOT, url);
273
280
  const globalDirs = await adminGlobalDirs();
281
+ const fragmentDirs = await adminFragmentDirs();
274
282
 
275
283
  if(segments.length === 0){
276
- const served = await serveDir(ADMIN_ROOT, method, request, response, resolveDir, ADMIN_ROOT, {}, globalDirs);
284
+ const served = await serveDir(ADMIN_ROOT, method, request, response, resolveDir, ADMIN_ROOT, {}, globalDirs, fragmentDirs);
277
285
  if(served) return;
278
286
  } else {
279
287
  const walked = await walkDynamic(ADMIN_ROOT, segments);
@@ -283,12 +291,12 @@ export default config => {
283
291
  try { fileStat = await stat(filePath); } catch { /* not found */ }
284
292
 
285
293
  if(fileStat?.isDirectory()){
286
- const served = await serveDir(filePath, method, request, response, resolveDir, ADMIN_ROOT, params, globalDirs);
294
+ const served = await serveDir(filePath, method, request, response, resolveDir, ADMIN_ROOT, params, globalDirs, fragmentDirs);
287
295
  if(served) return;
288
296
  } else if(fileStat?.isFile()){
289
297
  const name = filePath.split(/[/\\]/).pop();
290
298
  if(name.endsWith('.page.html')){
291
- await renderGuardedPage(filePath, request, response, ADMIN_ROOT, resolveDir, params, globalDirs);
299
+ await renderGuardedPage(filePath, request, response, ADMIN_ROOT, resolveDir, params, globalDirs, fragmentDirs);
292
300
  return;
293
301
  }
294
302
  if(ROUTE_FILES.includes(name)){
@@ -317,6 +325,7 @@ export default config => {
317
325
  const resolveDir = buildResolveDir(ADMIN_ROOT, url);
318
326
  const method = request.method?.toUpperCase() || 'GET';
319
327
  const globalDirs = await adminGlobalDirs();
328
+ const fragmentDirs = await adminFragmentDirs();
320
329
 
321
330
  const pageCandidates = subPath.endsWith('/')
322
331
  ? [join(adminDir, subPath, 'index.page.html')]
@@ -329,7 +338,7 @@ export default config => {
329
338
  try {
330
339
  await stat(pagePath);
331
340
  } catch { continue; }
332
- await renderGuardedPage(pagePath, request, response, ADMIN_ROOT, resolveDir, {}, globalDirs);
341
+ await renderGuardedPage(pagePath, request, response, ADMIN_ROOT, resolveDir, {}, globalDirs, fragmentDirs);
333
342
  return;
334
343
  }
335
344
 
@@ -351,12 +360,12 @@ export default config => {
351
360
  let walkedStat;
352
361
  try { walkedStat = await stat(walkedPath); } catch { /* not found */ }
353
362
  if(walkedStat?.isDirectory()){
354
- const served = await serveDir(walkedPath, method, request, response, resolveDir, ADMIN_ROOT, params, globalDirs);
363
+ const served = await serveDir(walkedPath, method, request, response, resolveDir, ADMIN_ROOT, params, globalDirs, fragmentDirs);
355
364
  if(served) return;
356
365
  } else if(walkedStat?.isFile()){
357
366
  const name = walkedPath.split(/[/\\]/).pop();
358
367
  if(name.endsWith('.page.html')){
359
- await renderGuardedPage(walkedPath, request, response, ADMIN_ROOT, resolveDir, params, globalDirs);
368
+ await renderGuardedPage(walkedPath, request, response, ADMIN_ROOT, resolveDir, params, globalDirs, fragmentDirs);
360
369
  return;
361
370
  }
362
371
  if(ROUTE_FILES.includes(name)){
@@ -378,6 +387,12 @@ export default config => {
378
387
  */
379
388
  const extensions = await getEnabledExtensions();
380
389
 
390
+ /*
391
+ Resolved once for every public render below — an extension's own scoped pages, the consumer's
392
+ pages, and the 404 alike — so any enabled extension can contribute to any of them.
393
+ */
394
+ const publicContentDirs = await extensionPublicDirs();
395
+
381
396
  for(const ext of extensions){
382
397
  let pkgPath, pkg;
383
398
  try {
@@ -398,7 +413,7 @@ export default config => {
398
413
  const segments = subPath.replace(/^\//, '').split('/').filter(Boolean);
399
414
 
400
415
  if(segments.length === 0){
401
- const served = await serveDir(publicDir, method, request, response, resolveDir, PROJECT_PUBLIC);
416
+ const served = await serveDir(publicDir, method, request, response, resolveDir, PROJECT_PUBLIC, {}, publicContentDirs, publicContentDirs);
402
417
  if(served) return;
403
418
  return next();
404
419
  }
@@ -410,12 +425,12 @@ export default config => {
410
425
  try { fileStat = await stat(filePath); } catch { /* not found */ }
411
426
 
412
427
  if(fileStat?.isDirectory()){
413
- const served = await serveDir(filePath, method, request, response, resolveDir, PROJECT_PUBLIC, params);
428
+ const served = await serveDir(filePath, method, request, response, resolveDir, PROJECT_PUBLIC, params, publicContentDirs, publicContentDirs);
414
429
  if(served) return;
415
430
  } else if(fileStat?.isFile()){
416
431
  const name = filePath.split(/[/\\]/).pop();
417
432
  if(name.endsWith('.page.html')){
418
- await renderGuardedPage(filePath, request, response, PROJECT_PUBLIC, resolveDir, params);
433
+ await renderGuardedPage(filePath, request, response, PROJECT_PUBLIC, resolveDir, params, publicContentDirs, publicContentDirs);
419
434
  return;
420
435
  }
421
436
  if(ROUTE_FILES.includes(name)){
@@ -448,7 +463,7 @@ export default config => {
448
463
 
449
464
  for(const pageFilePath of candidates){
450
465
  try { await stat(pageFilePath); } catch { continue; }
451
- await renderGuardedPage(pageFilePath, request, response, PROJECT_PUBLIC, resolveDir);
466
+ await renderGuardedPage(pageFilePath, request, response, PROJECT_PUBLIC, resolveDir, {}, publicContentDirs, publicContentDirs);
452
467
  return;
453
468
  }
454
469
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kempo",
3
- "version": "4.2.40",
3
+ "version": "4.3.0",
4
4
  "type": "module",
5
5
  "description": "A fullstack CMS framework for creating websites with user authentication",
6
6
  "scripts": {
@@ -58,7 +58,7 @@
58
58
  },
59
59
  "peerDependencies": {
60
60
  "kempo-css": ">=2.2.5",
61
- "kempo-server": ">=3.3.0",
61
+ "kempo-server": ">=3.4.0",
62
62
  "kempo-ui": ">=0.4.39"
63
63
  },
64
64
  "devDependencies": {
@@ -46,6 +46,8 @@ const DEFAULT_PERMISSIONS = [
46
46
  { name: 'system:extensions:uninstall', description: 'Uninstall extensions' },
47
47
  { name: 'system:extensions:manage', description: 'Enable and disable extensions' },
48
48
 
49
+ { name: 'system:realtime:read', description: 'View realtime connections and channels' },
50
+
49
51
  { name: 'system:admin:access', description: 'Access admin panel' },
50
52
  ];
51
53
 
@@ -66,7 +68,8 @@ const DEFAULT_GROUPS = {
66
68
  'system:menus:create', 'system:menus:read', 'system:menus:update', 'system:menus:delete',
67
69
  'system:fragments:read', 'system:fragments:create', 'system:fragments:update', 'system:fragments:delete',
68
70
  'system:globals:read', 'system:globals:create', 'system:globals:update', 'system:globals:delete',
69
- 'system:extensions:read', 'system:extensions:install', 'system:extensions:uninstall', 'system:extensions:manage'
71
+ 'system:extensions:read', 'system:extensions:install', 'system:extensions:uninstall', 'system:extensions:manage',
72
+ 'system:realtime:read'
70
73
  ]
71
74
  }
72
75
  };