@rowstile/next 0.1.0-alpha.6 → 0.1.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,9 @@ row in one file, and it becomes row-level security: the database filters every r
5
5
  the rules don't allow, whatever the code asks.
6
6
 
7
7
  This package is rowstile for Next.js (App Router). Importing it keeps signed-in reads out of Next's caches.
8
- `route(handler)` answers a refusal with 403 and a hidden row with 404. `action(fn)` gives a server action's
8
+ `route(handler)` answers a refusal with 403 and a hidden row with 404 (and what a call names that isn't there
9
+ 404, a missing or wrong argument 400, a call that needs someone signed in 401, a move inside itself 409).
10
+ `action(fn)` gives a server action's
9
11
  refusal back as a value. `authzRoutes()` is what the React kit calls, and `checkAtStart()` stops a server
10
12
  whose connection skips row-level security. It goes with a driver package, most often `@rowstile/prisma`.
11
13
 
package/dist/index.d.ts CHANGED
@@ -10,7 +10,10 @@ declare function keepOutOfCaches(p: Principal): Promise<void>;
10
10
  export declare function checkAtStart(calls: {
11
11
  check(): Promise<void>;
12
12
  }): Promise<void>;
13
- /** A route handler whose refusals answer 403 with the reason, and hidden rows 404 (RFC 9457 problem bodies). */
13
+ /** A route handler whose refusals answer 403 with the reason, and hidden rows 404 (RFC 9457 problem bodies); a call
14
+ * the database turns down that is no refusal answers as its code's page says, with its words: 401 for a login
15
+ * refused or a call that needs someone signed in (AZ703, AZ714), 404 for something it names that isn't there
16
+ * (AZ708), 400 for an argument missing or wrong (AZ710), 409 for a move inside itself (AZ713). */
14
17
  export declare function route<A extends unknown[]>(handler: (...args: A) => Response | Promise<Response>): (...args: A) => Promise<Response>;
15
18
  /** What a server action returns: its value, or the problem (a server action can't answer 403, and Next.js hides
16
19
  * a thrown error's message in production). */
@@ -21,7 +24,8 @@ export type ActionResult<R> = {
21
24
  ok: false;
22
25
  problem: Problem;
23
26
  };
24
- /** A server action whose refusals and hidden rows come back as { ok: false, problem } instead of an error. */
27
+ /** A server action whose refusals and hidden rows come back as { ok: false, problem } instead of an error, and
28
+ * so does what route() answers 401, 404, 400 or 409 for (AZ703, AZ714, AZ708, AZ710, AZ713). */
25
29
  export declare function action<A extends unknown[], R>(fn: (...args: A) => Promise<R>): (...args: A) => Promise<ActionResult<R>>;
26
30
  export interface RoutesOptions {
27
31
  /** The runtime's functions, each signed in as the request's user: db.$authz (Prisma), authz(pool) (pg), ... */
@@ -36,7 +40,9 @@ export interface RoutesOptions {
36
40
  * GET perms?type=folder&ids=1&ids=2 (POST perms {type, ids} for a long list), GET shares?type=folder&id=3,
37
41
  * GET events (server-sent events), POST share, POST unshare, POST request. Each answers as the signed-in
38
42
  * user, and only what they may see. The POST routes take application/json only: a form on another site
39
- * can't send that without the browser asking this one first. */
43
+ * can't send that without the browser asking this one first. They answer as route() does: a share with
44
+ * someone who doesn't exist 404, a request for access without a reason 400, and one from someone not signed in
45
+ * 401, with the database's words. */
40
46
  export declare function authzRoutes(options: RoutesOptions): {
41
47
  GET: (req: Request) => Promise<Response>;
42
48
  POST: (req: Request) => Promise<Response>;
package/dist/index.js CHANGED
@@ -43,7 +43,10 @@ export async function checkAtStart(calls) {
43
43
  process.exit(1);
44
44
  }
45
45
  }
46
- /** A route handler whose refusals answer 403 with the reason, and hidden rows 404 (RFC 9457 problem bodies). */
46
+ /** A route handler whose refusals answer 403 with the reason, and hidden rows 404 (RFC 9457 problem bodies); a call
47
+ * the database turns down that is no refusal answers as its code's page says, with its words: 401 for a login
48
+ * refused or a call that needs someone signed in (AZ703, AZ714), 404 for something it names that isn't there
49
+ * (AZ708), 400 for an argument missing or wrong (AZ710), 409 for a move inside itself (AZ713). */
47
50
  export function route(handler) {
48
51
  return async (...args) => {
49
52
  try {
@@ -57,7 +60,8 @@ export function route(handler) {
57
60
  }
58
61
  };
59
62
  }
60
- /** A server action whose refusals and hidden rows come back as { ok: false, problem } instead of an error. */
63
+ /** A server action whose refusals and hidden rows come back as { ok: false, problem } instead of an error, and
64
+ * so does what route() answers 401, 404, 400 or 409 for (AZ703, AZ714, AZ708, AZ710, AZ713). */
61
65
  export function action(fn) {
62
66
  return async (...args) => {
63
67
  try {
@@ -76,7 +80,9 @@ const json = (body, status = 200) => new Response(JSON.stringify(body), { status
76
80
  * GET perms?type=folder&ids=1&ids=2 (POST perms {type, ids} for a long list), GET shares?type=folder&id=3,
77
81
  * GET events (server-sent events), POST share, POST unshare, POST request. Each answers as the signed-in
78
82
  * user, and only what they may see. The POST routes take application/json only: a form on another site
79
- * can't send that without the browser asking this one first. */
83
+ * can't send that without the browser asking this one first. They answer as route() does: a share with
84
+ * someone who doesn't exist 404, a request for access without a reason 400, and one from someone not signed in
85
+ * 401, with the database's words. */
80
86
  export function authzRoutes(options) {
81
87
  const { calls } = options;
82
88
  const streams = { open: 0, most: options.maxStreams ?? 1000 };
@@ -130,32 +136,28 @@ function events(req, changes, streams) {
130
136
  return json({ title: "Service Unavailable", status: 503, detail: "too many event streams open" }, 503);
131
137
  }
132
138
  const encoder = new TextEncoder();
133
- let stop = () => { };
139
+ let stop; // set as the stream starts, which is at once
134
140
  const stream = new ReadableStream({
135
141
  start(controller) {
136
142
  streams.open += 1;
137
143
  let counted = true;
138
- controller.enqueue(encoder.encode(": connected\n\n"));
139
- const unsubscribe = changes(() => {
144
+ // a stream that ended, told of a change all the same: nothing to send, and it stops
145
+ const send = (text) => {
140
146
  try {
141
- controller.enqueue(encoder.encode("event: changed\ndata: {}\n\n"));
147
+ controller.enqueue(encoder.encode(text));
142
148
  }
143
149
  catch {
144
150
  stop();
145
151
  }
146
- });
147
- const ping = setInterval(() => {
148
- try {
149
- controller.enqueue(encoder.encode(": ping\n\n"));
150
- }
151
- catch {
152
- stop();
153
- }
154
- }, 25000);
152
+ };
153
+ send(": connected\n\n");
154
+ const unsubscribe = changes(() => send("event: changed\ndata: {}\n\n"));
155
+ const ping = setInterval(() => send(": ping\n\n"), 25000);
155
156
  stop = () => {
156
- if (counted)
157
- streams.open -= 1;
157
+ if (!counted)
158
+ return; // once: a page that leaves cancels the stream and aborts the request
158
159
  counted = false;
160
+ streams.open -= 1;
159
161
  clearInterval(ping);
160
162
  unsubscribe();
161
163
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowstile/next",
3
- "version": "0.1.0-alpha.6",
3
+ "version": "0.1.0-alpha.7",
4
4
  "description": "rowstile with Next.js: signed-in reads kept out of caches, 403 and 404 from route handlers and server actions",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://rowstile.dev",
@@ -20,7 +20,7 @@
20
20
  ],
21
21
  "sideEffects": true,
22
22
  "dependencies": {
23
- "@rowstile/client": "0.1.0-alpha.6"
23
+ "@rowstile/client": "0.1.0-alpha.7"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "next": ">=15"