@prisma/extension-optimize 0.0.0-dev.202408261039 → 0.0.0-dev.202408261100
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 +50 -27
- package/dist/index.d.ts +4 -9
- package/dist/index.js +2 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -13,19 +13,26 @@ Prisma is leading Data DX, a philosophy that promotes simplicity in data-driven
|
|
|
13
13
|
|
|
14
14
|
## Getting started with Prisma Optimize
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
### Resources
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
You can explore Optimize with the following resources:
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
- [Get started](https://pris.ly/optimize/r/getting-started)
|
|
21
|
+
- [Documentation](https://pris.ly/d/optimize)
|
|
22
|
+
- [Prisma Data Platform](https://console.prisma.io/login?utm_source=github&utm_medium=optimize-readme)
|
|
23
|
+
|
|
24
|
+
### Using Optimize
|
|
25
|
+
|
|
26
|
+
#### 1. Launch Optimize
|
|
27
|
+
|
|
28
|
+
1. Log in to your [Prisma Data Platform](https://console.prisma.io/login?utm_source=github&utm_medium=optimize-readme) account.
|
|
29
|
+
2. Access and launch the Optimize dashboard by following the instructions here.
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
#### 2. Add Optimize to your application
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
##### 2.1. Update Your schema.prisma file
|
|
27
34
|
|
|
28
|
-
Prisma Optimize uses [Prisma ORM's OpenTelemetry tracing functionality](https://www.prisma.io/docs/orm/prisma-client/observability-and-logging/opentelemetry-tracing).
|
|
35
|
+
Prisma Optimize uses [Prisma ORM's OpenTelemetry tracing functionality](https://www.prisma.io/docs/orm/prisma-client/observability-and-logging/opentelemetry-tracing). In the `generator` block of your Prisma schema, add the tracing preview feature:
|
|
29
36
|
|
|
30
37
|
```diff
|
|
31
38
|
generator client {
|
|
@@ -34,37 +41,53 @@ Prisma Optimize uses [Prisma ORM's OpenTelemetry tracing functionality](https://
|
|
|
34
41
|
}
|
|
35
42
|
```
|
|
36
43
|
|
|
37
|
-
|
|
44
|
+
Then, generate the Prisma Client:
|
|
38
45
|
|
|
39
|
-
```
|
|
46
|
+
```shell
|
|
40
47
|
npx prisma generate
|
|
41
48
|
```
|
|
42
49
|
|
|
43
|
-
|
|
50
|
+
##### 2.2. Install the Optimize Prisma Client extension
|
|
51
|
+
|
|
52
|
+
Install the latest versions of Prisma Client and the Optimize extension:
|
|
53
|
+
|
|
54
|
+
```shell
|
|
55
|
+
npm install @prisma/client@latest @prisma/extension-optimize
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
##### 2.3. Create an API Key via Optimize's UI and add it to your .env file
|
|
59
|
+
|
|
60
|
+
Generate an Optimize API key by following the instructions [here](https://pris.ly/optimize/r/api-token-generation) and add it to your .env file:
|
|
61
|
+
|
|
62
|
+
```env
|
|
63
|
+
OPTIMIZE_API_KEY="YOUR_OPTIMIZE_API_KEY"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
##### 2.4. Extend your Prisma Client instance with the Optimize extension
|
|
67
|
+
|
|
68
|
+
Extend your existing Prisma Client instance with the Optimize extension:
|
|
44
69
|
|
|
45
70
|
```typescript
|
|
46
71
|
import { PrismaClient } from "@prisma/client";
|
|
47
72
|
import { withOptimize } from "@prisma/extension-optimize";
|
|
48
73
|
|
|
49
|
-
const prisma = new PrismaClient().$extends(withOptimize());
|
|
74
|
+
const prisma = new PrismaClient().$extends(withOptimize({ token: process.env.OPTIMIZE_API_KEY }));
|
|
50
75
|
```
|
|
51
76
|
|
|
52
|
-
|
|
77
|
+
##### 2.5. Use Prisma Optimize to generate insights
|
|
78
|
+
|
|
79
|
+
Follow these steps to start generating query insights with Prisma Optimize:
|
|
80
|
+
|
|
81
|
+
1. In the Optimize dashboard, click the Start recording button, then run your app.
|
|
82
|
+
|
|
83
|
+
2. After your app runs and insights are generated for the desired queries, click the Stop recording button.
|
|
84
|
+
|
|
85
|
+
3. Explore individual query details by clicking on them, and check the Recommendations tab for any suggested improvements to enhance query performance.
|
|
53
86
|
|
|
54
|
-
-
|
|
55
|
-
- And be instructed to visit the URL of your Optimize dashboard:
|
|
87
|
+
> **_NOTE:_** Use the [AI Explainer](https://pris.ly/optimize/r/ai-explainer) feature to understand recommendations and apply them within your Prisma model context.
|
|
56
88
|
|
|
57
|
-
|
|
58
|
-
$ tsx index.ts
|
|
59
|
-
|
|
60
|
-
┌─────────────────────────────────┐
|
|
61
|
-
│ See your Optimize dashboard at: │
|
|
62
|
-
│ https://optimize.prisma.io/ │
|
|
63
|
-
└─────────────────────────────────┘
|
|
64
|
-
|
|
65
|
-
[...]
|
|
66
|
-
```
|
|
89
|
+
For a hands-on learning experience, try out the [step-by-step example](https://github.com/prisma/prisma-examples/optimize/starter).
|
|
67
90
|
|
|
68
|
-
|
|
91
|
+
### Need help?
|
|
69
92
|
|
|
70
|
-
|
|
93
|
+
If you need assistance, reach out in the #help-and-questions channel on our [Discord](https://pris.ly/discord), or connect with our community to see how others are using Optimize.
|
package/dist/index.d.ts
CHANGED
|
@@ -17,15 +17,10 @@ type OptimizeOptions = {
|
|
|
17
17
|
*/
|
|
18
18
|
dashboardUrl?: string;
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* A Prisma Optimize API token. Head to the Prisma Optimize dashboard to
|
|
21
|
+
* create one.
|
|
21
22
|
*/
|
|
22
|
-
token
|
|
23
|
-
/**
|
|
24
|
-
* In the past, this used to configure whether to use tracing or not.
|
|
25
|
-
* This is now a no-op and will be completely removed in a future version.
|
|
26
|
-
* @deprecated
|
|
27
|
-
*/
|
|
28
|
-
useTracing?: boolean;
|
|
23
|
+
token: string;
|
|
29
24
|
/**
|
|
30
25
|
* The minimum interval in milliseconds between sending batched requests to the ingestion service.
|
|
31
26
|
* This is only currently used when tracing is enabled. When tracing is disabled, the requests
|
|
@@ -37,6 +32,6 @@ type OptimizeOptions = {
|
|
|
37
32
|
*/
|
|
38
33
|
showToast?: boolean;
|
|
39
34
|
};
|
|
40
|
-
declare function withOptimize({ enable, ingestionUrl, dashboardUrl, minSendInterval, showToast, token
|
|
35
|
+
declare function withOptimize({ enable, ingestionUrl, dashboardUrl, minSendInterval, showToast, token, }: OptimizeOptions): (client: any) => _prisma_client_extension.PrismaClientExtends<_prisma_client_runtime_library.InternalArgs<{}, {}, {}, {}> & _prisma_client_runtime_library.DefaultArgs>;
|
|
41
36
|
|
|
42
37
|
export { type OptimizeOptions, PROD_DASHBOARD_URL, PROD_INGESTION_URL, withOptimize };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`:(
|
|
3
|
-
|
|
4
|
-
${ze("$")} ${Ae("npx prisma@optimize platform auth login --early-access --optimize")}`;function Fe({enable:t=!0,ingestionUrl:e=ce,dashboardUrl:n=le,minSendInterval:r=50,showToast:o=!0,token:p}={}){if(!t)return re;let c=new URL(e),s=p??w();if(!s)if(process.stdin.isTTY){if(console.error(oe),me.default.keyInYN("Run this command now?")&&(F(),s=w()),!s)throw new Error("Please login to Prisma Data Platform to use Prisma Optimize: either via the CLI, or by providing `token` as an option to `withOptimize()`")}else throw new Error(oe);return o&&N(n).then().catch(i=>{console.error("Failed to show toast",i)}),se.Prisma.defineExtension(i=>{let m=T(c,s),l=Z(m,r);if(!K(i))throw new Error('Please enable the "tracing" preview feature and regenerate the client.');let de=ee(i);return q(m,de).then(d=>{Oe("uploaded schema with hash",d),l.setSchemaHash(d)},d=>{console.error(d),console.error("Failed to upload the schema, no data will be sent to Prisma Optimize")}),i.$extends({query:{async $allOperations({query:d,model:ue,operation:he,args:I}){let y=ie.trace.getActiveSpan()?.spanContext().spanId;if(!y)throw new Error("prisma:client:operation span is expected to be entered in the client extension when tracing is enabled");l.createRequest({spanId:y,timestamp:Date.now(),model:ue,operation:he,args:I});try{return await d(I)}catch(P){let fe=te(P);throw l.setRequestError(y,fe),P}}}})})}0&&(module.exports={PROD_DASHBOARD_URL,PROD_INGESTION_URL,withOptimize});
|
|
1
|
+
"use strict";var se=Object.create;var h=Object.defineProperty;var re=Object.getOwnPropertyDescriptor;var oe=Object.getOwnPropertyNames;var ae=Object.getPrototypeOf,pe=Object.prototype.hasOwnProperty;var de=(n,e)=>{for(var t in e)h(n,t,{get:e[t],enumerable:!0})},x=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of oe(e))!pe.call(n,s)&&s!==t&&h(n,s,{get:()=>e[s],enumerable:!(i=re(e,s))||i.enumerable});return n};var m=(n,e,t)=>(t=n!=null?se(ae(n)):{},x(e||!n||!n.__esModule?h(t,"default",{value:n,enumerable:!0}):t,n)),ce=n=>x(h({},"__esModule",{value:!0}),n);var Se={};de(Se,{PROD_DASHBOARD_URL:()=>ee,PROD_INGESTION_URL:()=>K,withOptimize:()=>ge});module.exports=ce(Se);var X=require("@opentelemetry/api"),Y=require("@prisma/client/extension"),Z=m(require("@prisma/debug"));function me(n,e){let t;return async function(i,s,o){let a=new URL(i,n),p=new Headers({"Content-Type":"application/json",Authorization:`Bearer ${e}`});t&&p.set("prisma-optimize-jwt",t);let r=await fetch(a,{method:s,headers:p,body:JSON.stringify(o)});if(!r.ok){let d=await r.text().catch(()=>"<unreadable>");console.error(`[optimize] HTTP ${r.status} ${r.statusText}: ${d}`)}let c=r.headers.get("prisma-optimize-jwt");return c&&(t=c),r}}function P(n,e){let t=me(n,e);return{request:t,post:(i,s)=>t(i,"POST",s)}}async function v(n,e){return await(await n.post("/schema",{schema:e})).text()}var l=m(require("fs")),q=m(require("path")),T=require("stream"),E=require("stream/promises"),A=m(require("kleur")),_=require("node-notifier"),O=m(require("xdg-app-paths")),le="https://avatars.githubusercontent.com/u/17219288?s=96",{bold:ue,underline:he}=A.default;async function k(n){let e=new O.default("prisma-optimize").config();l.default.mkdirSync(e,{recursive:!0});let t=q.default.resolve(e,"avatar.png");if(!l.default.existsSync(t)){let r=await fetch(le);if(r.ok&&r.body!=null){let c=l.default.createWriteStream(t,{flags:"wx"});await(0,E.finished)(T.Readable.fromWeb(r.body).pipe(c))}}let i="See your Optimize dashboard at:",s=i.length,o=`${he(ue(n))}`,a=n.length,p=Math.max(s,a)+2;console.log("\u250C"+"\u2500".repeat(p)+"\u2510"),console.log("\u2502 "+i+" ".repeat(p-s-2)+" \u2502"),console.log("\u2502 "+o+" ".repeat(p-a-2)+" \u2502"),console.log("\u2514"+"\u2500".repeat(p)+"\u2518"),(0,_.notify)({timeout:10,open:n,subtitle:"Your dashboard is ready! \u{1F680} ",message:"Click to open",title:"Prisma Optimize",icon:l.default.existsSync(t)?t:void 0,contentImage:l.default.existsSync(t)?t:void 0})}var L=require("@opentelemetry/api"),N=require("@opentelemetry/context-async-hooks"),U=require("@opentelemetry/instrumentation"),$=require("@opentelemetry/resources"),H=require("@opentelemetry/sdk-trace-base"),y=require("@opentelemetry/semantic-conventions"),D=require("@prisma/instrumentation");var F=m(require("@prisma/debug"));async function M(n,e){await n.post("/ingest",e)}function I(n,...e){setTimeout(()=>{n(...e)},0)}function u(n){return n[0]*1e3+n[1]/1e6}var g=(0,F.default)("prisma:extension:optimize"),f="0000000000000000",j=(o=>(o.ClientOperation="prisma:client:operation",o.ClientConnect="prisma:client:connect",o.Engine="prisma:engine",o.EngineConnection="prisma:engine:connection",o.EngineQuery="prisma:engine:db_query",o))(j||{}),w=class{spanId;timestamp;model;operation;args;statementSpans;duration;connect;error;completionFlags={clientSpanClosed:!1,engineSpanClosed:!1};constructor(e){this.spanId=e.spanId,this.timestamp=e.timestamp,this.model=e.model,this.operation=e.operation,this.args=e.args,this.statementSpans=[]}isCompleted(){return this.completionFlags.clientSpanClosed&&this.completionFlags.engineSpanClosed}statementSpansToString(){return this.statementSpans.sort((t,i)=>{let s=u(t.startTime),o=u(i.startTime);return s-o}).reduce((t,i)=>{let s=i.attributes["db.statement"];return typeof s=="string"?`${t}${s}
|
|
2
|
+
`:(g("sql attribute is not a string: %o",s),t)},"")}toIngestRequestItem(e){if(this.duration===void 0)throw new Error("`duration` is `undefined`, this should not happen");return{ts:this.timestamp,model:this.model??null,operation:this.operation,args:this.args,latency:this.duration,connect:this.connect??!1,sql:this.statementSpansToString(),error:this.error??null,hash:e}}},S=class{#t=new Map;#n=new Map;#i=new Map;#o=new Set;#e=[];#a=Promise.resolve();#p=!1;#c;#m;#s=new Set;#l=Promise.resolve();#d=!1;#u;setSchemaHash=()=>{};__test_internalMetrics__={rescheduledCountTotal:0};constructor(e,t){this.#c=e,this.#m=t,this.#u=new Promise(i=>{this.setSchemaHash=i})}createRequest(e){let t=new w(e);this.#t.set(e.spanId,t)}setRequestError(e,t){let i=this.#t.get(e);if(!i)throw new Error(`Unknown request ${e}`);i.error=t}#S(e){for(let t=e;t!==f;t=this.#n.get(t)){if(t===void 0)return{type:"UndeliveredSpanInTree"};let i=this.#t.get(t);if(i)return{type:"Ok",request:i}}return{type:"NotInRequestTree"}}onStart(e,t){if(!z(e))return;let i=e.spanContext().spanId;if(!e.parentSpanId){this.#n.set(i,f);return}if(this.#n.set(i,e.parentSpanId),e.parentSpanId===f)return;let s=this.#i.get(e.parentSpanId);s?s.add(i):this.#i.set(e.parentSpanId,new Set([i])),e.name==="prisma:engine:connection"&&this.#o.add(i)}onEnd(e){z(e)?this.#h(e):(e.parentSpanId===void 0||e.parentSpanId===f)&&this.#r(e.spanContext().spanId)}async forceFlush(){await this.#a,await this.#g()}async shutdown(){await this.forceFlush()}#h(e){let t=e.spanContext().spanId,i,s=this.#S(t);switch(s.type){case"Ok":i=s.request;break;case"UndeliveredSpanInTree":this.#y(e);break;case"NotInRequestTree":this.#r(t);break}i&&(e.name==="prisma:client:connect"&&(i.connect=u(e.duration)),e.name==="prisma:engine"&&(i.completionFlags.engineSpanClosed=!0),e.name==="prisma:engine:db_query"&&!this.#o.has(e.parentSpanId)&&i.statementSpans.push(e),e.name==="prisma:client:operation"&&(i.duration=u(e.duration),i.completionFlags.clientSpanClosed=!0,g("latency otel: %d",i.duration)),i.isCompleted()&&this.#w(i))}#y(e){this.#e.push(e),this.__test_internalMetrics__.rescheduledCountTotal++;let{spanId:t,traceId:i}=e.spanContext();g("rescheduled ",e.name,t,e.parentSpanId,i),!this.#p&&(this.#p=!0,this.#a=Promise.all([this.#a,new Promise(I).then(()=>this.#C())]))}#C(){let e=this.#e;this.#e=[],this.#p=!1;for(let t of e)this.#h(t)}#r(e){this.#n.delete(e),this.#o.delete(e);let t=this.#i.get(e);if(this.#i.delete(e),this.#I(e),t)for(let i of t)this.#r(i)}#I(e){for(let t=this.#f(e);t!==-1;t=this.#f(e))this.#e.splice(t,1)}#f(e){return this.#e.findIndex(t=>t.spanContext().spanId===e)}#w(e){I(()=>{this.#t.delete(e.spanId),this.#r(e.spanId),this.#s.add(e),this.#R()})}async#R(){await this.#l,!this.#d&&(this.#d=!0,setTimeout(()=>{this.#l=this.#g().finally(()=>{this.#d=!1})},this.#m))}async#g(){if(this.#s.size===0)return;let e=await this.#u,t=[...this.#s].map(i=>i.toIngestRequestItem(e));this.#s.clear(),g("sending batch of %d requests",t.length);try{await M(this.#c,t)}catch(i){console.error(i.message??i)}}};function z(n){return Object.values(j).includes(n.name)}function Q(n,e){let t=new N.AsyncHooksContextManager().enable();L.context.setGlobalContextManager(t);let i=new H.BasicTracerProvider({resource:new $.Resource({[y.SEMRESATTRS_SERVICE_NAME]:"extension-optimize",[y.SEMRESATTRS_SERVICE_VERSION]:"0.0.0"})}),s=new S(n,e);return i.addSpanProcessor(s),(0,U.registerInstrumentations)({tracerProvider:i,instrumentations:[new D.PrismaInstrumentation]}),i.register(),s}function B(n){return n._previewFeatures?.includes("tracing")}function J(n){return n._engineConfig.inlineSchema}function V(n){if(n instanceof Error)return n.stack??n.message;switch(typeof n){case"undefined":return"undefined";case"object":{let e;return n!==null&&typeof n.toString=="function"&&(e=n.toString()),typeof e=="string"&&e!=="[object Object]"?e:JSON.stringify(n)}default:return String(n)}}var G=require("@prisma/client/extension"),W=G.Prisma.defineExtension(n=>n.$extends({}));var K="https://optimize-ingestion.datacdn.workers.dev/",ee="https://optimize.prisma.io",fe=(0,Z.default)("prisma:extension:optimize");function ge({enable:n=!0,ingestionUrl:e=K,dashboardUrl:t=ee,minSendInterval:i=50,showToast:s=!0,token:o}){return n?(s&&k(t).then().catch(a=>{console.error("Failed to show toast",a)}),Y.Prisma.defineExtension(a=>{let p=P(e,o),r=Q(p,i);if(!B(a))throw new Error('Please enable the "tracing" preview feature and regenerate the client.');let c=J(a);return v(p,c).then(d=>{fe("uploaded schema with hash",d),r.setSchemaHash(d)},d=>{console.error(d),console.error("Failed to upload the schema, no data will be sent to Prisma Optimize")}),a.$extends({query:{async $allOperations({query:d,model:te,operation:ne,args:R}){let C=X.trace.getActiveSpan()?.spanContext().spanId;if(!C)throw new Error("prisma:client:operation span is expected to be entered in the client extension when tracing is enabled");r.createRequest({spanId:C,timestamp:Date.now(),model:te,operation:ne,args:R});try{return await d(R)}catch(b){let ie=V(b);throw r.setRequestError(C,ie),b}}}})})):W}0&&(module.exports={PROD_DASHBOARD_URL,PROD_INGESTION_URL,withOptimize});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/extension-optimize",
|
|
3
|
-
"version": "0.0.0-dev.
|
|
3
|
+
"version": "0.0.0-dev.202408261100",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"@opentelemetry/resources": "1.22.0",
|
|
16
16
|
"@opentelemetry/semantic-conventions": "1.22.0",
|
|
17
17
|
"@prisma/debug": "5.12.1",
|
|
18
|
-
"@prisma/instrumentation": "5.
|
|
18
|
+
"@prisma/instrumentation": "5.18.0",
|
|
19
19
|
"kleur": "4.1.5",
|
|
20
20
|
"node-notifier": "10.0.1",
|
|
21
21
|
"readline-sync": "1.4.10",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"hono": "4.4.6",
|
|
29
29
|
"tsup": "8.0.2",
|
|
30
30
|
"vitest": "1.4.0",
|
|
31
|
-
"common": "0.0.0-dev.
|
|
31
|
+
"common": "0.0.0-dev.202408261100"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"@prisma/client": "5.x"
|